sqlpostgresqlexplainperformance

EXPLAIN (ANALYZE, BUFFERS) no PostgreSQL: lendo o plano real da consulta

Como ler EXPLAIN (ANALYZE, BUFFERS): linhas e tempo reais contra estimados, loops, e o desvio que denuncia estatisticas obsoletas ou indice ausente.

2 min de leituraReferencesql · postgresql · explain · performance · index

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;  -- reconstruir estatisticas

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.

-- 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 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.

Pratique com exercícios reais

Resolva exercícios no treinador de SQL com correção instantânea e dicas.

Abrir o treinador