DevGuideTK
← Trilha Backend

Plano de execução

Como ler o EXPLAIN para entender por que uma consulta está lenta.

As decisões em torno do plano de execução são quatro: como encontrar as consultas lentas, como investigar cada uma, como corrigir e como impedir que o problema volte.

Como encontrar as consultas lentas

FerramentaO que mostraCusto
pg_stat_statementsTempo total, médio e número de execuções de cada consulta, agrupadas pelo formatoBaixo; precisa estar em shared_preload_libraries
auto_explainO plano das consultas acima de um tempo, no logBaixo sem log_analyze; com ele, mede cada consulta
Log de consultas lentas (log_min_duration_statement)O texto e a duração das consultas acima de um tempoBaixo
Rastreamento da aplicaçãoQual rota disparou qual consulta, e quanto ela pesou no pedidoDepende da ferramenta; ver o tópico de rastreamento

Comece pelo pg_stat_statements, ordenado pelo tempo total, e não pelo tempo médio. Uma consulta de 2 ms executada um milhão de vezes por dia pesa mais que uma de 3 segundos que roda uma vez. E o caso típico do tópico de ORMs, o N+1, aparece justamente assim: uma consulta rápida, repetida demais.

No MySQL, os equivalentes são o performance_schema e o log de consultas lentas (slow query log), e o EXPLAIN ANALYZE existe a partir da versão 8.0.18.

Como investigar uma consulta

Um roteiro que resolve a maioria dos casos:

  1. Rode EXPLAIN (ANALYZE, BUFFERS) com valores reais, num banco com volume parecido com o de produção. Planos mudam com o volume.
  2. Ache o nó mais caro: o de maior actual time multiplicado por loops.
  3. Compare rows estimado com o real nesse nó e nos de baixo. Uma diferença de dez vezes ou mais aponta estatísticas ruins ou uma condição que o planejador não sabe estimar.
  4. Procure Seq Scan com Rows Removed by Filter grande: falta índice, ou algo impede o uso dele.
  5. Confira Buffers: read alto: dados fora da memória, comuns na primeira execução. Rode duas vezes antes de concluir.
  6. Veja Sort Method: external merge: a ordenação não coube na memória (work_mem).

Planos de texto longos ficam mais fáceis de ler em visualizadores, como o explain.dalibo.com, que destacam o nó mais caro.

Como corrigir

Sintoma no planoCausa provávelCorreção
Seq Scan com muitas linhas descartadasFalta índiceÍndice nas colunas do WHERE, na ordem certa (ver o tópico de índices)
Filter com uma função na colunaFunção impede o índiceÍndice na expressão, ou reescrever a condição como faixa
Filter com ::numeric ou outra conversão na colunaTipo do parâmetro diferente do da colunaCorrigir o tipo no código ou no ORM
Seq Scan com LIKE 'prefixo%'Collation diferente de CÍndice com text_pattern_ops
Estimativa de 1 linha e milhares reaisEstatísticas velhasANALYZE; ajustar o autovacuum da tabela
Estimativa errada em colunas relacionadas (cidade e estado)O planejador supõe colunas independentesEstatísticas estendidas: CREATE STATISTICS
Sort sobre a tabela inteira com LIMITSem índice na ordem pedidaÍndice na coluna do ORDER BY
Sort Method: external mergePouca memória para ordenarÍndice na ordem, ou aumentar work_mem para essa consulta
Nested Loop com milhares de loopsEstimativa baixa no lado externoCorrigir a estimativa; raramente, reescrever a consulta

Evite forçar planos. O PostgreSQL não tem dicas de plano por padrão, de propósito: um plano forçado hoje fica errado quando os dados mudam. Corrija a causa (índice, tipo, estatística), e o planejador escolhe sozinho.

Como impedir que volte

PráticaComo
Teste que confere o uso de índicePlanejar as consultas críticas com enable_seqscan = off e falhar se ainda aparecer Seq Scan, como mostra a Implementação
EXPLAIN na revisão de códigoConsultas novas em tabelas grandes chegam com o plano na descrição da mudança
ANALYZE depois de cargasMigrações e importações grandes terminam com ANALYZE nas tabelas afetadas
Acompanhamento contínuoUm painel com as consultas de maior tempo total do pg_stat_statements

O teste automatizado tem uma armadilha. Em bancos de teste pequenos, o planejador escolhe Seq Scan mesmo com índice, porque ler 10 linhas de uma vez é mais barato. No teste da Implementação, uma tabela vazia com índice mostrou Seq Scan no plano padrão. Desligar a varredura completa (enable_seqscan = off) resolve: com ela desligada, o Seq Scan só aparece quando nenhum índice serve para a consulta.

Resumo da decisão

  1. pg_stat_statements para achar as consultas de maior tempo total.
  2. EXPLAIN (ANALYZE, BUFFERS) com volume realista, olhando o nó mais caro e a diferença entre estimado e real.
  3. Corrigir a causa: índice, expressão, tipo, estatística. Não forçar o plano.
  4. ANALYZE depois de cargas grandes.
  5. Teste automatizado do uso de índice nas consultas críticas, com enable_seqscan = off.

O próximo nível mostra esse teste nas cinco linguagens e os comandos de investigação.

Prompts para aprofundar

Ao abrir no ChatGPT ou no Claude, o prompt é enviado ao serviço escolhido pela URL e também fica copiado na sua área de transferência.

  • Investigar uma consulta lenta

    Tenho uma consulta lenta e quero investigá-la com método.
    
    Pergunte qual banco e versão eu uso, peça a consulta, o volume das tabelas envolvidas e os índices existentes, e depois a saída do EXPLAIN (ANALYZE, BUFFERS).
    
    Me guie:
    1. Qual é o nó mais caro.
    2. Onde as estimativas erram, e por quê.
    3. Qual é a causa provável.
    4. Qual é a correção, e como confirmar com um novo plano.
    
    Não proponha índices sem antes olhar o plano.
  • Montar o acompanhamento de consultas lentas

    Quero acompanhar as consultas lentas do meu banco em produção.
    
    Pergunte qual banco, versão e serviço gerenciado eu uso, e que ferramentas de observabilidade já existem.
    
    Monte comigo:
    1. A configuração do pg_stat_statements (ou equivalente) e do log de consultas lentas.
    2. O auto_explain, com os limites certos para não pesar no banco.
    3. As consultas para achar o maior tempo total e as que mais pioraram.
    4. Como ligar uma consulta lenta à rota da aplicação que a disparou.
    5. Uma rotina semanal de revisão.
  • Estatísticas e planos instáveis

    Algumas consultas do meu sistema ficam lentas de vez em quando, sem mudança no código.
    
    Pergunte qual banco eu uso, como os dados chegam (cargas grandes, importações) e como o autovacuum está configurado.
    
    Explique:
    1. Como as estatísticas influenciam o plano.
    2. Quando o autovacuum demora a atualizá-las, e como ajustar por tabela.
    3. Estatísticas estendidas para colunas relacionadas.
    4. O efeito de parâmetros diferentes no mesmo formato de consulta.
    5. Como confirmar que o problema é de estatística, com os comandos.