Padrões de Multi-Tenancy Explicados
Multi-tenancy é a prática de servir muitos clientes, ou tenants, de infraestrutura compartilhada, mantendo os dados de cada tenant isolados de todos os outros tenants.
Todo padrão para fazer isso em PostgreSQL, desde uma coluna tenant_id até um banco de dados totalmente separado por cliente, situa-se em algum lugar em um espectro subjacente entre simplicidade operacional e força de isolamento.
Esta página explica esse espectro diretamente, para que as páginas mais concretas nesta seção (consultas de esquema compartilhado, políticas de RLS, esquema por tenant, banco de dados por tenant) sejam lidas como pontos ao longo dele, em vez de técnicas não relacionadas.
Resumo
- Padrões multi-tenant trocam simplicidade operacional por força de isolamento, e cada padrão concreto se situa em um ponto específico dessa troca.
- Por Que Importa: Escolher isolamento insuficiente arrisca vazamentos de dados entre tenants e problemas de desempenho de vizinho barulhento; escolher isolamento excessivo multiplica o custo operacional e de migração.
- Conceitos Chave: tenant, esquema compartilhado, esquema por tenant, banco de dados por tenant, segurança em nível de linha (RLS), vizinho barulhento.
- Quando Usar: Qualquer produto SaaS que atenda a mais de um cliente de infraestrutura PostgreSQL compartilhada.
- Limitações / Trocas: Isolamento mais forte significa relatórios entre tenants mais lentos e multiplicação do trabalho de migração e backup.
- Tópicos Relacionados: segurança em nível de linha, pooling de conexões por tenant, migrações de esquema em escala, isolamento orientado por conformidade.
Fundamentos
O padrão mais simples, esquema compartilhado com uma coluna tenant_id, coloca as linhas de cada tenant nas mesmas tabelas, distinguido apenas por um valor tenant_id.
CREATE TABLE projects (
tenant_id uuid NOT NULL REFERENCES tenants (tenant_id),
project_id bigint GENERATED ALWAYS AS IDENTITY,
name text NOT NULL,
PRIMARY KEY (tenant_id, project_id)
);Este é o padrão mais barato de operar, pois uma migração, um backup e um pool de conexões servem a todos os tenants simultaneamente.
Esquema por tenant avança um nível no espectro, dando a cada tenant um schema PostgreSQL dedicado com DDL de tabela duplicada, ganhando isolamento de namespace ao custo de executar cada migração uma vez por schema de tenant.
Banco de dados por tenant avança ainda mais, dando a cada tenant um banco de dados totalmente separado com ciclos independentes de backup, restauração e atualização, que é o isolamento mais forte que o PostgreSQL oferece dentro de um único cluster.
Cada etapa ao longo desse espectro, de esquema compartilhado a banco de dados por tenant, compra contenção de raio de explosão mais forte em troca de mais infraestrutura para operar e mais lugares para uma migração falhar.
Mecânicas e Interações
A tenancy de esquema compartilhado coloca todo o ônus da correção em cada consulta lembrando de filtrar por tenant_id, o que é uma garantia frágil para depender de algo tão sério quanto vazamento de dados entre tenants.
Segurança em nível de linha (RLS) existe para tornar essa garantia estrutural em vez de convencional, anexando uma política diretamente à tabela que o PostgreSQL impõe, independentemente de a consulta da aplicação ter lembrado de uma cláusula WHERE tenant_id = ....
ALTER TABLE projects ENABLE ROW LEVEL SECURITY;
ALTER TABLE projects FORCE ROW LEVEL SECURITY;
CREATE POLICY projects_tenant_isolation ON projects
USING (tenant_id = current_setting('app.tenant_id', true)::uuid);RLS é uma rede de segurança sobreposta à filtragem em nível de aplicação, não um substituto para ela, pois uma política ainda depende da sessão configurando corretamente app.tenant_id para cada conexão.
O problema do vizinho barulhento é o modo de falha característico do padrão de esquema compartilhado: a carga de trabalho incomumente grande de um tenant consome CPU, I/O ou contenção de bloqueio desproporcionais, degradando o desempenho para todos os outros tenants que compartilham as mesmas tabelas e pool de conexões.
Detectá-lo requer observabilidade por tenant, como agrupar pg_stat_statements ou logs de auditoria por tenant_id, já que o PostgreSQL em si não tem um conceito nativo de tenant para isolar recursos automaticamente.
Esquema por tenant e banco de dados por tenant reduzem o risco de vizinho barulhento estruturalmente, pois os dados de cada tenant e, no caso de banco de dados por tenant, o pool de conexões, são fisicamente separados de todos os outros tenants.
Considerações Avançadas e Aplicações
Requisitos de conformidade frequentemente forçam a decisão de isolamento em vez de deixá-la puramente como uma troca de desempenho.
Um cliente empresarial regulamentado pode exigir contratualmente que seus dados nunca compartilhem armazenamento físico ou um pool de conexões com qualquer outro tenant, o que efetivamente exige banco de dados por tenant, independentemente do que a economia de esquema compartilhado sugeriria de outra forma.
Muitos produtos SaaS reais não escolhem um padrão para todo o sistema; eles executam esquema compartilhado + RLS para a maioria dos tenants e reservam esquema por tenant ou banco de dados por tenant para um nível empresarial disposto a pagar por esse isolamento.
Essa abordagem em níveis significa que a camada de roteamento de conexão, não apenas a camada de esquema, tem que saber qual padrão um determinado tenant usa, o que adiciona complexidade real à aplicação em troca de atender a ambas as pontas do mercado a partir de um único produto.
| Abordagem | Força | Fraqueza | Melhor Ajuste |
|---|---|---|---|
| Esquema compartilhado + tenant_id | Mais barato de operar; uma migração atende a todos os tenants | A correção depende de cada consulta filtrar corretamente; risco de vizinho barulhento | SaaS em estágio inicial, a maioria dos níveis de tenant de autoatendimento |
| Esquema compartilhado + RLS | Isolamento imposto pelo banco de dados como rede de segurança | Ainda compartilha recursos físicos; a correção da política depende da configuração da sessão | Qualquer produto de esquema compartilhado que deseje defesa em profundidade |
| Esquema por tenant | Isolamento de namespace mais forte; exportação de dados por tenant mais fácil | Migrações executadas uma vez por schema de tenant; mais difícil de escalar para milhares de tenants | Tenants de mercado intermediário que precisam de mais isolamento sem separação completa do banco de dados |
| Banco de dados por tenant | Isolamento mais forte; backup, restauração e escalonamento independentes por tenant | Custo operacional mais alto; relatórios entre tenants exigem federação | Tenants regulamentados ou empresariais com requisitos de isolamento contratuais |
O pooling de conexões também precisa ser reconsiderado em cada ponto desse espectro, pois o PgBouncer faz pool por banco de dados, não por schema, então tenants de esquema por tenant ainda compartilham um pool, enquanto tenants de banco de dados por tenant precisam de pool por banco de dados ou roteamento dinâmico.
Equívocos Comuns
- "Segurança em nível de linha substitui a necessidade de filtrar por tenant_id em consultas." RLS é uma rede de segurança que captura filtros ausentes, mas depender apenas dela ainda depende da sessão configurando corretamente o contexto do tenant, então a filtragem em nível de aplicação continua sendo a defesa primária.
- "Esquema por tenant escala para qualquer número de tenants tão bem quanto esquema compartilhado." Executar uma migração por schema de tenant torna-se operacionalmente pesado bem antes de se tornar tecnicamente impossível, razão pela qual este padrão é geralmente reservado para um nível limitado de mercado intermediário, em vez de milhares de tenants de autoatendimento.
- "Banco de dados por tenant é apenas sobre segurança." É também uma ferramenta de escalonamento e isolamento independente de conformidade, permitindo que a carga de trabalho de um tenant seja escalada ou restaurada sem afetar nenhum outro tenant.
- "Você tem que escolher um padrão para todo o produto." Muitos sistemas SaaS de produção misturam padrões por nível de tenant, executando esquema compartilhado para a maioria dos clientes e isolamento mais forte apenas para os tenants que precisam ou pagam por ele.
- "Uma coluna tenant_id é suficiente por si só, sem outras precauções." Sem RLS ou aplicação equivalente, uma única cláusula
WHERE tenant_id = ...ausente em qualquer caminho de consulta é um vazamento de dados entre tenants, razão pela qual a tenancy de esquema compartilhado é considerada o elo mais fraco neste espectro sem salvaguardas adicionais.
FAQs
Qual é o espectro central que esta página descreve?
Padrões multi-tenant vão de esquema compartilhado (mais barato, isolamento mais fraco) através de esquema por tenant até banco de dados por tenant (mais caro, isolamento mais forte).
Cada padrão concreto nesta seção é um ponto no mesmo espectro.
Qual é o padrão multi-tenant mais simples no PostgreSQL?
Um esquema compartilhado onde as linhas de cada tenant vivem nas mesmas tabelas, distinguidas por uma coluna tenant_id na qual cada consulta deve filtrar.
É o mais barato de operar, mas coloca o maior peso na correção da consulta.
A segurança em nível de linha substitui a filtragem tenant_id em consultas de aplicação?
Não - RLS é uma rede de segurança imposta pelo banco de dados que captura consultas que perdem um filtro de tenant, mas ainda depende da sessão configurando corretamente o contexto do tenant.
Complementa a filtragem da aplicação em vez de substituí-la.
O que é o problema do vizinho barulhento?
É quando a carga de trabalho desproporcionalmente pesada de um tenant degrada o desempenho para outros tenants que compartilham as mesmas tabelas e pool de conexões.
É mais visível na tenancy de esquema compartilhado e encolhe à medida que o isolamento se fortalece.
Quando um produto deve mudar de esquema compartilhado para esquema por tenant?
Quando um conjunto limitado de tenants precisa de isolamento de namespace mais forte ou exportação de dados por tenant mais fácil do que uma tabela compartilhada pode oferecer, sem o custo operacional total de bancos de dados separados.
Torna-se mais difícil de justificar quando a contagem de tenants cresce para milhares.
Quando o banco de dados por tenant se torna necessário?
Mais comumente quando o contrato ou requisito regulatório de um tenant exige que seus dados nunca compartilhem armazenamento físico ou um pool de conexões com qualquer outro tenant.
Também é útil quando a escala de um tenant genuinamente necessita de ciclos independentes de backup e restauração.
Um único produto pode usar mais de um padrão multi-tenant ao mesmo tempo?
Sim, e muitos sistemas SaaS reais o fazem - esquema compartilhado + RLS para a maioria dos tenants, com esquema por tenant ou banco de dados por tenant reservado para um nível empresarial.
Isso requer que a camada de roteamento de conexão saiba qual padrão cada tenant usa.
Como o pooling de conexões interage com esses padrões?
O PgBouncer faz pool por banco de dados, não por schema, então tenants de esquema por tenant ainda compartilham um pool, enquanto tenants de banco de dados por tenant geralmente precisam de um pool por banco de dados ou roteamento dinâmico.
Este é um custo operacional que cresce junto com a força do isolamento.
Uma restrição UNIQUE em uma coluna é suficiente em um esquema multi-tenant?
Somente se estiver escopada para o tenant, como UNIQUE (tenant_id, slug).
Uma restrição UNIQUE (slug) global impede incorretamente que dois tenants diferentes usem o mesmo valor.
Como detectar um tenant vizinho barulhento em um esquema compartilhado?
Através de observabilidade por tenant, como agrupar atividade de consulta ou pg_stat_statements por tenant_id, já que o PostgreSQL não tem um conceito nativo de tenant para isolar recursos automaticamente.
Este trabalho de detecção é o que geralmente aciona uma mudança para um nível de isolamento mais forte para esse tenant.
O isolamento mais forte sempre significa melhor desempenho?
Não necessariamente para análises entre tenants - banco de dados por tenant isola cargas de trabalho bem, mas faz com que qualquer relatório que abranja tenants exija federação em vez de um simples join.
A força do isolamento e a conveniência de consulta entre tenants se movem em direções opostas.
O que deve impulsionar a escolha entre esses padrões para um novo produto?
Contagem esperada de tenants, requisitos de conformidade e se algum tenant está disposto a pagar por isolamento mais forte.
Produtos em estágio inicial adotam esquema compartilhado por padrão e movem tenants específicos para cima no espectro apenas quando um requisito concreto o exige.
Relacionados
- Noções Básicas de Multi-Tenancy - exemplos práticos em todo o espectro de isolamento
- Esquema Compartilhado + tenant_id - padrões de indexação e consulta para o padrão compartilhado
- Tenancy com Segurança em Nível de Linha - modelos de política e detalhes de aplicação
- Esquema por Tenant - trocas de isolamento de namespace
- Banco de Dados por Tenant - isolamento de nível empresarial
- Fundamentos de Modelagem de Dados - o processo de modelagem em que uma decisão de tenancy se encaixa
Versões de Stack: Esta página foi escrita para PostgreSQL 18.4 (estável 18, manutenção 17) e PgBouncer 1.x.