Il doit néanmoins exécuter la sous-requête pour calculer les classements sur chaque sujet de l’ensemble de la table des messages, peu importe le nombre de classements réellement mis à jour.
Cette sous-requête prend environ 40 secondes à s’exécuter, et elle se trouve à l’intérieur d’une boucle imbriquée qui doit être exécutée pour la jointure avant même d’atteindre la clause WHERE. Cela semble être ce qui fait exploser le coût de la requête.
SELECT id, percent_rank()
OVER (PARTITION BY topic_id ORDER BY SCORE DESC) as percent_rank
FROM posts
J’ai examiné le code et je comprends maintenant. Cette requête est exécutée de manière répétée jusqu’à ce qu’elle ait traité tous les messages dont le rang doit être mis à jour. Ainsi, lorsque je surveillais les processus SQL, j’observais de nombreuses exécutions distinctes de cette requête au fil des heures, au fur et à mesure qu’une exécution se terminait et qu’une autre commençait.
Cependant, il ne semble pas que la clause LIMIT aide vraiment à réduire la complexité de la requête. En fait, j’ai découvert que si j’augmente la limite à 200 000, le plan d’exécution de la requête s’améliore considérablement.
Avec 20 000, j’observe une énorme boucle imbriquée avec un coût proche de 200 millions :
Si j’augmente simplement la limite à 200 000, alors elle produit ce plan d’exécution, qui semble beaucoup plus propre :
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------
Update on posts (cost=7334394.89..8873863.82 rows=200000 width=833)
-> Nested Loop (cost=7334394.89..8873863.82 rows=200000 width=833)
-> Subquery Scan on x (cost=7334394.45..7370147.82 rows=200000 width=48)
-> Limit (cost=7334394.45..7368147.82 rows=200000 width=12)
-> Hash Join (cost=7334394.45..11837188.36 rows=26680560 width=12)
Hash Cond: (posts_1.id = y.id)
Join Filter: ((posts_1.percent_rank IS NULL) OR (y.percent_rank <> posts_1.percent_rank))
-> Hash Join (cost=35403.20..2845221.73 rows=26703700 width=12)
Hash Cond: (posts_1.topic_id = topics.id)
-> Seq Scan on posts posts_1 (cost=0.00..2739721.00 rows=26703700 width=16)
-> Hash (cost=23918.11..23918.11 rows=918807 width=4)
-> Index Only Scan using topics_pkey on topics (cost=0.42..23918.11 rows=918807 width=4)
-> Hash (cost=6834805.00..6834805.00 rows=26703700 width=12)
-> Subquery Scan on y (cost=6033694.00..6834805.00 rows=26703700 width=12)
-> WindowAgg (cost=6033694.00..6567768.00 rows=26703700 width=24)
-> Sort (cost=6033694.00..6100453.25 rows=26703700 width=16)
Sort Key: posts_2.topic_id, posts_2.score DESC
-> Seq Scan on posts posts_2 (cost=0.00..2739721.00 rows=26703700 width=16)
-> Index Scan using posts_pkey on posts (cost=0.44..7.52 rows=1 width=789)
Index Cond: (id = x.id)
JIT:
Functions: 30
Options: Inlining true, Optimization true, Expressions true, Deforming true
(23 rows)
Je me demande ce qui provoque ce changement. Il pourrait être utile d’augmenter cette limite, au moins après une grande importation comme celle que nous avons effectuée, pendant qu’il traite l’arrière-plan.