Noções Básicas de EXPLAIN
EXPLAIN mostra o caminho de acesso escolhido pelo planejador antes de você arcar com o custo de execução de uma consulta. EXPLAIN (ANALYZE, BUFFERS) executa a consulta e adiciona tempos e I/O de buffer para que você possa ver o que realmente aconteceu no PostgreSQL 18.4.
Receita
Cartão de referência rápida - pronto para copiar e colar.
EXPLAIN (ANALYZE, BUFFERS, VERBOSE, SETTINGS, WAL)
SELECT o.id, o.total
FROM orders o
WHERE o.status = 'shipped'
AND o.created_at >= now() - interval '7 days';Quando usar isso: Qualquer consulta lenta ou surpreendente em um conjunto de dados semelhante à produção. Capture o plano antes e depois de alterações de índice ou estatísticas.
Exemplo de Trabalho
CREATE TABLE orders (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
customer_id bigint NOT NULL,
status text NOT NULL DEFAULT 'pending',
created_at timestamptz NOT NULL DEFAULT now(),
total numeric(12,2) NOT NULL
);
CREATE INDEX orders_status_created_idx ON orders (status, created_at);
INSERT INTO orders (customer_id, status, created_at, total)
SELECT
(random() * 10000)::bigint,
CASE WHEN random() < 0.05 THEN 'shipped' ELSE 'pending' END,
now() - (random() * interval '90 days'),
(random() * 500)::numeric(12,2)
FROM generate_series(1, 200000);
ANALYZE orders;
EXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT)
SELECT id, total
FROM orders
WHERE status = 'shipped'
AND created_at >= now() - interval '7 days';Um plano saudável geralmente mostra Index Scan em orders_status_created_idx com baixos rows removed by filter e contagens modestas de shared hit.
O que isso demonstra:
ANALYZEantes de comparar planos- Leitura do tipo de scan e nome do índice
- Interpretação de
actual timeerowsversus estimativas do planejador - Uso de
BUFFERSpara identificar cache misses
Mergulho Profundo
Como Funciona
- O planejador constrói uma árvore de nós (scan, join, sort, aggregate). Cada nó tem contagens de linhas estimadas e reais.
EXPLAINsozinho é barato e seguro em produção.ANALYZEexecuta a consulta, então use-o em staging ou com guardasLIMITem instruções destrutivas.BUFFERSrelatashared hit(cache) eshared read(disco). Altoreadem uma consulta frequente significa que mais RAM ou melhor indexação podem ajudar.SETTINGS(PostgreSQL 12+) mostra GUCs não padrão que influenciaram o plano, útil quandorandom_page_costouwork_memdiferem por role.
Opções de EXPLAIN em Resumo
| Opção | Propósito |
|---|---|
ANALYZE | Executa a consulta; mostra tempos reais |
BUFFERS | Hit/leitura de buffer por nó |
VERBOSE | Saída de listas de colunas e nomes de esquema |
SETTINGS | Mostra valores de GUC que afetam o planejador |
WAL | Bytes WAL gerados (escritas) |
FORMAT JSON | Plano legível por máquina para ferramentas |
Notas SQL
-- Salva um plano para um ticket (sem execução)
EXPLAIN (FORMAT JSON)
SELECT count(*) FROM orders WHERE status = 'pending';
-- Compara apenas estimativas (seguro em produção)
EXPLAIN (VERBOSE)
SELECT * FROM orders WHERE customer_id = 42;Armadilhas
- Executar
EXPLAIN ANALYZEemDELETE/UPDATEsem cuidado - Você modifica dados de produção. Correção: Envolva em uma transação eROLLBACK, ou teste em um snapshot. - Confiar em números de cache frio uma vez - A primeira execução após a reinicialização parece pior do que o estado estável. Correção: Execute duas vezes; compare o segundo resultado de
ANALYZE. - Ignorar a deriva da estimativa de linhas -
rows=1000vsactual rows=85000sinaliza estatísticas ruins ou predicados correlacionados. Correção:ANALYZEe verifique estatísticas estendidas. - Ler apenas o tempo total - Um nó externo barato pode esconder um loop aninhado caro por dentro. Correção: Encontre o nó com a maior participação em
actual time. - Esquecer
search_path- Os planos diferem quando a qualificação do esquema muda. Correção: Definasearch_pathexplicitamente na sessão que você captura.
Alternativas
| Alternativa | Use Quando | Não Use Quando |
|---|---|---|
auto_explain | Captura contínua de consultas lentas | Você precisa de iteração interativa |
pg_stat_statements | Custo agregado por texto de consulta | Você precisa de detalhes de plano pontuais |
EXPLAIN (GENERIC_PLAN) | Planejamento de prepared statement | Estatísticas de tabela muito desatualizadas |
FAQs
EXPLAIN é seguro em produção?
EXPLAIN sem ANALYZE é um planejamento somente leitura. EXPLAIN ANALYZE executa a consulta; use cautela com escritas e scans pesados.
O que significa "Seq Scan"?
PostgreSQL leu o heap sequencialmente. Frequentemente correto para grandes frações da tabela ou tabelas pequenas.
Por que as linhas estimadas e reais diferem?
Estatísticas desatualizadas, colunas correlacionadas ou dados distorcidos. Execute ANALYZE e considere estatísticas estendidas.
Devo usar o formato TEXT ou JSON?
TEXT para humanos em tickets. JSON para explain.dalibo.com, pganalyze ou ferramentas de diff de plano em CI.
EXPLAIN mostra esperas de bloqueio?
Não. Verifique pg_stat_activity e wait_event_type para contenção de bloqueio separada da forma do plano.
O que é "Buffers: shared hit=..."?
Páginas encontradas nos buffers compartilhados do PostgreSQL (cache) versus lidas do SO/disco.
Posso usar EXPLAIN em um prepared statement?
Sim: EXPLAIN EXECUTE stmt_name(args) ou EXPLAIN (GENERIC_PLAN) EXECUTE ... para planos genéricos.
Como oculto literais sensíveis?
Use placeholders de parâmetro em consultas de aplicativos; evite colar PII em logs de plano compartilhados.
O PostgreSQL 18 altera a saída do EXPLAIN?
O formato principal da árvore é estável; novos nós do planejador podem aparecer. Sempre fixe a versão em linhas de base de regressão.
O que devo ler a seguir?
Trade-offs entre scan sequencial e scan de índice e páginas de algoritmo de join nesta seção.
Relacionado
- Seq Scan vs Index Scan - Quando cada scan vence
- Nested Loop, Hash Join, Merge Join - Nós de join em planos
- Estudos de Caso de Planos Ruins - Padrões reais de estimativa incorreta
- Noções Básicas de Estatísticas - Por que as estimativas dão errado
Versões de Pilha: Esta página foi escrita para PostgreSQL 18.4 (estável 18, manutenção 17), pgvector 0.8+, PgBouncer 1.x, Patroni 3.x e PostGIS 3.5+.