Hoje vamos entender o que são os nós do plano de execução do PostgreSQL, como ler eles e, o mais importante, como sacar quando algo tá estranho. Você vai descobrir porque seu banco às vezes prefere usar o Seq Scan (que gasta mais recurso), mesmo quando já tem índice criado, ou até mais de um.
Quando o PostgreSQL monta o plano de execução de uma query, ele quebra tudo em etapas chamadas de nós. Cada nó é tipo um "passo" que o servidor do banco executa pra processar sua query. Os principais tipos de nós são:
Sequential Scan (Seq Scan)
Seq Scan ou varredura sequencial é o jeito mais simples de buscar dados de uma tabela. O PostgreSQL literalmente pega a tabela, lê linha por linha e checa se bate com as condições da sua query.
Quando rola um Seq Scan?
Seq Scan aparece se:
- Não tem índice que ajude a acelerar a query na tabela.
- A condição do filtro é muito genérica pra um índice ser útil (tipo, buscar mais de 50% dos dados).
- O PostgreSQL acha que ler a tabela inteira é mais rápido do que usar o índice (às vezes rola em tabelas bem pequenas).
EXPLAIN SELECT * FROM students WHERE age > 18;
Exemplo de resultado:
Seq Scan on students (cost=0.00..35.50 rows=10 width=50)
Filter: (age > 18)
Repara no Seq Scan on students — é o PostgreSQL avisando que vai ler a tabela "students" inteira.
Problemas com Seq Scan: Se a tabela for gigante, a varredura sequencial pode demorar pra caramba.
Index Scan
Index Scan é a busca usando um índice. Quando você cria um índice no PostgreSQL, é tipo fazer um "sumário" pra sua tabela. Se a query pode usar esse índice, o PostgreSQL não lê tudo, só as partes que precisa.
Quando rola um Index Scan?
- A query tem filtro em coluna que tem índice (tipo
WHERE). - Usa operações tipo
=,<,>,BETWEENe por aí vai.
CREATE INDEX idx_students_age ON students(age);
EXPLAIN SELECT * FROM students WHERE age = 18;
Exemplo de resultado:
Index Scan using idx_students_age on students (cost=0.15..8.27 rows=1 width=50)
Index Cond: (age = 18)
Aqui o Index Scan using idx_students_age mostra que o PostgreSQL tá usando o índice idx_students_age. Ao invés de ler linha por linha, ele vai direto no que interessa pelo índice.
Vantagens do Index Scan:
- Query fica muito mais rápida em tabelas grandes.
- Lê bem menos dados do disco.
Problemas do Index Scan:
Se sua query retorna dados demais (tipo, mais da metade da tabela), usar índice pode ser até mais lento que Seq Scan.
Hash Join
Hash Join é usado pra juntar duas tabelas baseado numa condição de join (tipo ON students.course_id = courses.id). O PostgreSQL monta uma hash table pra uma das tabelas (a menor) e usa ela pra achar os matches na outra tabela.
Quando rola um Hash Join?
- Quando faz join de tabelas com
INNER JOIN,LEFT JOINe afins. - Quando o PostgreSQL acha que
Hash Joiné mais eficiente que outros jeitos de juntar.
EXPLAIN
SELECT *
FROM students
JOIN courses ON students.course_id = courses.id;
Exemplo de resultado:
Hash Join (cost=25.00..50.00 rows=10 width=100)
Hash Cond: (students.course_id = courses.id)
-> Seq Scan on students (cost=0.00..20.00 rows=10 width=50)
-> Hash (cost=15.00..15.00 rows=10 width=50)
-> Seq Scan on courses (cost=0.00..15.00 rows=10 width=50)
Aqui o Hash Join junta as duas tabelas. Repara que o PostgreSQL faz Seq Scan nas duas tabelas primeiro, depois monta a hash table (Hash).
Vantagens do Hash Join:
- Rápido pra tabelas de tamanho médio.
- Funciona bem pra juntar tabelas com bastante linha.
Problemas do Hash Join:
Se a hash table ficar maior que a memória disponível, o PostgreSQL vai usar o disco pra guardar ela, e aí o join fica bem mais lento.
Exemplo de análise de plano de execução
Bora ver um exemplo real.
Query:
EXPLAIN ANALYZE
SELECT *
FROM students
JOIN courses ON students.course_id = courses.id
WHERE students.age > 18;
Resultado:
Hash Join (cost=35.00..75.00 rows=5 width=100) (actual time=1.00..2.50 rows=5 loops=1)
Hash Cond: (students.course_id = courses.id)
-> Seq Scan on students (cost=0.00..40.00 rows=10 width=50) (actual time=0.50..1.00 rows=7 loops=1)
Filter: (age > 18)
Rows Removed by Filter: 3
-> Hash (cost=25.00..25.00 rows=5 width=50) (actual time=0.30..0.30 rows=5 loops=1)
-> Seq Scan on courses (cost=0.00..20.00 rows=5 width=50) (actual time=0.20..0.25 rows=5 loops=1)
Planning Time: 0.50 ms
Execution Time: 3.00 ms
Interpretação:
Hash Join: Nó principal. O PostgreSQL tá juntando as tabelasstudentsecourses.actual time: de 1.00 até 2.50 ms.rows=5: a query retornou 5 linhas.
- Nós internos:
Seq Scan on students: lê a tabelastudentssequencialmente e aplica o filtro(age > 18).Rows Removed by Filter = 3: 3 linhas não passaram no filtro.Hash: o PostgreSQL cria a hash table pra tabelacourses.
Comparando e escolhendo nós
Quando você analisa o plano de execução, o segredo é entender porque o PostgreSQL escolheu aquele jeito de processar os dados. Às vezes você precisa dar uma ajudinha, tipo criar um índice ou reescrever a query. Algumas dicas:
- Se ver
Seq Scannuma tabela grande, pensa em criar índices. - Se o
Hash Jointá lento, confere a memória disponível pro PostgreSQL. - Usa
EXPLAIN ANALYZEpra comparar os valores estimados e reais das métricas (rows,time).
Nessa altura você já tem uma noção básica de como ler planos de execução de queries e interpretar os nós. Nas próximas aulas a gente vai falar dos problemas comuns de otimização e como resolver eles.
GO TO FULL VERSION