Tecnologia

Performance: Entendendo o EXPLAIN — o raio-X da sua query no PostgreSQL

Quando uma consulta está lenta, no Excel você torce e espera. No PostgreSQL, você abre o EXPLAIN — e descobre exatamente onde está o gargalo.

A dor real: "está lento, mas não sei por quê"

Toda empresa que cresce, em algum momento, vive isso. Aquela consulta no sistema que era rápida começa a demorar. Primeiro, 1 segundo. Depois, 3. Quando você percebe, virou 12 segundos pra abrir o relatório de vendas do mês — e a equipe comercial começa a reclamar.

No Excel, você não tem muita escolha. Espera carregar, torce pra dar certo, talvez quebra a planilha em pedaços menores. Diagnóstico fica em "está lento" — sem entender o porquê.

No banco de dados, é diferente. Você tem uma ferramenta que mostra exatamente o caminho que o banco está fazendo, quanto tempo demora cada etapa, e onde está o gargalo real. Esse comando se chama EXPLAIN.


O que é EXPLAIN

EXPLAIN é um comando do PostgreSQL (e de praticamente todo banco relacional sério) que pede ao banco pra mostrar o plano de execução de uma consulta. Em vez de rodar a consulta e te dar o resultado, ele te mostra como ele rodaria.

Uso básico — basta colocar EXPLAIN antes do SELECT:

EXPLAIN
SELECT cliente, SUM(valor)
FROM pedidos
WHERE data >= '2026-01-01'
GROUP BY cliente;

O banco devolve algo assim:

HashAggregate  (cost=1234.56..1456.78 rows=200 width=40)
  Group Key: cliente
  ->  Seq Scan on pedidos  (cost=0.00..1000.00 rows=10000 width=40)
        Filter: (data >= '2026-01-01')

Parece grego. Mas em 5 minutos de leitura vira informação cristalina.


EXPLAIN vs EXPLAIN ANALYZE — a diferença que importa

ComandoO que faz
EXPLAINMostra o plano estimado. Não roda a query.
EXPLAIN ANALYZEMostra o plano + roda a query de verdade + tempo real medido.

Na prática, você sempre quer EXPLAIN ANALYZE quando está investigando lentidão real. O EXPLAIN puro é estimativa (cost calculado por heurística). O EXPLAIN ANALYZE traz números reais (actual time).

EXPLAIN ANALYZE
SELECT cliente, SUM(valor)
FROM pedidos
WHERE data >= '2026-01-01'
GROUP BY cliente;

Cuidado: EXPLAIN ANALYZE executa a query. Em produção, evite com UPDATE/DELETE/INSERT — vai aplicar a mudança. Pra investigação segura, envolva em transação:

BEGIN;
EXPLAIN ANALYZE UPDATE pedidos SET status = 'pago' WHERE id = 123;
ROLLBACK;

O ROLLBACK desfaz tudo após a análise.


Como ler o output — as 4 informações que importam

Cada linha do plano é uma operação. Olhe de baixo pra cima (o banco executa de dentro pra fora — o nó mais interno é o primeiro).

1. Tipo de operação

OperaçãoO que éBom ou ruim?
Seq ScanLeu a tabela inteira, linha por linha🟡 Ok em tabela pequena, ruim em tabela grande
Index ScanUsou índice pra ir direto nas linhas certas🟢 Geralmente ótimo
Bitmap Index ScanMisto — usa índice mas com ajuste🟢 Bom pra filtros que retornam muitos registros
Index Only ScanResposta veio só do índice, sem ir na tabela🟢🟢 Excelente
Nested LoopPra cada linha da tabela A, percorre tabela B🟡 Ok com poucas linhas, péssimo em massa
Hash JoinConstruiu hash em memória pra combinar tabelas🟢 Geralmente bom
SortOrdenou em memória ou disco🟡 Disco = ruim, memória = ok

2. Cost (custo estimado)

(cost=0.00..1000.00 rows=10000 width=40)

Cost não tem unidade real. É uma escala interna do PostgreSQL. Compare cost antes e depois da otimização — você quer ver o número cair.

3. Actual time (só com EXPLAIN ANALYZE)

(actual time=2.345..1234.567 rows=10000 loops=1)

4. Diferença entre estimado e real

estimated rows=10000  ↔  actual rows=2 000 000

Se o estimado é muito diferente do real, o banco está com estatísticas desatualizadas. Solução: rode ANALYZE nome_da_tabela; pra atualizar.


Caso real — antes e depois do índice

Imagine que essa consulta está demorando 8 segundos:

SELECT * FROM despesas
WHERE vendedor_id = 42 AND data >= '2026-01-01';

Antes do índice (EXPLAIN ANALYZE)

Seq Scan on despesas  (cost=0.00..18500.00 rows=300 width=80)
                      (actual time=12.456..8245.789 rows=287 loops=1)
  Filter: ((vendedor_id = 42) AND (data >= '2026-01-01'))
  Rows Removed by Filter: 765432
Planning Time: 0.234 ms
Execution Time: 8246.123 ms

Leitura:

Solução: criar índice composto

CREATE INDEX idx_despesas_vendedor_data
ON despesas (vendedor_id, data);

Depois do índice

Index Scan using idx_despesas_vendedor_data on despesas
                      (cost=0.42..125.50 rows=287 width=80)
                      (actual time=0.123..3.456 rows=287 loops=1)
  Index Cond: ((vendedor_id = 42) AND (data >= '2026-01-01'))
Planning Time: 0.456 ms
Execution Time: 4.123 ms

Leitura:

Esse é o tipo de ganho que muda a percepção do sistema da equipe.


Quando criar um índice — sinais óbvios

Use EXPLAIN ANALYZE e procure por:

E não crie índice quando:


3 pegadinhas que enganam até quem já usa EXPLAIN

1. Cache mascara o problema

A primeira execução de uma query lê do disco (lenta). A segunda já lê do cache em memória (rápida). Pra medir de verdade, rode 3-4 vezes e considere as últimas — não a primeira.

2. Dataset pequeno em dev, grande em produção

Sua máquina de desenvolvimento tem 1.000 pedidos. Produção tem 5 milhões. O plano que o banco escolhe muda com o tamanho dos dados. Uma query que parece rápida no dev pode virar um Seq Scan brutal em produção.

Solução: teste sempre contra um snapshot recente da produção (ou um dataset com pelo menos a mesma ordem de grandeza).

3. ANALYZE travada

Se o EXPLAIN mostra rows=1000 mas você sabe que a tabela tem 1 milhão, as estatísticas estão velhas. Rode:

ANALYZE nome_da_tabela;

Pra coletar estatísticas atualizadas. Sem isso, o PostgreSQL pode tomar decisão errada baseada em dado defasado.


Ferramentas que ajudam — além do terminal

EXPLAIN puro é texto. Pra queries complexas, ler árvore aninhada de 30 linhas vira tortura. Use:

explain.dalibo.com

pgAdmin (GUI do PostgreSQL)

pg_stat_statements (extensão do PostgreSQL)


Por que isso importa pro negócio

Quando você não tem EXPLAIN:

Quando você usa EXPLAIN:

A diferença entre "achar que melhorou" e ter controle real do sistema está em saber ler 20 linhas de texto. É um dos melhores investimentos de tempo que um analista ou desenvolvedor pode fazer.


Resumo prático — sua próxima consulta lenta

  1. Identifique a query que está lenta
  2. Rode com EXPLAIN ANALYZE na frente
  3. Procure: tem Seq Scan em tabela grande? Rows Removed by Filter alto? Sort em disco?
  4. Identifique a coluna do filtro/join sem índice
  5. Crie o índice apropriado:
    CREATE INDEX nome_descritivo ON tabela (coluna);
  6. Rode EXPLAIN ANALYZE de novo
  7. Compare o tempo. Se caiu = sucesso. Se não caiu = índice errado, tente outro

Em 90% dos casos de "está lento", o problema é índice faltando ou mal escolhido. EXPLAIN te dá o mapa pra encontrar.


Fechamento

Dominar EXPLAIN significa sair do "funciona mais ou menos" pra ter controle real sobre seus dados. E quando você tem controle sobre dados, tem controle sobre decisões — que é onde mora o resultado real do negócio.

O Excel te ensina a fazer cálculo. O SQL te ensina a entender o cálculo. O EXPLAIN te ensina a explicar por que o cálculo demora — e como tirar o gargalo.

Próximo passo: pega uma query lenta do seu sistema hoje e roda EXPLAIN ANALYZE nela. Em 10 minutos você vai entender mais sobre performance do que muita gente entende em 10 anos.

#banco-de-dados #desenvolvimento #otimizacoes #PostgreSQL #SQL
Gostou? Compartilhe
F

Gostou do conteúdo da Fabi?

Ela publica 1 insight por dia aqui no blog da Excelmax. Siga junto e não perca nada.

Ver mais artigos

Outros posts da Fabi

PostgreSQL pra quem veio do Excel: 7 conceitos que mudam t…

4 min de leitura · 11 Mai, 2026

Django em 2026: por que ainda vale aprender este framework…

3 min de leitura · 17 Abr, 2026

O que mudou no NEXUS: 7 otimizacoes que impactam a operacao

2 min de leitura · 13 Abr, 2026