Como implementar índices parciais em SaaS com muitos tenants: guia prático para produção
Aprenda a usar índices parciais no PostgreSQL para acelerar consultas críticas em SaaS multi-tenant, reduzir o tamanho dos índices e validar ganhos com segurança em produção.
Max Alex

Em um SaaS multi-tenant, uma única tabela pode reunir dados de centenas ou milhares de clientes. Com o tempo, índices convencionais passam a incluir registros históricos, inativos ou excluídos logicamente que não participam das telas e fluxos mais importantes. Os índices parciais PostgreSQL ajudam a concentrar o índice no subconjunto realmente consultado.
Este guia mostra como selecionar uma boa candidata, desenhar o índice, criar a estrutura com segurança e comprovar o ganho antes de adotá-la em produção.
Por que índices convencionais se tornam caros em SaaS multi-tenant
Em tabelas compartilhadas, o volume total cresce com todos os tenants, mas as consultas críticas quase nunca precisam percorrer todos os estados e todo o histórico. Um índice completo sobre uma tabela de pedidos, por exemplo, também armazena pedidos cancelados, arquivados e antigos.
Isso aumenta o tamanho do índice, disputa espaço no cache e amplia o trabalho de manutenção em inserções e atualizações. A distribuição entre tenants também tende a ser desigual: poucos clientes podem concentrar grande parte das linhas e alterar o perfil de carga.
Imagine uma tabela orders com 50 milhões de linhas. Se o painel operacional consulta somente pedidos abertos, pagos ou em processamento dos últimos 90 dias, indexar indiscriminadamente todo o histórico pode custar mais do que entrega. Esse é um cenário típico em que vale investigar a performance de consultas no PostgreSQL e a seletividade dos filtros.
O que são índices parciais e quando eles funcionam melhor
Um índice parcial contém apenas as linhas que atendem a uma condição definida na cláusula WHERE do próprio índice. Ele funciona melhor quando esse predicado representa uma parcela pequena e estável da tabela e aparece de forma compatível nas consultas.
CREATE INDEX CONCURRENTLY idx_orders_active_tenant_created_at
ON orders (tenant_id, created_at DESC)
WHERE status IN ('open', 'paid', 'processing');Nesse exemplo, pedidos fora desses estados não ocupam espaço nesse índice nem exigem sua atualização. Porém, o otimizador precisa conseguir concluir que a consulta também respeita o predicado. Uma consulta por status = 'open' é compatível; uma consulta sem filtro de status, em geral, não é.
Índices parciais não corrigem filtros mal definidos, joins caros ou ordenações desalinhadas. Eles são uma otimização direcionada a um padrão de acesso comprovado.
Como escolher consultas candidatas usando dados reais de produção
Comece pelas consultas com maior custo acumulado, não apenas pelas mais lentas isoladamente. Uma consulta moderadamente lenta executada milhares de vezes por hora pode ter prioridade maior que um relatório pesado executado uma vez por dia.
Use pg_stat_statements, logs de consultas lentas e métricas da aplicação para identificar frequência, tempo médio, linhas retornadas e tabelas envolvidas. Em seguida, rode EXPLAIN (ANALYZE, BUFFERS) com parâmetros representativos.
Procure filtros como tenant_id, status operacionais, deleted_at IS NULL e recortes de dados recentes. Uma boa candidata costuma retornar uma fração pequena da tabela e ter uma forma de consulta repetível. Evite criar índice para uma consulta ocasional ou para uma condição que ainda seleciona a maior parte das linhas.
Por exemplo, se uma busca por tenant e status pendente aparece milhares de vezes por hora e retorna menos de 1% das linhas, ela merece uma análise detalhada de seletividade e plano de execução.
Como projetar o índice parcial para consultas por tenant
O desenho deve refletir a consulta, não apenas a tabela. Em um modelo com isolamento lógico por coluna, tenant_id normalmente faz parte da chave porque restringe o conjunto logo no início.
Colunas filtradas por igualdade tendem a vir antes de colunas usadas para ordenação ou intervalo. Para uma listagem de tickets abertos ordenada pela atualização mais recente, um padrão possível é:
CREATE INDEX CONCURRENTLY idx_tickets_open_by_tenant_updated
ON tickets (tenant_id, updated_at DESC)
WHERE deleted_at IS NULL
AND status IN ('open', 'pending');Se a consulta sempre lê colunas adicionais, avalie INCLUDE para favorecer leituras cobertas sem colocá-las na chave de ordenação. Mantenha o predicado simples e explícito; isso facilita a manutenção e aumenta a chance de o PostgreSQL reconhecer sua compatibilidade.
Também confira se a consulta usa a mesma semântica. Alterar o filtro para uma expressão diferente, usar valores nulos de forma ambígua ou omitir uma condição central pode impedir o uso do índice parcial.
Criação segura em produção: CONCURRENTLY, migrações e limites
Em tabelas ativas, prefira CREATE INDEX CONCURRENTLY. A criação concorrente reduz o bloqueio sobre as operações normais da aplicação, embora ainda consuma I/O, CPU e tempo de processamento.
Esse comando não pode ser executado dentro de uma transação convencional. Configure a migração para rodar sem transação, ou execute-a como uma etapa operacional separada, conforme as capacidades da sua ferramenta de migração.
Antes da mudança, teste em um ambiente próximo da produção e estime a duração com volume semelhante. Durante a criação, acompanhe atividade do banco, latência da aplicação, I/O e eventuais esperas. Se a criação falhar, valide a existência de índices inválidos antes de repetir a operação.
Planeje também o rollback: documente como verificar uso e em que condição o índice será removido. Criar estruturas sem uma rotina de revisão transforma uma otimização pontual em custo permanente.
Como validar se o PostgreSQL está usando o índice parcial
A validação começa com o plano de execução. Execute a consulta real, ou uma equivalente com parâmetros de produção, usando EXPLAIN (ANALYZE, BUFFERS) antes e depois da implantação.
EXPLAIN (ANALYZE, BUFFERS)
SELECT id, status, updated_at
FROM tickets
WHERE tenant_id = $1
AND deleted_at IS NULL
AND status IN ('open', 'pending')
ORDER BY updated_at DESC
LIMIT 50;Observe se o plano passou a usar Index Scan ou Index Only Scan, além do tempo total, blocos lidos e número de linhas examinadas. Mais importante que um tipo de scan isolado é o ganho mensurável na consulta crítica sem regressão nas escritas.
Teste tenants pequenos, médios e grandes. Um índice pode ser excelente para um tenant volumoso e irrelevante para outro; essa variação é normal em PostgreSQL multi-tenant. Caso as estimativas estejam muito distantes do resultado real, revise estatísticas e a forma da consulta antes de concluir que o índice é inadequado.
Erros comuns, monitoramento contínuo e quando não usar índices parciais
O erro mais comum é criar um índice parcial para cada combinação de status, tenant ou tela sem evidência de uso. Isso fragmenta a estratégia de índices e aumenta o custo de escrita e manutenção.
Predicados pouco seletivos também decepcionam. Se 85% dos pedidos estão ativos, o índice parcial pode continuar grande demais para justificar sua existência. Nesse caso, um índice composto convencional, revisão da consulta ou particionamento podem ser opções mais adequadas.
Tenha cautela com condições dinâmicas, como datas relativas. Um predicado baseado em “últimos 90 dias” não se atualiza sozinho no índice à medida que o tempo passa; frequentemente é melhor modelar a consulta e a estratégia de retenção de outra forma.
Monitore uso, tamanho, bloat, impacto em escrita e sobreposição com outros índices. Revise periodicamente estruturas que não aparecem nos planos relevantes. Para volumes em que o principal desafio é o gerenciamento físico do histórico, o particionamento de tabelas pode resolver uma camada diferente do problema.
Conclusão
Índices parciais PostgreSQL são especialmente úteis quando consultas frequentes de um SaaS acessam apenas dados ativos, recentes ou não excluídos de cada tenant. O resultado depende menos da sintaxe e mais da disciplina de medir: escolha uma consulta recorrente, confirme a seletividade, crie o índice com segurança e compare os planos com dados realistas.
Avalie as consultas mais executadas do seu SaaS, escolha um caso com filtro altamente seletivo e valide um índice parcial em ambiente controlado antes de levá-lo à produção.
Max Alex
Criador de conteúdo apaixonado por tecnologia e inovação. Acompanhe nossos artigos para ficar por dentro das melhores estratégias digitais.
Continue lendo

Como implementar templates de mensagem em campanhas transacionais: guia prático para produção
Aprenda a implementar templates de mensagem para campanhas transacionais, do mapeamento de eventos e variáveis à aprovação, integração, testes e monitoramento em produção.
Max Alex

Como implementar webhooks idempotentes em assinaturas recorrentes: guia prático para produção
Aprenda a receber, validar, deduplicar e processar webhooks de assinaturas recorrentes sem cobranças duplicadas, estados inconsistentes ou falhas difíceis de auditar.
Max Alex

Como implementar isolamento por tenant em banco compartilhado: guia prático para produção
Aprenda a implementar isolamento por tenant em banco compartilhado com tenant_id, Row Level Security, controles na aplicação, testes de acesso cruzado e práticas de produção.
Max Alex