← Todos os textos

12 segundos para varrer 3.000 linhas. Opa

Duas coisas me salvaram esta semana: EXPLAIN ANALYZE e SET LOCAL enable_nestloop = off. A primeira é o jeito do Postgres de mostrar o que o planejador de consultas realmente fez, não o que ele acha que vai fazer, mas tempos de execução reais, contagens de linhas reais, estratégias de join reais. A segunda é um override restrito à transação que diz ao planejador “você não pode usar nested loop join nesta consulta”. Você embrulha num bloco BEGIN / COMMIT, vale só para aquela execução e não vaza para mais nada na conexão. É feio. É um instrumento bruto. Me deu um ganho de 16× numa consulta em que três reescritas de SQL, cada vez mais sofisticadas, pioraram tudo.

O problema: o nó WindowAgg do Postgres, que executa seu ROW_NUMBER(), RANK(), SUM() OVER (...), não propaga estimativas de cardinalidade. O planejador avalia a saída da função de janela, entra em pânico e estima rows=1. Se essa função de janela está dentro de uma view que alimenta joins mais adiante, essa única estimativa ruim se espalha por toda decisão de join seguinte.

O planejador escolhe nested loops porque nested loops são ótimos para uma linha. Você não tem uma linha. Você tem milhares. Cada iteração varre de novo uma view de vários milhares de linhas. Milhões de linhas intermediárias são geradas e descartadas na hora. Sua consulta leva 12 segundos em 3.000 linhas de origem e você perde uma hora da vida tentando reescritas que falham, porque o bug de estimativa por baixo envenena qualquer formato de consulta que você jogar nele. SET LOCAL enable_nestloop = off força hash joins, que atravessam os dados reais em menos de um segundo. É uma limitação conhecida, sem contorno limpo em Postgres gerenciado. Às vezes a correção pragmática é a certa.