CodeGym /Cursos /SQL SELF /Interpretando o plano de execução: lendo e analisando nós...

Interpretando o plano de execução: lendo e analisando nós ( Seq Scan, Index Scan, Hash Join)

SQL SELF
Nível 41 , Lição 4
Disponível

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 =, <, >, BETWEEN e 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 JOIN e 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:

  1. Hash Join: Nó principal. O PostgreSQL tá juntando as tabelas students e courses.
    • actual time: de 1.00 até 2.50 ms.
    • rows=5: a query retornou 5 linhas.
  2. Nós internos:
    • Seq Scan on students: lê a tabela students sequencialmente 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 tabela courses.

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 Scan numa tabela grande, pensa em criar índices.
  • Se o Hash Join tá lento, confere a memória disponível pro PostgreSQL.
  • Usa EXPLAIN ANALYZE pra 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.

1
Pesquisa/teste
Plano de execução da query, nível 41, lição 4
Indisponível
Plano de execução da query
Plano de execução da query
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION