EXPLAIN — Afficher le plan d'exécution d'une instruction
EXPLAIN [ (option
[, ...] ) ]instruction
oùoption
est : ANALYZE [boolean
] VERBOSE [boolean
] COSTS [boolean
] SETTINGS [boolean
] GENERIC_PLAN [boolean
] BUFFERS [boolean
] SERIALIZE [ { NONE | TEXT | BINARY } ] WAL [boolean
] TIMING [boolean
] SUMMARY [boolean
] MEMORY [boolean
] FORMAT { TEXT | XML | JSON | YAML }
Cette commande affiche le plan d'exécution que l'optimiseur de PostgreSQL engendre pour l'instruction fournie. Le plan d'exécution décrit le parcours de la (des) table(s) utilisée(s) dans la requête -- parcours séquentiel, parcours d'index, etc. -- . Si plusieurs tables sont référencées, il présente également les algorithmes de jointures utilisés pour rassembler les lignes issues des différentes tables.
La partie la plus importante de l'affichage concerne l'affichage des coûts
estimés d'exécution. Ils représentent l'estimation faite par le
planificateur des temps d'exécution de la requête (mesuré en une unité de
coût arbitraire bien que conventionnellement ce sont des lectures de page
disque). Deux nombres sont affichés : le coût de démarrage, écoulé
avant que la première ligne soit renvoyée, et le coût d'exécution total,
nécessaire au renvoi de toutes les lignes. Pour la plupart des requêtes, le
coût qui importe est celui d'exécution totale. Mais dans certains cas, tel
que pour une sous-requête dans la clause EXISTS
, le
planificateur choisira le coût de démarrage le plus court, et non celui
d'exécution totale (car, de toute façon, l'exécuteur s'arrête après la
récupération d'une ligne). De même, lors de la limitation des résultats à
retourner par une clause LIMIT
, la planificateur
effectue une interpolation entre les deux coûts limites pour choisir le
plan réellement le moins coûteux.
L'option ANALYZE
impose l'exécution de la requête en
plus de sa planification. De ce fait, les statistiques d'exécution réelle
sont ajoutées à l'affichage, en incluant le temps total écoulé à chaque
nœud du plan (en millisecondes) et le nombre total de lignes renvoyées.
C'est utile pour vérifier la véracité des informations fournies par le
planificateur.
Il ne faut pas oublier que l'instruction est réellement exécutée avec
l'option ANALYZE
. Bien qu'EXPLAIN
inhibe l'affichage des retours d'une commande SELECT
,
les autres effets de l'instruction sont présents. Si EXPLAIN
ANALYZE
doit être utilisé sur une instruction
INSERT
, UPDATE
,
DELETE
, MERGE
, CREATE TABLE AS
ou
EXECUTE
sans que la commande n'affecte les données,
l'approche suivante peut être envisagée :
BEGIN; EXPLAIN ANALYZE ...; ROLLBACK;
ANALYZE
Exécute la commande et affiche les temps d'exécution réels et d'autres
statistiques. Ce paramètre est par défaut à FALSE
.
VERBOSE
Affiche des informations supplémentaires sur le plan. Cela inclut la
liste des colonnes en sortie pour chaque nœud du plan, les noms
des tables et fonctions avec le nom du schéma, les labels des variables
dans les expressions avec des alias de tables et le nom de chaque
trigger pour lesquels les statistiques sont affichées. L'identifiant de
requête sera aussi affiché si une requête a été évaluée, voir compute_query_id pour plus de détails. Ce paramètre est
par défaut à FALSE
.
COSTS
Inclut des informations sur le coût estimé au démarrage et au total de
chaque nœud du plan, ainsi que le nombre estimé de lignes et la
largeur estimée de chaque ligne. Ce paramètre est par défaut à
TRUE
.
GENERIC_PLAN
Autorise la requête à contenir des caractères joker de placement
comme $1
, et génère un plan générique qui ne dépend
pas de ces paramètres.
Voir PREPARE
pour les détails sur les plans génériques et les types de requêtes qui
acceptent des paramètres.
Ce paramètre ne peut pas être utilisé en même temps que
ANALYZE
. Il est à FALSE
par
défaut.
BUFFERS
Inclut des informations sur l'utilisation des tampons. Spécifiquement,
inclut le nombre de blocs partagés lus dans la cache, lus en dehors du
cache, modifiés et écrits, le nombre de blocs locaux lus dans le cache,
lus en dehors du cache, modifiés, et écrits, et le nombre de blocs
temporaires lus et écrits, et le temps passé à lire et écrire des blocs
dans les fichiers de données, dans les blocs locaux et dans les fichiers
temporaires (en millisecondes) si le paramètre track_io_timing est activé. Le terme
hit signifie que la lecture a
été évitée car le bloc se trouvait déjà dans le cache.
Les blocs partagés contiennent les données de tables et index
standards ; les blocs locaux contiennent les tables et index
temporaires ; les blocs temporaires contiennent les données de
travail à court terme, comme les tris, les hachages, les nœuds
Materialize, et des cas similaires. Le nombre de blocs modifiés
(dirtied) indique le nombre de blocs précédemment
propres qui ont été modifiés par cette requête ; le nombre de
blocs écrits (written) indique le nombre de blocs
déjà modifiés qui a été enlevé du cache pour être écrit sur disque lors
de l'exécution de cette requête. Le nombre de blocs affichés pour un
nœud de niveau supérieur inclut ceux utilisés par tous ses enfants. Dans
le format texte, seules les valeurs différentes de zéro sont affichées.
La valeur par défaut de ce paramètre est FALSE
.
SETTINGS
Inclut des informations sur les paramètres de configuration. Plus
précisément, inclut les options affectant l'optimisation des requêtes et
qui ont des valeurs différentes de leur valeur par défaut. Sa valeur par
défaut est FALSE
.
SERIALIZE
Inclut des informations sur le coût de
sérialisation des données en sortie de la
requête, autrement dit la conversion en format texte ou binaire à
envoyer au client. Ceci peut être une part significative du temps
nécessaire pour l'exécution régulière d'une requête, si les fonctions
en sortie sont coûteuses ou si des valeurs duTOAST
doivent être récupérées. Le comportement par défaut de
EXPLAIN
, SERIALIZE NONE
, ne
réalise pas ces conversions. Si SERIALIZE TEXT
ou SERIALIZE BINARY
est indiqué, les conversions
appropriées sont réalisées et le temps passé à le faire est mesuré
(sauf si TIMING OFF
est indiqué). Si l'option
BUFFERS
est aussi indiqué, alors tout accès au
cache impliqué dans les conversions est aussi compté. Cependant,
en aucun cas, EXPLAIN
n'enverra les données
résultantes au client ; de ce fait, les coûts de transmission sur le
réseau ne peuvent pas être investigués de cette façon. La sérialisation
peut seulement être activée quand ANALYZE
est aussi
activé. Si SERIALIZE
est écrit sans argument,
TEXT
est la valeur utilisée.
WAL
Inclut des informations sur les génération des enregistrements dans les
WAL. Spécifiquement, inclut le nombre d'enregistrements, le nombre
d'images complète de bloc (FPI), et la quantité d'octets générés dans
les WAL. Dans le format texte, seules les valeurs différentes de zéro
seront affichées. Ce paramètre peut seulement aussi être utilisé quand
ANALYZE
est aussi activé. Il vaut par défaut
FALSE
.
TIMING
Inclut le temps réel de démarrage et le temps réel passé dans le nœud en
sortie. La surcharge de la lecture répétée de l'horloge système peut
ralentir la requête de façon significative sur certains systèmes, et
donc il est utile de pouvoir configurer ce paramètre à
FALSE
quand seuls le décompte réel des lignes est
nécessaire. La durée d'exécution complète de la commande est toujours
mesurée, même si le chronométrage des nœuds est désactivé avec cette
option. Ce paramètre peut seulement être utilisé quand l'option
ANALYZE
est aussi activée. La valeur par défaut est
TRUE
.
SUMMARY
Inclut des informations résumées (par exemple : information de temps
total) après le plan de la requête. Les informations résumées sont
inclues par défaut quand ANALYZE
est utilisé mais
sinon ne sont pas inclues par défaut, mais peuvent être activées avec
cette option. Le temps de planification dans EXPLAIN
EXECUTE
inclut le temps nécessaire pour récupérer le plan du
cache ainsi que le temps nécessaire pour le replanifier, si nécessaire.
MEMORY
Inclut des informations sur la consommation mémoire par la phase
d'optimisation de la requête. Plus spécifiquement, inclut la quantité
précise de stockage utilisé par l'optimiseur pour les structures en
mémoire, ainsi qu'un total de mémoire. Ce paramètre vaut par défaut
FALSE
.
FORMAT
Indique le format de sortie. Il peut valoir TEXT, XML, JSON ou YAML.
Toutes les sorties contiennent les mêmes informations, mais les
programmes pourront plus facilement traiter les sorties autres que TEXT.
Ce paramètre est par défaut à TEXT
.
boolean
Spécifie si l'option sélectionnée doit être activée ou désactivée. Vous
pouvez écrire TRUE
, ON
ou
1
pour activer l'option, et FALSE
,
OFF
ou 0
pour la désactiver. La
valeur de type boolean
peut
aussi être omise, auquel cas la valeur sera TRUE
.
instruction
Toute instruction SELECT
, INSERT
,
UPDATE
, DELETE
,
MERGE
,
VALUES
, EXECUTE
,
DECLARE
, CREATE TABLE AS
ou
CREATE MATERIALIZED VIEW AS
dont le plan d'exécution
est souhaité.
La sortie de la commande est une description textuelle du plan sélectionné
pour la requête
, annotée en
option des statistiques d'exécution. Section 14.1 décrit
les informations fournies.
Pour permettre au planificateur de requêtes de
PostgreSQL de prendre des décisions en étant
raisonnablement informé pour l'optimisation des requêtes, les données du
catalogue pg_statistic
doivent être à jour pour toutes les tables utilisées dans la requête.
Habituellement, le démon autovacuum s'en
chargera automatiquement. Mais si une table a eu récemment des changements
importants dans son contenu, vous pourriez avoir besoin de lancer un
ANALYZE
manuel plutôt
que d'attendre que l'autovacuum s'occupe des modifications.
Pour mesurer le coût d'exécution de chaque nœud dans le plan d'exécution,
l'implémentation actuelle de la commande EXPLAIN ANALYZE
ajoute une surcharge de profilage à l'exécution de la requête. En résultat,
exécuter EXPLAIN ANALYZE
sur une requête peut parfois
prendre un temps significativement plus long que l'exécution de la requête.
La durée supplémentaire dépend de la nature de la requête ainsi que de la
plateforme utilisée. Le pire des cas survient pour les nœuds du plan
nécessitant en eux-même peu de durée d'exécution par exécution et sur les
machines disposant d'appels systèmes relativement lents pour obtenir
l'heure du jour.
Afficher le plan d'une requête simple sur une table d'une seule colonne de
type integer
et 10000 lignes :
EXPLAIN SELECT * FROM foo; QUERY PLAN --------------------------------------------------------- Seq Scan on foo (cost=0.00..155.00 rows=10000 width=4) (1 row)
Voici le même plan, mais formaté avec JSON :
EXPLAIN (FORMAT JSON) SELECT * FROM foo; QUERY PLAN -------------------------------- [ + { + "Plan": { + "Node Type": "Seq Scan",+ "Relation Name": "foo", + "Alias": "foo", + "Startup Cost": 0.00, + "Total Cost": 155.00, + "Plan Rows": 10000, + "Plan Width": 4 + } + } + ] (1 row)
S'il existe un index et que la requête contient une condition
WHERE
indexable, EXPLAIN
peut
afficher un plan différent :
EXPLAIN SELECT * FROM foo WHERE i = 4; QUERY PLAN -------------------------------------------------------------- Index Scan using fi on foo (cost=0.00..5.98 rows=1 width=4) Index Cond: (i = 4) (2 rows)
Voici le même plan, mais formaté avec YAML :
EXPLAIN (FORMAT YAML) SELECT * FROM foo WHERE i='4'; QUERY PLAN ------------------------------- - Plan: + Node Type: "Index Scan" + Scan Direction: "Forward"+ Index Name: "fi" + Relation Name: "foo" + Alias: "foo" + Startup Cost: 0.00 + Total Cost: 5.98 + Plan Rows: 1 + Plan Width: 4 + Index Cond: "(i = 4)" (1 row)
L'obtention du format XML est laissé en exercice au lecteur.
Voici le même plan avec les coûts supprimés :
EXPLAIN (COSTS FALSE) SELECT * FROM foo WHERE i = 4; QUERY PLAN ---------------------------- Index Scan using fi on foo Index Cond: (i = 4) (2 rows)
Exemple de plan de requête pour une requête utilisant une fonction d'agrégat :
EXPLAIN SELECT sum(i) FROM foo WHERE i < 10; QUERY PLAN --------------------------------------------------------------------- Aggregate (cost=23.93..23.93 rows=1 width=4) -> Index Scan using fi on foo (cost=0.00..23.92 rows=6 width=4) Index Cond: (i < 10) (3 rows)
Exemple d'utilisation de EXPLAIN EXECUTE
pour afficher
le plan d'exécution d'une requête préparée :
PREPARE query(int, int) AS SELECT sum(bar) FROM test WHERE id > $1 AND id < $2 GROUP BY foo; EXPLAIN ANALYZE EXECUTE query(100, 200); QUERY PLAN ------------------------------------------------------------------------------------- HashAggregate (cost=10.77..10.87 rows=10 width=12) (actual time=0.043..0.044 rows=10 loops=1) Group Key: foo Batches: 1 Memory Usage: 24kB -> Index Scan using test_pkey on test (cost=0.29..10.27 rows=99 width=8) (actual time=0.009..0.025 rows=99 loops=1) Index Cond: ((id > 100) AND (id < 200)) Planning Time: 0.244 ms Execution Time: 0.073 ms (7 rows)
Il est évident que les nombres présentés ici dépendent du contenu effectif
des tables impliquées. De plus, les nombres, et la stratégie sélectionnée
elle-même, peuvent différer en fonction de la version de
PostgreSQL du fait des améliorations apportées
au planificateur. Il faut également savoir que la commande
ANALYZE
calcule les statistiques des données à partir
d'extraits aléatoires ; il est de ce fait possible que les coûts
estimés soient modifiés après l'exécution de cette commande, alors même la
distribution réelle des données dans la table n'a pas changé.
Notez que l'exemple précédent montrait un plan « personnalisé »
pour les valeurs spécifiques des paramètres données dans
EXECUTE
. Nous pourrions aussi souhaiter voir le plan
générique pour une requête à paramètre, ce qui peut se faire avec
GENERIC_PLAN
:
EXPLAIN (GENERIC_PLAN) SELECT sum(bar) FROM test WHERE id > $1 AND id < $2 GROUP BY foo; QUERY PLAN ------------------------------------------------------------------------------- HashAggregate (cost=26.79..26.89 rows=10 width=12) Group Key: foo -> Index Scan using test_pkey on test (cost=0.29..24.29 rows=500 width=8) Index Cond: ((id > $1) AND (id < $2)) (4 rows)
Dans ce cas, l'analyseur détecte correctement que $1
et $2
devraient avoir le même type de données que
id
, donc la manque d'information sur le type de
paramètre par PREPARE
n'était pas un problème. Dans
d'autres cas, il pourrait être nécessaire de spécifier explicitement les
types pour les symboles des paramètres, ce qui peut se faire en les
convertissant. Par exemple :
EXPLAIN (GENERIC_PLAN) SELECT sum(bar) FROM test WHERE id > $1::integer AND id < $2::integer GROUP BY foo;
L'instruction EXPLAIN
n'est pas définie dans le standard
SQL.
La syntaxe suivante était utilisée avant la version 9.0 de PostgreSQL et est toujours acceptée :
EXPLAIN [ ANALYZE ] [ VERBOSE ] requete
Notez que, dans cette syntaxe, les options doivent être indiquées dans l'ordre exact affiché.