← Tous les articles

12 secondes pour scanner 3 000 lignes, oups

Deux choses m’ont sauvé cette semaine : EXPLAIN ANALYZE et SET LOCAL enable_nestloop = off. La première, c’est la façon dont Postgres te montre ce que le planificateur de requêtes a réellement fait. Pas ce qu’il pense faire, mais les vrais temps d’exécution, les vrais nombres de lignes, les vraies stratégies de jointure. La seconde est un réglage limité à la transaction qui dit au planificateur « tu n’as pas le droit d’utiliser de jointures par boucles imbriquées pour cette requête ». Tu l’enveloppes dans un bloc BEGIN / COMMIT, il ne s’applique qu’à cette exécution et ne déborde sur rien d’autre dans la connexion. C’est moche. C’est un outil brutal. Il m’a donné une accélération de 16× sur une requête où trois réécritures SQL de plus en plus sophistiquées avaient chacune empiré les choses.

Le problème : le nœud WindowAgg de Postgres, celui qui exécute tes ROW_NUMBER(), RANK(), SUM() OVER (...), ne propage pas les estimations de cardinalité. Le planificateur évalue la sortie de la fonction de fenêtrage, panique, et estime rows=1. Si cette fonction de fenêtrage se trouve dans une vue qui alimente des jointures en aval, cette seule mauvaise estimation se répercute sur chaque décision de jointure suivante.

Le planificateur choisit les boucles imbriquées parce qu’elles sont optimales pour une seule ligne. Tu n’as pas une ligne. Tu en as des milliers. Chaque itération re-scanne une vue de plusieurs milliers de lignes. Des millions de lignes intermédiaires sont générées et jetées aussitôt. Ta requête met 12 secondes pour 3 000 lignes sources et tu perds une heure de ta vie à essayer des réécritures qui échouent toutes, parce que le bug d’estimation sous-jacent empoisonne chaque forme de requête que tu lui lances. SET LOCAL enable_nestloop = off force des jointures par hachage, qui avalent les vraies données en moins d’une seconde. C’est une limitation connue, sans contournement propre sur Postgres managé. Parfois, la correction pragmatique est la bonne.