Um EXPLAIN simples mostra apenas o plano e as estimativas do planejador. EXPLAIN (ANALYZE, BUFFERS) executa de fato a consulta e relata linhas, tempo e acessos a paginas reais. O desvio entre o estimado e o real costuma ser justamente o que aponta o problema.
Valores reais contra estimados
Cada no do plano mostra dois pares de numeros. Os estimados sao cost e rows (o que o planejador supos antes de rodar). Os reais sao actual time e rows (o que de fato aconteceu).
EXPLAIN (ANALYZE, BUFFERS)
SELECT * FROM orders WHERE user_id = 5;
Uma linha de no tipica fica assim:
Seq Scan on orders (cost=0.00..1934.00 rows=12 width=40)
(actual time=0.40..18.7 rows=9 loops=1)
cost=0.00..1934.00 e uma estimativa em unidades arbitrarias: inicio e varredura completa.
rows=12 era esperado, actual ... rows=9 e o que realmente voltou.
actual time=0.40..18.7 sao milissegundos ate a primeira e ate a ultima linha.
Leia o plano de baixo para cima e de dentro para fora: os nos folha executam primeiro.
Loops: multiplique pelo numero de passagens
Em um nested loop o no interno roda muitas vezes, e seu actual time/rows e relatado por passagem. Multiplique por loops para obter o total.
EXPLAIN (ANALYZE, BUFFERS)
SELECT u.email, o.amount
FROM users u
JOIN orders o ON o.user_id = u.id
WHERE u.country = 'DE';
Se o Index Scan interno mostra actual time=0.02 rows=3 loops=5000, nao sao 0.02 ms e sim cerca de 100 ms no total e 15000 linhas. Gotcha: um tempo por passagem minimo com um loops grande e a forma mais comum de subestimar um no caro.
O desvio entre estimado e real
Uma grande diferenca entre rows estimado e real e o sinal principal. O planejador escolhe a estrategia pela estimativa; se a estimativa mente, ele escolhe um plano ruim.
ANALYZE orders;
EXPLAIN (ANALYZE, BUFFERS)
SELECT * FROM orders
WHERE status = 'paid' AND created_at >= '2026-01-01';
- Estimativa errada por dezenas de vezes costuma indicar estatisticas obsoletas: rode
ANALYZE.
- O desvio sobrevive ao
ANALYZE: provavelmente predicados correlacionados; ajudam estatisticas estendidas (CREATE STATISTICS) ou um indice.
- Uma superestimativa faz o planejador gastar esforco em um Seq Scan em vez de um Index Scan.
BUFFERS: shared hit e read
BUFFERS mostra o trabalho de memoria em cada no. shared hit sao paginas achadas em cache; shared read sao paginas lidas do disco (lento).
Seq Scan on orders (actual time=0.4..210 rows=2000000 loops=1)
Buffers: shared hit=128 read=16234
shared hit e um acerto em shared_buffers, barato.
shared read e uma falha de cache, uma leitura fisica; muitos read significam um no pesado em I/O.
Buffers diz se a consulta foi lenta por causa do disco ou por causa de um plano ruim.
Um Seq Scan que deveria ser Index Scan
O caso pratico principal: uma consulta filtra por uma coluna seletiva, mas voce ve um Seq Scan com read enorme e tempo real alto.
EXPLAIN (ANALYZE, BUFFERS)
SELECT * FROM orders WHERE user_id = 5;
CREATE INDEX idx_orders_user ON orders (user_id);
EXPLAIN (ANALYZE, BUFFERS)
SELECT * FROM orders WHERE user_id = 5;
Depois do indice, o Seq Scan vira um Index Scan e Buffers: shared read cai de milhares para poucas.
- MySQL:
EXPLAIN ANALYZE (8.0+) da actual time e loops; nao ha buffers no estilo PostgreSQL, entao olhe Handler_read* e o profiler.
- ClickHouse: em vez de um plano com dados reais voce usa
EXPLAIN para a estrutura e os logs do sistema (query_log) para as metricas de leitura reais.
Um
EXPLAINsimples mostra apenas o plano e as estimativas do planejador.EXPLAIN (ANALYZE, BUFFERS)executa de fato a consulta e relata linhas, tempo e acessos a paginas reais. O desvio entre o estimado e o real costuma ser justamente o que aponta o problema.Valores reais contra estimados
Cada no do plano mostra dois pares de numeros. Os estimados sao
costerows(o que o planejador supos antes de rodar). Os reais saoactual timeerows(o que de fato aconteceu).EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM orders WHERE user_id = 5;Uma linha de no tipica fica assim:
cost=0.00..1934.00e uma estimativa em unidades arbitrarias: inicio e varredura completa.rows=12era esperado,actual ... rows=9e o que realmente voltou.actual time=0.40..18.7sao milissegundos ate a primeira e ate a ultima linha.Leia o plano de baixo para cima e de dentro para fora: os nos folha executam primeiro.
Loops: multiplique pelo numero de passagens
Em um nested loop o no interno roda muitas vezes, e seu
actual time/rowse relatado por passagem. Multiplique porloopspara obter o total.EXPLAIN (ANALYZE, BUFFERS) SELECT u.email, o.amount FROM users u JOIN orders o ON o.user_id = u.id WHERE u.country = 'DE';Se o
Index Scaninterno mostraactual time=0.02 rows=3 loops=5000, nao sao 0.02 ms e sim cerca de 100 ms no total e 15000 linhas. Gotcha: um tempo por passagem minimo com umloopsgrande e a forma mais comum de subestimar um no caro.O desvio entre estimado e real
Uma grande diferenca entre
rowsestimado e real e o sinal principal. O planejador escolhe a estrategia pela estimativa; se a estimativa mente, ele escolhe um plano ruim.ANALYZE orders; -- reconstruir estatisticas EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM orders WHERE status = 'paid' AND created_at >= '2026-01-01';ANALYZE.ANALYZE: provavelmente predicados correlacionados; ajudam estatisticas estendidas (CREATE STATISTICS) ou um indice.BUFFERS: shared hit e read
BUFFERSmostra o trabalho de memoria em cada no.shared hitsao paginas achadas em cache;shared readsao paginas lidas do disco (lento).shared hite um acerto emshared_buffers, barato.shared reade uma falha de cache, uma leitura fisica; muitosreadsignificam um no pesado em I/O.Buffersdiz se a consulta foi lenta por causa do disco ou por causa de um plano ruim.Um Seq Scan que deveria ser Index Scan
O caso pratico principal: uma consulta filtra por uma coluna seletiva, mas voce ve um
Seq Scancomreadenorme e tempo real alto.-- antes: Seq Scan, le a tabela inteira para 9 linhas EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM orders WHERE user_id = 5; CREATE INDEX idx_orders_user ON orders (user_id); -- depois: Index Scan, leitura pontual EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM orders WHERE user_id = 5;Depois do indice, o
Seq Scanvira umIndex ScaneBuffers: shared readcai de milhares para poucas.EXPLAIN ANALYZE(8.0+) daactual timeeloops; nao ha buffers no estilo PostgreSQL, entao olheHandler_read*e o profiler.EXPLAINpara a estrutura e os logs do sistema (query_log) para as metricas de leitura reais.