Réponses:
Explaining_EXPLAIN.pdf pourrait également vous aider.
La partie que j'ai toujours trouvée déroutante est le coût de démarrage par rapport au coût total. Je cherche ça sur Google à chaque fois que j'oublie ça, ce qui me ramène ici, ce qui n'explique pas la différence, c'est pourquoi j'écris cette réponse. C'est ce que j'ai glané dans la documentation PostgresEXPLAIN
, expliqué comme je le comprends.
Voici un exemple d'une application qui gère un forum:
EXPLAIN SELECT * FROM post LIMIT 50;
Limit (cost=0.00..3.39 rows=50 width=422)
-> Seq Scan on post (cost=0.00..15629.12 rows=230412 width=422)
Voici l'explication graphique de PgAdmin:
(Lorsque vous utilisez PgAdmin, vous pouvez pointer votre souris sur un composant pour lire les détails du coût.)
Le coût est représenté par un tuple, par exemple le coût du LIMIT
is cost=0.00..3.39
et le coût du balayage séquentiel post
est cost=0.00..15629.12
. Le premier nombre du tuple est le coût de démarrage et le second le coût total . Parce que j'ai utilisé EXPLAIN
et non EXPLAIN ANALYZE
, ces coûts sont des estimations, pas des mesures réelles.
En guise de complication, les coûts de chaque nœud «parent» comprennent les coûts de ses nœuds enfants. Dans la représentation textuelle, l'arbre est représenté par une indentation, par exemple LIMIT
est un nœud parent etSeq Scan
est son enfant. Dans la représentation PgAdmin, les flèches pointent de l'enfant au parent - la direction du flux de données - ce qui peut être contre-intuitif si vous êtes familier avec la théorie des graphes.
La documentation indique que les coûts incluent tous les nœuds enfants, mais notez que le coût total du parent 3.39
est beaucoup plus petit que le coût total de son enfant 15629.12
. Le coût total n'est pas inclusif car un composant comme celui LIMIT
-ci n'a pas besoin de traiter l'intégralité de son entrée. Voir l' EXPLAIN SELECT * FROM tenk1 WHERE unique1 < 100 AND unique2 > 9000 LIMIT 2;
exemple dans la documentation PostgresEXPLAIN
.
Dans l'exemple ci-dessus, le temps de démarrage est nul pour les deux composants, car aucun des deux composants n'a besoin d'effectuer de traitement avant de commencer à écrire des lignes: une analyse séquentielle lit la première ligne de la table et l'émet. Le LIMIT
lit sa première ligne puis l'émet.
Quand un composant doit-il effectuer beaucoup de traitements avant de pouvoir commencer à produire des lignes? Il y a beaucoup de raisons possibles, mais regardons un exemple clair. Voici la même requête d'avant mais contenant maintenant une ORDER BY
clause:
EXPLAIN SELECT * FROM post ORDER BY body LIMIT 50;
Limit (cost=23283.24..23283.37 rows=50 width=422)
-> Sort (cost=23283.24..23859.27 rows=230412 width=422)
Sort Key: body
-> Seq Scan on post (cost=0.00..15629.12 rows=230412 width=422)
Et graphiquement:
Encore une fois, le balayage séquentiel post
n'a aucun coût de démarrage: il commence immédiatement à produire des lignes. Mais le tri a un coût de démarrage important 23283.24
car il doit trier la table entière avant de pouvoir produire ne serait-ce qu'une seule ligne . Le coût total du tri 23859.27
n'est que légèrement supérieur au coût de démarrage, reflétant le fait qu'une fois l'ensemble de données trié, les données triées peuvent être émises très rapidement.
Notez que l'heure de démarrage de LIMIT
23283.24
est exactement égale à l'heure de démarrage du tri. Ce n'est pas parce que LIMIT
lui - même a un temps de démarrage élevé. Il n'a en fait aucun temps de démarrage en soi, mais EXPLAIN
cumule tous les coûts enfants pour chaque parent, de sorte que leLIMIT
temps de démarrage inclut la somme des temps de démarrage de ses enfants.
Ce cumul des coûts peut rendre difficile la compréhension du coût d'exécution de chaque composant individuel. Par exemple, notre LIMIT
temps de démarrage est nul, mais ce n'est pas évident à première vue. Pour cette raison, plusieurs autres personnes se sont connectées à Expliquer.depesz.com , un outil créé par Hubert Lubaczewski (alias depesz) qui aide à comprendre EXPLAIN
, entre autres, en soustrayant les coûts des enfants des coûts des parents. Il mentionne d'autres complexités dans un court article de blog sur son outil.
Il s'exécute du plus indenté au moins indenté, et je crois du bas du plan vers le haut. (Donc, s'il y a deux sections en retrait, l'une plus en bas de la page s'exécute en premier, puis quand elles se rencontrent, les autres s'exécute, alors la règle qui les rejoint s'exécute.)
L'idée est qu'à chaque étape, il y a 1 ou 2 ensembles de données qui arrivent et sont traités par une règle. S'il s'agit d'un seul ensemble de données, cette opération est effectuée sur cet ensemble de données. (Par exemple, parcourez un index pour déterminer les lignes que vous voulez, filtrez un ensemble de données ou triez-le.) Si deux, les deux ensembles de données sont les deux éléments qui sont plus indentés et ils sont joints par la règle que vous voyez. La signification de la plupart des règles peut être raisonnablement facilement devinée (en particulier si vous avez déjà lu un tas de plans d'explication auparavant), mais vous pouvez essayer de vérifier des éléments individuels soit en regardant dans la documentation ou (plus facilement) en jetant simplement la phrase dans Google avec quelques mots clés comme EXPLAIN
.
Ce n'est évidemment pas une explication complète, mais cela fournit suffisamment de contexte pour que vous puissiez généralement comprendre ce que vous voulez. Par exemple, considérez ce plan à partir d'une base de données réelle:
explain analyze
select a.attributeid, a.attributevalue, b.productid
from orderitemattribute a, orderitem b
where a.orderid = b.orderid
and a.attributeid = 'display-album'
and b.productid = 'ModernBook';
------------------------------------------------------------------------------------------------------------------------------------------------------------
Merge Join (cost=125379.14..125775.12 rows=3311 width=29) (actual time=841.478..841.478 rows=0 loops=1)
Merge Cond: (a.orderid = b.orderid)
-> Sort (cost=109737.32..109881.89 rows=57828 width=23) (actual time=736.163..774.475 rows=16815 loops=1)
Sort Key: a.orderid
Sort Method: quicksort Memory: 1695kB
-> Bitmap Heap Scan on orderitemattribute a (cost=1286.88..105163.27 rows=57828 width=23) (actual time=41.536..612.731 rows=16815 loops=1)
Recheck Cond: ((attributeid)::text = 'display-album'::text)
-> Bitmap Index Scan on (cost=0.00..1272.43 rows=57828 width=0) (actual time=25.033..25.033 rows=16815 loops=1)
Index Cond: ((attributeid)::text = 'display-album'::text)
-> Sort (cost=15641.81..15678.73 rows=14769 width=14) (actual time=14.471..16.898 rows=1109 loops=1)
Sort Key: b.orderid
Sort Method: quicksort Memory: 76kB
-> Bitmap Heap Scan on orderitem b (cost=310.96..14619.03 rows=14769 width=14) (actual time=1.865..8.480 rows=1114 loops=1)
Recheck Cond: ((productid)::text = 'ModernBook'::text)
-> Bitmap Index Scan on id_orderitem_productid (cost=0.00..307.27 rows=14769 width=0) (actual time=1.431..1.431 rows=1114 loops=1)
Index Cond: ((productid)::text = 'ModernBook'::text)
Total runtime: 842.134 ms
(17 rows)
Essayez de le lire par vous-même et voyez si cela a du sens.
Ce que j'ai lu, c'est que la base de données analyse d'abord l' id_orderitem_productid
index, en l'utilisant pour trouver les lignes à partir desquelles elle veut orderitem
, puis trie cet ensemble de données à l'aide d'un tri rapide (le tri utilisé changera si les données ne rentrent pas dans la RAM), puis le met de côté.
Ensuite, il scanne orditematt_attributeid_idx
pour trouver les lignes à partir desquelles il veut orderitemattribute
, puis trie cet ensemble de données à l'aide d'un tri rapide.
Il prend ensuite les deux ensembles de données et les fusionne. (Une jointure par fusion est une sorte d'opération de "compression" dans laquelle elle parcourt les deux ensembles de données triés en parallèle, émettant la ligne jointe lorsqu'ils correspondent.)
Comme je l'ai dit, vous travaillez de la partie intérieure du plan à la partie extérieure, de bas en haut.
Il existe également un outil d'aide en ligne, Depesz , qui mettra en évidence où se trouvent les parties coûteuses des résultats d'analyse.
a également un, voici les mêmes résultats , qui pour moi permettent de mieux comprendre où se trouve le problème.
PgAdmin vous montrera une représentation graphique du plan d'explication. Basculer entre les deux peut vraiment vous aider à comprendre ce que signifie la représentation textuelle. Cependant, si vous voulez juste savoir ce qu'il va faire, vous pourrez peut-être toujours utiliser l'interface graphique.
La documentation officielle de PostgreSQL fournit une explication intéressante et approfondie sur la façon de comprendre la sortie d'Expliquer.