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.
Busque em todas as páginas da documentação
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.
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.
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.
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.
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.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.
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.
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.
É 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 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.
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.
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.
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.
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.
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.
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.
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.
Versões de Stack: Esta página foi escrita para PostgreSQL 18.4 (estável 18, manutenção 17) e PgBouncer 1.x.
Revisado por Chris St. John·Última atualização: 15 de jul. de 2026