← Alle berichten

12 seconden om 3.000 rijen te scannen, oeps

Twee dingen redden me deze week: EXPLAIN ANALYZE en SET LOCAL enable_nestloop = off. Het eerste is hoe Postgres je laat zien wat de query planner echt deed. Niet wat hij denkt te gaan doen, maar echte uitvoeringstijden, echte aantallen rijen, echte join-strategieën. Het tweede is een override binnen één transactie die de planner vertelt: “je mag voor deze query geen nested loop joins gebruiken.” Je zet het in een BEGIN / COMMIT-blok, het geldt voor die ene uitvoering en lekt niet naar de rest van de verbinding. Het is lelijk. Het is een bot instrument. Het gaf me een 16× snellere query, terwijl drie steeds slimmere SQL-herschrijvingen het elk erger maakten.

Het probleem: de WindowAgg-node van Postgres, die je ROW_NUMBER(), RANK() en SUM() OVER (...) uitvoert, geeft schattingen van het aantal rijen niet door. De planner kijkt naar de uitvoer van de windowfunctie, raakt in paniek en schat rows=1. Zit die windowfunctie in een view die doorloopt naar latere joins, dan werkt die ene slechte schatting door in elke join-beslissing daarna.

De planner kiest nested loops omdat nested loops optimaal zijn voor één rij. Jij hebt geen één rij. Je hebt er duizenden. Elke iteratie scant opnieuw een view van meerdere duizenden rijen. Er ontstaan miljoenen tussenliggende rijen die meteen worden weggegooid. Je query doet er 12 seconden over bij 3.000 bronrijen en je verliest een uur van je leven aan herschrijvingen die allemaal mislukken, omdat de onderliggende schattingsbug elke vorm van query vergiftigt die je probeert. SET LOCAL enable_nestloop = off dwingt hash joins af, en die vliegen in minder dan een seconde door de echte data. Het is een bekende beperking zonder nette omweg op beheerde Postgres. Soms is de pragmatische oplossing de juiste.