Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Entendendo qual modelo adotar em Multitenancy n...

Entendendo qual modelo adotar em Multitenancy no PostgreSQL

Existem 3 modelos de multitenancy no PostgreSQL. Cada um desses modelos tem prós e contras em diferentes situações. Qual o melhor modelo para você?

Com o crescimento dos sistemas em nuvens, o uso de sistemas multitenancy, que agregam vários clientes num mesmo banco de dados, tornou-se comum. Mas qual a melhor estratégia para cada cenário? Vamos abordar aqui 3 estratégias diferentes e avaliar qual resolve melhor diferentes tipos de problemas: o modelo de tabela compartilhada, o modelo de um schema por cliente e, por fim, o modelo de um database por cliente.

A ideia aqui é abordar estes 3 modelos do ponto de vista da segurança, da facilidade de deploy, da configuração de conexões, da flexibilidade, da capacidade de agrupamento de dados e, claro, do desempenho. Além disso, vamos apresentar algumas ferramentas e configurações específicas do PostgreSQL que podem ajudar em cada modelo.

Avatar for Fábio Telles Rodriguez

Fábio Telles Rodriguez

September 03, 2026

More Decks by Fábio Telles Rodriguez

Other Decks in Programming

Transcript

  1. Modelos de Multi-Tenancy no PostgreSQL Agenda ◦ Introdução ◦ Implementação

    ◦ Segurança ◦ Desempenho ◦ Outros ◦ Recomendações
  2. Introdução O que é Multi-Tenancy? • Uma única aplicação (e

    um único banco ou cluster) atende vários clientes — os tenants — mantendo os dados de cada um isolados dos demais • Relevante quando: produto SaaS B2B, plataforma com muitos clientes pequenos, ou onboarding de tenant precisa ser rápido e barato • Menos relevante quando: poucos clientes grandes, cada um já justificando infraestrutura dedicada desde o início
  3. Introdução Vetores • Número de tenants • Volume de dados

    por tenant • Volume de objetos por tenant • Volume de conexões no banco por tenant • Conformidade com padrões de segurança • Objetos compartilhados entre os tenants • Personalização de objetos para tenants específicos • Consultar dados de mais de um tenant ao mesmo tempo • Necessidade de extrair e/ou movimentar dados de um tenant para outro servidor
  4. Introdução O modelo de exemplo • 4 tabelas simples: ◦

    clientes , ◦ produtos, ◦ pedidos, ◦ pedido_itens • Um usuário dono de todos os objetos utilizado apenas no deploy • Um usuário utilizado pela aplicação com permissões nos objetos • Um banco de dados
  5. Introdução Agora vamos criar 5 tenants • Modelos sugeridos: 1.

    Compartilhar as mesmas tabelas para todos os tenants 2. Criar um schema para cada tenant e manter a tabela tenants no schema public 3. Criar um banco de dados para cada tenant e manter a tabela tenants num database só para isso
  6. Implementação Agora queremos utilizar o mesmo modelo, mas com 5

    tenants • Criar a tabela tenants, que será compartilhada por todos os tenants. CREATE TABLE tenants ( id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, slug TEXT NOT NULL UNIQUE, nome TEXT NOT NULL, criado_em TIMESTAMPTZ NOT NULL DEFAULT now() ); INSERT INTO tenants (slug, nome) VALUES ('comprafacil', 'Compra Fácil'), ('mercadodigital', 'Mercado Digital'), ('lojaagil', 'Loja Ágil'), ('clickecompre', 'Click e Compre'), ('comerciofacil', 'Comércio Fácil');
  7. Implementação - Tabela compartilhada Ajustes no modelo • Criação da

    coluna tenant_id: ALTER TABLE clientes ADD COLUMN tenant_id INTEGER REFERENCES tenants(id); ALTER TABLE produtos ADD COLUMN tenant_id INTEGER REFERENCES tenants(id); ALTER TABLE pedidos ADD COLUMN tenant_id INTEGER REFERENCES tenants(id); ALTER TABLE pedido_itens ADD COLUMN tenant_id INTEGER REFERENCES tenants(id); • Remover constraints ALTER TABLE clientes DROP CONSTRAINT clientes_pkey; ALTER TABLE produtos DROP CONSTRAINT produtos_pkey; ALTER TABLE pedidos DROP CONSTRAINT pedidos_pkey; ALTER TABLE pedido_itens DROP CONSTRAINT pedido_itens_pkey; ALTER TABLE pedidos DROP CONSTRAINT fk_pedidos_cliente; ALTER TABLE pedido_itens DROP CONSTRAINT fk_pedido_itens_pedido; ALTER TABLE pedido_itens DROP CONSTRAINT fk_pedido_itens_produto;
  8. Implementação - Tabela compartilhada Ajustes no modelo • A Primary

    key da tabela deverá incluir a coluna tenant_id, como a primeira coluna ALTER TABLE clientes ADD PRIMARY KEY (tenant_id, id); ALTER TABLE produtos ADD PRIMARY KEY (tenant_id, id); ALTER TABLE pedidos ADD PRIMARY KEY (tenant_id, id); ALTER TABLE pedido_itens ADD PRIMARY KEY (tenant_id, id); ALTER TABLE pedidos ADD FOREIGN KEY (tenant_id, cliente_id) REFERENCES clientes (tenant_id, id); ALTER TABLE pedido_itens ADD FOREIGN KEY (tenant_id, pedido_id) REFERENCES pedidos (tenant_id, id); ALTER TABLE pedido_itens ADD FOREIGN KEY (tenant_id, produto_id) REFERENCES produtos (tenant_id, id);
  9. Implementação - Tabela compartilhada Isolamento dos dados pela aplicação Delegar

    o controle do acesso às linhas de cada tabela apenas no nível da aplicação. Neste caso a aplicação deverá sempre filtrar as consultas pela coluna tenant_id: • Criação de usuário: CREATE ROLE app_user LOGIN PASSWORD '...'; GRANT SELECT, INSERT, UPDATE, DELETE ON clientes, produtos, pedidos, pedido_itens TO app_user; GRANT USAGE ON ALL SEQUENCES IN SCHEMA public TO app_user; • Ajustar o pg_hba.conf: # TYPE DATABASE host app_db host app_db • USER deploy_owner app_user ADDRESS 10.0.0.0/16 10.0.0.0/16 METHOD scram-sha-256 scram-sha-256 Utilização com o usuário app_user onde o filtro foi emitido pela aplicação: SELECT * FROM clientes WHERE tenant_id = 3;
  10. Implementação - Tabela compartilhada Isolamento por RLS com usuário único

    (app_user) Utiliza o Row Level Security (RLS) nas tabelas e ao início da sessão do usuário a aplicação deve utilizar uma variável de sessão para ajustar o tenant_id daquela sessão. • Criação dos usuários: CREATE ROLE app_user LOGIN PASSWORD '...'; GRANT SELECT, INSERT, UPDATE, DELETE ON clientes, produtos, pedidos, pedido_itens TO app_user; GRANT USAGE ON ALL SEQUENCES IN SCHEMA public TO app_user;
  11. Implementação - Tabela compartilhada Isolamento por RLS com usuário único

    (app_user) • Permitir o uso do RLS nas tabelas: ALTER TABLE pedidos ENABLE ROW LEVEL SECURITY; CREATE POLICY tenant_isolation_policy ON pedidos FOR ALL USING (tenant_id = current_setting('app.current_tenant', true)::integer); • Ajustar o pg_hba.conf: # TYPE host host • DATABASE app_db app_db USER deploy_owner app_user ADDRESS 10.0.0.0/16 10.0.0.0/16 METHOD scram-sha-256 scram-sha-256 Agora ao conectar com o usuário app_user configuramos a variável e fazemos a consulta normalmente (dentro de uma transação): BEGIN; SET LOCAL app.current_tenant = '3'; SELECT * FROM pedidos WHERE status = 'CONCLUIDO';
  12. Implementação - Tabela compartilhada Isolamento por RLS com um usuário

    por tenant Utiliza o Row Level Security (RLS) nas tabelas automaticamente o RLS é aplicado de acordo com o usuário conectado • Criar os usuários CREATE ROLE app_tenant NOLOGIN; CREATE ROLE comprafacil LOGIN PASSWORD '...' IN ROLE app_tenant; CREATE ROLE mercadodigital LOGIN PASSWORD '...' IN ROLE app_tenant; CREATE ROLE lojaagil LOGIN PASSWORD '...' IN ROLE app_tenant; CREATE ROLE clickecompre LOGIN PASSWORD '...' IN ROLE app_tenant; CREATE ROLE comerciofacil LOGIN PASSWORD '...' IN ROLE app_tenant; • Preparar a tabela para permitir o uso do RLS nas tabelas: ALTER TABLE pedidos ENABLE ROW LEVEL SECURITY; CREATE POLICY tenant_isolation_policy ON pedidos FOR ALL USING (tenant_id = (SELECT id FROM tenants WHERE slug = current_user));
  13. Implementação - Tabela compartilhada Isolamento por RLS com um usuário

    por tenant Utiliza o Row Level Security (RLS) nas tabelas automaticamente o RLS é aplicado de acordo com o usuário conectado • Ajustar o pg_hba.conf: # TYPE host host • DATABASE app_db app_db USER deploy_owner +app_tenant ADDRESS 10.0.0.0/16 10.0.0.0/16 Agora ao conectar fazemos a consulta normalmente: SELECT * FROM pedidos WHERE status = 'CONCLUIDO'; METHOD scram-sha-256 scram-sha-256
  14. Implementação - Tabela compartilhada Otimização de desempenho com índices compostos

    com a coluna tenant_id Todos os índices criados deverão incluir a coluna tenant_id, como primeira coluna. DROP INDEX pedidos_status_idx; CREATE INDEX ON pedidos ( tenant_id, status);
  15. Implementação - Tabela compartilhada Otimização de desempenho com particionamento pela

    coluna tenant_id CREATE TABLE pedidos ( id BIGINT GENERATED ALWAYS AS IDENTITY, tenant_id INTEGER NOT NULL REFERENCES tenants(id), cliente_id BIGINT NOT NULL, status TEXT NOT NULL DEFAULT 'pendente', total NUMERIC(14,2) NOT NULL DEFAULT 0, criado_em TIMESTAMPTZ NOT NULL DEFAULT now(), PRIMARY KEY (tenant_id, id) ) PARTITION BY LIST (tenant_id); CREATE TABLE pedidos_tenant_3 PARTITION OF pedidos FOR VALUES in (3);
  16. Implementação - Um schema por tenant Criação dos schemas: •

    Criação do schema template: CREATE SCHEMA tenant_template AUTHORIZATION deploy_owner; ALTER TABLE clientes SET SCHEMA tenant_template; ALTER TABLE produtos SET SCHEMA tenant_template; ALTER TABLE pedidos SET SCHEMA tenant_template; ALTER TABLE pedido_itens SET SCHEMA tenant_template;
  17. Implementação - Um schema por tenant Criação dos schemas: •

    Criação de função para popular os schemas de cada tenant: CREATE OR REPLACE FUNCTION provisionar_tenant(p_slug TEXT) RETURNS void LANGUAGE plpgsql AS $$ DECLARE tbl TEXT; BEGIN EXECUTE format('CREATE SCHEMA %I AUTHORIZATION deploy_owner', p_slug); FOR tbl IN SELECT tablename FROM pg_tables WHERE schemaname = 'tenant_template' LOOP EXECUTE format( 'CREATE TABLE %I.%I (LIKE tenant_template.%I INCLUDING ALL)', p_slug, tbl, tbl); END LOOP; END; $$;
  18. Implementação - Um schema por tenant Criação dos schemas: •

    Chamar função para criar schemas: SELECT provisionar_tenant('comprafacil'); SELECT provisionar_tenant('mercadodigital'); SELECT provisionar_tenant('lojaagil '); SELECT provisionar_tenant('clickecompre '); SELECT provisionar_tenant('comerciofacil '); Ou: SELECT provisionar_tenant(slug) FROM public.tenants;
  19. Implementação - Um schema por tenant Utilização de usuário único

    para todos os tenants: • Criar usuário: CREATE ROLE app_user LOGIN PASSWORD '...'; • Dar permissão em todos objetos de todos os schemas dos tenants: DO $$ DECLARE schema_name TEXT; BEGIN FOR schema_name IN SELECT slug FROM tenants LOOP EXECUTE format('GRANT USAGE ON SCHEMA %I TO app_user', schema_name); EXECUTE format( 'GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA %I TO app_user', schema_name); EXECUTE format( 'ALTER DEFAULT PRIVILEGES FOR ROLE deploy_owner IN SCHEMA %I GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO app_user',schema_name); END LOOP; END $$;
  20. Implementação - Um schema por tenant Utilização de usuário único

    para todos os tenants: • Ajustar o pg_hba.conf: # TYPE host host • DATABASE app_db app_db USER deploy_owner app_user ADDRESS 10.0.0.0/16 10.0.0.0/16 METHOD scram-sha-256 scram-sha-256 Ao conectar a aplicação deve sempre setar o search_path da conexão: SET search_path TO comprafacil, public; SELECT * FROM clientes; Ou escrever o nome do schema em cada comando SQL: SELECT * FROM comprafacil.clientes;
  21. Implementação - Um schema por tenant Utilização de um usuário

    por tenant: • Criar os usuários CREATE ROLE app_tenant NOLOGIN; CREATE ROLE comprafacil LOGIN PASSWORD '...' IN ROLE app_tenant; CREATE ROLE mercadodigital LOGIN PASSWORD '...' IN ROLE app_tenant; CREATE ROLE lojaagil LOGIN PASSWORD '...' IN ROLE app_tenant; CREATE ROLE clickecompre LOGIN PASSWORD '...' IN ROLE app_tenant; CREATE ROLE comerciofacil LOGIN PASSWORD '...' IN ROLE app_tenant;
  22. Implementação - Um schema por tenant Utilização de um usuário

    por tenant: • Dar permissão em todos objetos de cada schema para cada usuário e ajustar o search_path: DO $$ DECLARE schema_name TEXT; BEGIN FOR schema_name IN SELECT slug FROM tenants LOOP EXECUTE format('ALTER ROLE %I SET search_path TO %I, public', schema_name, schema_name); EXECUTE format('GRANT USAGE ON SCHEMA %I TO %I', schema_name, schema_name); EXECUTE format('GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA %I TO %I', schema_name, schema_name); EXECUTE format('ALTER DEFAULT PRIVILEGES FOR ROLE deploy_owner IN SCHEMA %I GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO %I',schema_name, schema_name); END LOOP; END $$;
  23. Implementação - Um schema por tenant Utilização de um usuário

    por tenant: • Ajustar o pg_hba.conf: # TYPE host host • DATABASE app_db app_db USER deploy_owner +app_tenant ADDRESS 10.0.0.0/16 10.0.0.0/16 METHOD scram-sha-256 scram-sha-256 Ao conectar a aplicação deve utilizar o usuário correto de cada tenant e fazer a consulta normalmente: SELECT * FROM clientes;
  24. Implementação - Um database por tenant Criação do banco de

    dados de controle: • Criar banco de dados: CREATE DATABASE controle OWNER deploy_owner; REVOKE ALL ON DATABASE controle FROM public; REVOKE ALL ON SCHEMA public FROM public; • Criar tabela tenants no database controle: CREATE TABLE tenants ( id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, slug TEXT NOT NULL UNIQUE, nome TEXT NOT NULL, criado_em TIMESTAMPTZ NOT NULL DEFAULT now()); INSERT INTO tenants (slug, nome) VALUES ('comprafacil', 'Compra Fácil'), ('mercadodigital', 'Mercado Digital'), ('lojaagil', 'Loja Ágil'), ('clickecompre', 'Click e Compre'), ('comerciofacil', 'Comércio Fácil');
  25. Implementação - Um database por tenant Criação do banco de

    dados de template: • Criar banco de dados: CREATE DATABASE db_template IS_TEMPLATE TRUE OWNER deploy_owner; REVOKE ALL ON DATABASE db_template FROM public; • Conectar na base db_template: REVOKE ALL ON SCHEMA public FROM public; • Criar todos os objetos no database db_template
  26. Implementação - Um database por tenant Utilização de usuário único

    para todos os tenants: • Criar usuário: CREATE ROLE app_user LOGIN PASSWORD '...'; • Dar permissão em todos objetos do template (conectado em db_template) antes de criar os databases de cada tenant: GRANT USAGE ON SCHEMA public TO app_user; GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO app_user; GRANT USAGE ON ALL SEQUENCES IN SCHEMA public TO app_user;
  27. Implementação - Um database por tenant Utilização de usuário único

    para todos os tenants: • Criar um database por tenant a partir do template (conectado na base controle: DO $$ DECLARE db_name TEXT; BEGIN FOR db_name IN SELECT slug FROM tenants LOOP EXECUTE format('CREATE DATABASE %I TEMPLATE db_template OWNER deploy_owner', db_name); EXECUTE format('GRANT CONNECT ON DATABASE %I TO app_user', db_name); END LOOP; END $$; • Configurar o pg_hba.conf: # TYPE host host DATABASE all all USER deploy_owner app_user ADDRESS 10.0.0.0/16 10.0.0.0/16 METHOD scram-sha-256 scram-sha-256
  28. Implementação - Um database por tenant Utilização de um usuário

    por tenant: • Criar os usuários CREATE ROLE app_tenant NOLOGIN; CREATE ROLE comprafacil LOGIN PASSWORD '...' IN ROLE app_tenant; CREATE ROLE mercadodigital LOGIN PASSWORD '...' IN ROLE app_tenant; CREATE ROLE lojaagil LOGIN PASSWORD '...' IN ROLE app_tenant; CREATE ROLE clickecompre LOGIN PASSWORD '...' IN ROLE app_tenant; CREATE ROLE comerciofacil LOGIN PASSWORD '...' IN ROLE app_tenant; • Dar permissões na base db_template: GRANT USAGE ON SCHEMA public TO app_tenant; GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO app_tenant; GRANT USAGE ON ALL SEQUENCES IN SCHEMA public TO app_tenant;
  29. Implementação - Um database por tenant Utilização de um usuário

    por tenant: • Criar um database por tenant a partir do template (conectado na base controle: DO $$ DECLARE db_name TEXT; BEGIN FOR db_name IN SELECT slug FROM tenants LOOP EXECUTE format('CREATE DATABASE %I TEMPLATE db_template OWNER deploy_owner', db_name); EXECUTE format('GRANT CONNECT ON DATABASE %I TO %I', db_name, db_name); END LOOP; END $$;
  30. Implementação - Um database por tenant Utilização de um usuário

    por tenant: • Configurar o pg_hba.conf: # TYPE DATABASE host all host sameuser • USER deploy_owner all ADDRESS 10.0.0.0/16 10.0.0.0/16 METHOD scram-sha-256 scram-sha-256 Ao conectar, a aplicação deve conectar no banco de dados correto de cada tenant e depois fazer a consulta normalmente: SELECT * FROM clientes;
  31. Implementação - Um database por tenant Acesso a tabelas compartilhadas

    no database controle A tabela tenant fica no database controle, e assim como ela, outras podem ser comuns a todos os tenants. Para acessar estes objetos temos algumas opções: • A aplicação abre uma conexão separada no database controle ◦ Toda a responsabilidade fica por conta da aplicação ◦ Pode abrir um grande número extra de conexões ◦ Não é possível fazer um JOIN entre tabelas compartilhadas e as tabelas locais
  32. Implementação - Um database por tenant Acesso a tabelas compartilhadas

    no database controle • O database de cada tenant cria um FDW (Foreign Data Wrapper) e mapeia os objetos do database controle localmente ◦ Permite realizar operações de leitura e escrita nas tabelas compartilhadas como se elas fossem locais ◦ Consultas pesadas ou muito frequentes podem ser mais lentas devida a operação remota ◦ Útil quando os objetos da base controle são acessados raramente
  33. Implementação - Um database por tenant Acesso a tabelas compartilhadas

    no database controle • O database controle faz uma replicação lógica das tabelas para o database de cada tenant ◦ Cria uma cópia local de cada tabela do database controle. ◦ Não permite gravação nas tabelas compartilhadas ◦ Se as tabelas forem muito grandes, pode duplicar um grande volume de dados ◦ Útil quando são feitas muitas consultas nas tabelas compartilhadas
  34. Segurança O desafio da segurança • Isolamento dos dados ◦

    • Vazamento por erro na aplicação ◦ • Risco de erro no código da aplicação comprometer outros tenants SQL injection ◦ • Isolar os dados de cada tenant em todas tabelas Risco de um SQL injection comprometer outros tenants Monitoramento e auditoria ◦ ◦ ◦ Monitorar conexões em tempo real sabendo qual tenant está conectado Gerar estatísticas de acesso por tenant Gerar logs e auditoria por tenant
  35. Segurança Isolamento dos dados • Tabela compartilhada ◦ Isolamento pela

    aplicação: filtro em cada tabela pela coluna tenant_id ◦ RLS na coluna tenant_id com usuário único: ajusta uma variável no início da sessão: SET LOCAL app.current_tenant = '3'; ◦ RLS na coluna tenant_id com um usuário por tenant: Regra de RLS é definida de acordo com o usuário conectado
  36. Segurança Isolamento dos dados • • Um schema por tenant

    ◦ Usuário único: ajusta o search_path no início da sessão: SET search_path TO comprafacil, public; ◦ Um usuário por tenant: cada usuário tem seu próprio search_path definido quando o tenant é criado Um database por tenant ◦ Usuário único: a aplicação se conecta no database específico do tenant ◦ Um usuário por tenant: a aplicação se conecta no database específico do tenant com o usuário específico daquele tenant
  37. Segurança Vazamento por erro na aplicação • Tabela compartilhada ◦

    Isolamento pela aplicação: ▪ ◦ RLS na coluna tenant_id com usuário único: ▪ ▪ ▪ ◦ Os tenants estão 100% expostos Risco de esquecer de ajustar a variável no início da sessão Risco de falha na configuração do RLS em cada tabela Risco de um pool de conexão utilizar a variável da sessão anterior RLS na coluna tenant_id com um usuário por tenant ▪ Risco de falha na configuração do RLS em cada tabela
  38. Segurança Vazamento por erro na aplicação • Um schema por

    tenant ◦ ◦ • Usuário único ▪ Risco de esquecer de ajustar o search_path no início da sessão ▪ Risco de um pool de conexão utilizar o search_path da sessão anterior Um usuário por tenant: ▪ Risco muito baixo Um database por tenant ◦ ◦ Usuário único ▪ Erro na string de conexão Um usuário por tenant: ▪ Erro na string de conexão e na senha
  39. Segurança SQL injection • Tabela compartilhada com usuário único ◦

    Se a variável de sessão for contornada, todos os tenants estão expostos • Um schema por tenant com usuário único ◦ • Pode inserir o nome de outro schema na consulta e expor qualquer tenant Um database por tenant ◦ Sem risco de SQL injection
  40. Segurança Monitoramento e auditoria • Tabela compartilhada ◦ ◦ •

    Um schema por tenant ◦ ◦ • Usuário único: Não é possível identificar qual o tenant sem olhar o tenant_id na consulta Um usuário por tenant: Rastreamento por usuário Usuário único: Não é possível identificar qual o tenant Um usuário por tenant: Rastreamento por usuário Um database por tenant ◦ ◦ Usuário único: Rastreamento por database Um usuário por tenant: Rastreamento por database e por usuário
  41. Segurança Modelo Usuário Barreira de segurança Vazamento por erro em

    código Vazamento por SQL injection Auditoria / Monitoramento Tabela compartilhada Único Controlada pela aplicação 🔴Muito alto 🔴Muito alto 🟠 Ruim Tabela compartilhada Único Políticas de RLS. Aplicação ajusta variável no início da transação 🟡 Médio 🟠 Alto 🟠 Ruim Tabela compartilhada Por tenant Política de RLS, validada pelo usuário conectado 🟡 Médio 🟢 Baixo 🟢Bom
  42. Segurança Modelo Usuário Barreira de segurança Vazamento por erro em

    código Vazamento por SQL injection Auditoria / Monitoramento Um schema por tenant Único Parâmetro search_path. Aplicação ajusta o parâmetro no início da sessão 🟠 Alto 🟠 Alto 🟠 Ruim Um schema por tenant Por tenant Parâmetro search_path ajustado por usuário nativamente + pg_hba.conf + GRANT / REVOKE por usuário 🔵Muito Baixo 🟢Baixo 🟢Bom Um database por tenant Único Isolamento físico por database 🟢Baixo 🔵Baixíssimo 🟡 Médio Um database por tenant Por tenant Isolamento físico por database + pg_hba.conf + GRANT / REVOKE por usuário. 🔵Baixíssimo 🔵Baixíssimo 🔵Excelente
  43. Desempenho Métricas de desempenho Conforme o número de tenants aumenta,

    o consumo de recursos tende a aumentar: CPU, Memória, disco e redo. Existem pontos de atenção que merecem destaque: • Desempenho no acesso aos dados de cada tenant ◦ ◦ • Grande volume de dados por tenant pode impactar no desempenho das consultas. Crítico no uso de tabelas compartilhadas ▪ Índices compostos com a coluna tenant_id diminui o problema ▪ Particionamento de tabelas por tenant_id elimina o problema Desempenho no acesso ao catálogo ◦ ◦ Grande volume de objetos por tenant podem tornar o catálogo de dados inchado. Crítico com muitos objetos e muitos tenants em: ▪ Um schema por tenant ▪ Tabelas compartilhadas com particionamento de tabelas
  44. Desempenho Métricas de desempenho • Desempenho no estabelecimento das conexões

    ◦ ◦ • Um grande volume de conexões por tenant pode gerar um alto número de conexões e desconexões consumindo muita memória e CPU em system space Pool de conexões alivia o problema. ▪ Cada tenant requer um pool separado em: • Um database por tenant • Um usuário por tenant (em qualquer um dos 3 modelos) • Recomenda-se criar pools pequenos e com desconexão após período menor de inatividade para minimizar o número de conexões. Consumo de memória ◦ No modelo com um database por tenant, um alto número de tenants exerce uma pressão significativa no consumo de memória e no gerenciamento de muitos arquivos abertos ao mesmo tempo para gerenciar cada database.
  45. Desempenho Modelo Alto volume de tenants (no mesmo servidor) Alto

    volume de dados por tenant Alto número de objetos por tenant Alto número de conexões por tenant Tabela compartilhada 🔵Muito bom 🟡 Médio (c/ particionamento de tabelas) 🔴Muito ruim 🟠 Ruim (c/ índices compostos) 🟡 Médio (c/ particionamento de tabelas) 🟢Bom 🟠 Ruim (c/ particionamento de tabelas 🟢Bom (c/ usuário único) 🟠 Ruim (c/ um usuário por tenant) Um schema por tenant 🟠 Ruim 🟢Bom 🔴Muito ruim 🟢Bom (c/ usuário único) 🟠 Ruim (c/ um usuário por tenant) Um database por tenant 🔴Muito ruim 🔵Muito bom 🟢Bom 🟠 Ruim
  46. Outros fatores importantes • Uso de dados/objetos compartilhados por todos

    os tenants ◦ Complexo no modelo de um database por tenant (uso de FDW ou réplica lógica) • Consultas analíticas cruzando dados de vários tenants ◦ Muito complexo e lento para realizar no modelo de um tenant por database (uso de FDW) • Autovacuum ◦ Pode gerar uma grande fila com muitos tenants no modelo de um database por tenant. • Deploy ◦ Pode gerar locks em todos os tenants ao mesmo tempo no modelo de tabela compartilhada ◦ Aumenta a complexidade executar o deploy em vários tenants nos outros modelos. Funciona melhor utilizando uma ferramenta externa.
  47. Outros fatores importantes • Personalização de DDL por tenant ◦

    Muito difícil de realizar no modelo de tabela compartilhada. • Exportar / mover / remover dados de um único tenant ◦ Mais complexo no modelo de tabela compartilhada, com o uso de COPY com SELECT. Se não utilizar particionamento, remover um tenant pode exigir o uso do DELETE em várias tabelas. • Ajustes de desempenho por tenant ◦ Difícil de realizar com um usuário por tenant ◦ Melhor cenário com um database e um usuário por tenant
  48. Outros fatores importante Modelo Objetos compartilhados por todos os tenants

    Consultas analíticas com dados de vários tenants Ajustes de desempenho por tenant 🔴Muito ruim (com usuário único) 🟡 Médio (com um usuário por tenant) Deploy Tabela compartilhada 🔵Muito bom 🔵Muito bom Um schema por tenant 🔵Muito bom 🟠 Ruim 🟠 Ruim (com usuário único) 🟢Bom (com um usuário por tenant) 🟠 Ruim Um database por tenant 🟠 Ruim 🔴Muito ruim 🟡 Médio (com usuário único) 🔵Muito bom (com um usuário por tenant) 🔴Muito ruim 🟡 Médio
  49. Outros fatores importante Modelo Personalização de DDL por tenant Exportar

    / mover dados por tenant Tabela compartilhada 🔴Muito ruim 🟠 Ruim Um schema por tenant 🔵Muito bom 🟢Bom 🟢Bom 🟡 Médio Um database por tenant 🔵Muito bom 🔵Muito bom 🟠 Ruim 🟡 Médio Autovacuum 🟡 Médio Aderência a Frameworks 🔵Muito bom
  50. Recomendações Quando utilizar cada modelo? • Tabela compartilhada (usuário único):

    ◦ ◦ ◦ ◦ ◦ • Um schema por tenant: ◦ ◦ ◦ • Grande quantidade de tenants (> 1000) Baixo volume de dados por tenant (< 1GB) Aplicação de prateleira, sem a necessidade de customizações para clientes específicos Baixa exigência de segurança Consultas analíticas envolvendo vários tenants Volume médio de tenants (> 100) Volume médio de dados por tenant (< 100GB) Meio termo entre flexibilidade, desempenho e segurança. Um database por tenant (um usuário por database): ◦ ◦ ◦ ◦ Pequena quantidade de tenants (< 100) Alto volume de dados por tenant (> 100GB) Clientes enterprise com alta exigência de desempenho e segurança Baixo uso de dados compartilhados entre os tenants