J'ai passé plus d'une heure aujourd'hui à m'interroger sur un plan de requête que je ne pouvais pas comprendre. La requête était un UDPATE
et elle ne fonctionnerait pas du tout. Totalement dans l'impasse: pg_locks
a montré qu'il n'attendait rien non plus. Maintenant, je ne me considère pas comme le meilleur ou le pire gars lecteur de plan de requête, mais je trouve celui-ci exceptionnellement difficile. Je me demande comment peut-on lire ces? Y a-t-il une méthodologie que les as Pg suivent afin de localiser l'erreur?Comment puis-je "penser mieux" en lisant un plan de requête PostgreSQL? (Exemple ci-joint)
Je prévois de poser une autre question sur la façon de contourner ce problème, mais en ce moment je parle spécifiquement sur comment lire ces types de plans. Veuillez ne pas pointer vers un didacticiel générique à moins qu'il ne traite spécifiquement ce problème, mis en évidence ci-dessous le plan de requête.
QUERY PLAN
--------------------------------------------------------------------------------------------
Nested Loop Anti Join (cost=47680.88..169413.12 rows=1 width=77)
Join Filter: ((co.fkey_style = v.chrome_styleid) AND (co.name = o.name))
-> Nested Loop (cost=5301.58..31738.10 rows=1 width=81)
-> Hash Join (cost=5301.58..29722.32 rows=229 width=40)
Hash Cond: ((io.lot_id = iv.lot_id) AND ((io.vin)::text = (iv.vin)::text))
-> Seq Scan on options io (cost=0.00..20223.32 rows=23004 width=36)
Filter: (name IS NULL)
-> Hash (cost=4547.33..4547.33 rows=36150 width=24)
-> Seq Scan on vehicles iv (cost=0.00..4547.33 rows=36150 width=24)
Filter: (date_sold IS NULL)
-> Index Scan using options_pkey on options co (cost=0.00..8.79 rows=1 width=49)
Index Cond: ((co.fkey_style = iv.chrome_styleid) AND (co.code = io.code))
-> Hash Join (cost=42379.30..137424.09 rows=16729 width=26)
Hash Cond: ((v.lot_id = o.lot_id) AND ((v.vin)::text = (o.vin)::text))
-> Seq Scan on vehicles v (cost=0.00..4547.33 rows=65233 width=24)
-> Hash (cost=20223.32..20223.32 rows=931332 width=44)
-> Seq Scan on options o (cost=0.00..20223.32 rows=931332 width=44)
(17 rows)
Le problème avec ce plan de requête - je crois que je comprends - est probablement mieux dit par RhodiumToad
(il est certainement mieux à cela, donc je vais parier sur son explication étant mieux) de irc://irc.freenode.net/#postgresql
:
oh, ce plan est potentiellement disasterous le problème avec ce plan est qu'il est en cours d'exécution d'un hashjoin extrêmement coûteux pour chaque ligne le problème est rows = 1 estimation de l'autre rejoindre et le planificateur pense qu'il est autorisé à mettre un énorme demande coûteuse dans le chemin interne d'un nestloop où le chemin externe est estimé pour retourner une seule ligne. puisque, évidemment, selon l'estimation du planificateur, la partie la plus chère ne sera exécutée qu'une fois, mais cela a une tendance évidente à vraiment gâcher en pratique le problème est que le planificateur estime ses propres estimations idéalement, le planificateur doit connaître le différence entre « estimée à revenir 1 rang » et « pas possible de revenir plus de 1 ligne » mais pas du tout clair comment incorporer dans le code existant
il poursuit en disant:
il peut affecter n'importe quelle jointure, mais se joint généralement contre les sous-requêtes sont les plus susceptibles
Maintenant, quand je lis ce plan, la première chose que j'ai remarqué était le Nested Loop Anti Join
, cela a eu un coût de 169,413
(je vais en tenir à des limites supérieures). Cet anti-joint se décompose en résultat Nested Loop
au coût de 31,738
, et le résultat d'un Hash Join
au coût de 137,424
. Maintenant, le 137,424
, est beaucoup plus grand que 31,738
donc je savais que le problème était le Hash Join. Ensuite, je passe à EXPLAIN ANALYZE
le segment de jointure de hachage en dehors de la requête. Il a exécuté en 7 secondes. Je me suis assuré qu'il y avait des index sur (lot_id, vin), et (co.code, et v.code) - il y avait. J'ai désactivé seq_scan
et hashjoin
individuellement et remarquez une augmentation de la vitesse de moins de 2 secondes. Pas assez proche pour expliquer pourquoi il ne progressait pas après une heure.
Mais, après tout cela, je me trompe totalement! Oui, c'était la partie la plus lente de la requête, mais parce que le bit rows="1"
(je présume qu'il était sur le Nested Loop Anti Join
). Y a-t-il un tutoriel qui m'aidera à identifier ces types de problèmes? Ici, c'est un bug (manque de capacité) dans le planificateur d'estimation erronée du nombre de lignes?Comment suis-je censé lire ceci pour arriver à la même conclusion? RhodiumToad
fait?
Est-ce simplement rows="1"
qui est censé me déclencher ceci comprendre?
J'ai couru VACUUM FULL ANALYZE
sur toutes les tables impliquées, et c'est Postgresql 8.4.
C'est une bonne réponse, mais de laquelle parlez-vous lorsque vous dites "se joindre"? Je crois que "RhodiumToads" explication du problème, car il semble exact? Est-ce que vous expliquez la même chose ou quelque chose de différent? –
Même chose. Rejoindre 'co' est le' Index Scan en utilisant options_pkey sur le nœud interne des options co' de la jointure de la boucle imbriquée. Il a deux conditions que le planificateur pense probablement irréaliste se traduira par une ligne de sortie.Si vous essayez d'exécuter cette requête et voyez combien de lignes elle retourne vraiment, vous pouvez vérifier si c'est le cas. Les mauvaises estimations pour les prédicats corrélés sont un problème connu. Il y a quelques discussions à ce sujet sur la liste des performances: http://archives.postgresql.org/pgsql-performance/2009-06/msg00055.php –