Le module pg_stat_statements
fournit un moyen de
surveiller les statistiques d'optimisation et d'exécution de tous les
ordres SQL exécutés par un serveur.
Le module doit être chargé par l'ajout de
pg_stat_statements
à shared_preload_libraries dans le fichier de configuration
postgresql.conf
parce qu'il a besoin de mémoire
partagée supplémentaire. Ceci signifie qu'il faut redémarrer le serveur
pour ajouter ou supprimer le module. De plus, le calcul de l'identifiant de
requête doit être activé pour que le module soit actif, ce qui est fait
automatiquement si compute_query_id est configuré à
auto
ou on
, ou tout autre module
tiers chargé qui calcule les identifiants de requête.
Quand pg_stat_statements
est actif, il récupère des
statistiques sur toutes les bases de données du serveur. Pour y accéder et
les manipuler, le module fournit les vues
pg_stat_statements
et
pg_stat_statements_info
, et les fonctions
pg_stat_statements_reset
et
pg_stat_statements
. Elles ne sont pas disponibles
globalement mais peuvent être activées pour une base de données spécifique
avec l'instruction CREATE EXTENSION pg_stat_statements
.
pg_stat_statements
#
Les statistiques collectées par le module sont rendues disponibles par une
vue nommée pg_stat_statements
. Cette vue contient
une ligne pour chaque combinaison d'un identifiant de base de données,
d'un identifiant utilisateur, d'un identifiant de requête et s'il s'agit
d'une requête principale ou non (jusqu'au nombre maximum d'ordres
distincts que le module peut surveiller). Les colonnes de la vue sont
affichées dans Tableau F.22.
Tableau F.22. Colonnes de pg_stat_statements
Type de colonne Description |
---|
OID de l'utilisateur qui a exécuté la requête |
OID de la base dans laquelle a été exécutée la requête |
True si la requête a été exécutée comme instruction de haut niveau
(toujours true si |
Code de hachage, calculé à partir de l'arbre d'analyse de la requête |
Texte d'une requête représentative |
Nombre d'optimisations de la requête
(si |
Durée totale passée à optimiser la requête, en millisecondes
(si |
Durée minimale passée à optimiser la requête, en millisecondes
(si |
Durée maximale passée à optimiser la requête, en millisecondes
(si |
Durée moyenne passée à optimiser la requête, en millisecondes
(si |
Déviation standard de la durée passée à optimiser la requête, en
millisecondes
(si |
Nombre d'exécutions de la requête |
Durée totale passée à exécuter la requête, en millisecondes |
Durée minimale passée à exécuter la requête, en millisecondes |
Durée maximale passée à exécuter la requête, en millisecondes |
Durée moyenne passée à exécuter la requête, en millisecondes |
Déviation standard de la durée passée à exécuter la requête, en millisecondes |
Nombre total de lignes récupérées ou affectées par la requête |
Nombre total de blocs lus dans le cache partagé par la requête |
Nombre total de blocs lus hors cache partagé par la requête |
Nombre total de blocs modifiés dans le cache partagé par la requête |
Nombre total de blocs du cache partagé écrit sur disque par la requête |
Nombre total de blocs lus dans le cache local par la requête |
Nombre total de blocs lus hors du cache local par la requête |
Nombre total de blocs modifiés dans le cache local par la requête |
Nombre total de blocs du cache local écrit sur disque par la requête |
Nombre total de blocs lus dans les fichiers temporaires par la requête |
Nombre total de blocs écrits dans les fichiers temporaires par la requête |
Durée totale de lecture des blocs des fichiers de données (hors cache) par la requête, en millisecondes (si track_io_timing est activé, sinon zéro) |
Durée totale de l'écriture des blocs des fichiers de données (hors cache) par la requête, en millisecondes (si track_io_timing est activé, sinon zéro) |
Durée totale des lectures des blocs de fichiers temporaires en millisecondes (si track_io_timing est activé, sinon zéro) |
Durée totale des écritures des blocs de fichiers temporaires en millisecondes (si track_io_timing est activé, sinon zéro) |
Nombre total d'enregistrements générés dans les WAL par la requête |
Nombre total d'images complètes de blocs (full page images) générés dans les WAL par la requête |
Nombre total d'octets générés dans les WAL par la requête |
Nombre total de fonctions compilées par JIT pour cette requête |
Durée totale passée par la requête sur la génération de code JIT, en millisecondes |
Nombre de fois où les fonctions ont été incluses |
Durée totale passée par la requête sur l'inclusion de fonctions, en millisecondes |
Nombre de fois où la requête a été optimisée |
Durée totale passée sur l'optimisation de la requête, en millisecondes |
Nombre de fois où du code a été émis |
Durée totale passée par la requête sur de l'émission de code, en millisecondes |
Pour raisons de sécurité, seuls les superutilisateurs et les rôles
disposant des droits du
rôle pg_read_all_stats
sont autorisé à voir le texte
SQL ainsi que le champ queryid
des requêtes
exécutées par d'autres utilisateurs. Les autres utilisateurs peuvent
cependant voir les statistiques, si la vue a été installée dans leur base
de données.
Les requêtes qui disposent d'un plan d'exécution
(c'est-à-dire SELECT
, INSERT
,
UPDATE
, DELETE
et
MERGE
) et les commandes utilitaires sont combinées en
une entrée unique dans pg_stat_statements
lorsqu'elles ont un plan d'exécution similaire (d'après leur hachage). En
substance, cela signifie que deux requêtes seront considérées comme
équivalentes si elles sont sémantiquement les mêmes mais disposent de
valeurs littérales différentes dans la requête.
Les détails suivant sur le remplacement des constantes et le
queryid
s'appliquent seulement si compute_query_id est activé. Si vous utilisez un module
externe pour calculer queryid
, vous devez vous
référer à sa documentation pour les détails.
Quand la valeur d'une constante a été ignorée pour pouvoir comparer la
requête à d'autres requêtes, la constante est remplacée par un symbole de
paramètre, tel que $1
, dans l'affichage de
pg_stat_statements
. Le reste du texte de la
requête est tel qu'était la première requête ayant la valeur de hashage
queryid
spécifique associée à l'entrée dans
pg_stat_statements
.
Les requêtes sur lesquelles la normalisation peut être appliquée peuvent
être observées avec des valeurs constantes dans
pg_stat_statements
, principalement quand il y a
un taux élevé de désallocations d'entrées. Pour réduire ce risque, pensez
à augmenter pg_stat_statements.max
. La vue
pg_stat_statements_info
, discutée ci-dessous dans
Section F.32.2, fournit des
statistiques sur les désallocations d'entrées.
Dans certains cas, les requêtes SQL avec des textes différents peuvent être
fusionnés en une seule entrée pg_stat_statements
.
Normalement, cela n'arrive que pour les requêtes dont la sémantique est
équivalente, mais il y a une petite chance que des collisions de
l'algorithme de hachage aient pour conséquence la fusion de requêtes sans
rapport en une entrée. (Cela ne peut cependant pas arriver pour des
requêtes appartenant à des utilisateurs différents ou des bases de données
différentes).
Puisque la valeur de hachage queryid
est
calculée sur la représentation de la requête après analyse, l'inverse est
également possible : des requêtes avec un texte identique peuvent
apparaître comme des entrées séparées, si elles ont des significations
différentes en fonction de facteurs externes, comme des réglages de
search_path
différents.
Les programmes utilisant pg_stat_statements
pourraient préférer utiliser queryid
(peut-être
en association avec dbid
et
userid
) pour disposer d'un identifiant plus
stable et plus sûr pour chaque entrée plutôt que le texte de la requête.
Cependant, il est important de comprendre qu'il n'y a qu'une garantie
limitée sur la stabilité de la valeur de hachage de
queryid
. Puisque l'identifiant est dérivé de
l'arbre après analyse, sa valeur est une fonction, entre autres choses, des
identifiants d'objet interne apparaissant dans cette représentation. Cela
a des implications paradoxales. Par exemple,
pg_stat_statements
considérera deux requêtes
apparemment identiques comme distinctes, si elles référencent une table qui
a été supprimée et recréée entre l'exécution de ces deux requêtes. Le
processus de hachage est également sensible aux différences d'architecture
des machines ainsi que d'autres facettes de la plateforme. De plus, il
n'est pas sûr de partir du principe que queryid
restera stable entre des versions majeures de
PostgreSQL.
Deux serveurs participant à une réplication basée sur le rejeu physique des
journaux de transactions devraient avoir des valeurs de
queryid
identiques pour la même requête.
Cependant, les systèmes de réplication logique ne promettent pas de conserver
des réplicas identiques surtout les détails intéressants, donc
queryid
ne sera pas un identifiant utile pour
accumuler les coûts dans un ensemble de réplicas logiques. En cas de doute,
un test direct est recommandé.
En règle général, il peut être supposé que les valeurs
queryid
sont stables entre des versions mineures
de PostgreSQL, en supposant que les instances sont
exécutées sur la même architecture matérielle et que les détails de
métadonnées du catalogue correspondent. La compatibilité entre versions
mineures ne sera cassée qu'en cas de dernière extrémité.
Le symbole de paramètre utilité pour remplacer les constantes dans le texte
représentatif de la requête démarre après le plus grand paramètre
$
n
dans le texte de la
requête originale, ou $1
s'il n'y en avait pas. Il est
intéressant de noter que dans certains cas il pourrait y avoir un symbole de
paramètre caché qui affecte cette numérotation. Par exemple,
PL/pgSQL utilise des symbole de paramètre cachés
pour insérer des valeurs de variables locales à la fonction dans les
requêtes, ainsi un ordre PL/pgSQL comme
SELECT i + 1 INTO j
aurait un texte représentatif tel
que SELECT i + $2
.
Les textes des requêtes sont conservées dans un fichier texte externe et ne
consomment pas de mémoire partagée. De ce fait, même les textes très longs
de requêtes peuvent être enregistrés avec succès. Néanmoins, si beaucoup de
textes très longs de requêtes sont accumulées, le fichier externe peut
devenir suffisamment gros pour ne plus être gérable. Si cela survient,
comme méthode de restauration, pg_stat_statements
peut
choisir d'ignorer les textes de requêtes. Dans ce cas, le champ
query
apparaitra vide sur les lignes de la vue
pg_stat_statements
mais les statistiques associées
seront préservées. Si cela arrive, réfléchissez à réduire la valeur du
paramètre pg_stat_statements.max
pour empêcher que cela
ne recommence.
plans
et calls
peuvent différer car les statistiques d'optimisation/planification et
d'exécution sont mises à jours à leur fin respective, et seulement si elles
ont réussi. Par exemple, si une requête est optimisée avec succès mais
échoue sur la phase d'exécution, seules les statistiques
d'optimisation/planification seront modifiées. Si
l'optimisation/planification est ignorée parce qu'un plan en cache est
utilisé, seules les statistiques d'exécution seront mises à jour.
pg_stat_statements_info
#
Les statistiques du module pg_stat_statements
lui-même
sont tracées et rendues disponibles via une vue nommée
pg_stat_statements_info
. Cette vue contient une
seule ligne. Les colonnes de la vue sont affichées dans Tableau F.23.
Tableau F.23. Colonnes de pg_stat_statements_info
Type de colonne Description |
---|
Nombre total de fois où les enregistrements de
|
Horodatage de la dernière réinitialisation de toutes les statistiques de
|
pg_stat_statements_reset(userid Oid, dbid Oid, queryid bigint) returns void
pg_stat_statements_reset
annule les statistiques
récupérées jusqu'à maintenant par
pg_stat_statements
correspondant aux
userid
, dbid
et
queryid
indiqués. Si un de ces paramètres
n'est pas spécifié, la valeur par défaut 0
(invalide) est utilisée pour chacun d'entre eux et les statistiques qui
correspondent avec les autres paramètres seront réinitialisées. Si
aucun paramètre n'est spécifié ou si tous les paramètres spécifiés sont
à 0
(invalide), elle annulera toutes les
statistiques. Si toutes les statistiques de la vue
pg_stat_statements
sont annulées, cela va aussi
réinitialiser les statistiques de la vue
pg_stat_statements_info
. Par défaut, cette
fonction peut seulement être exécutée par les superutilisateurs.
L'accès peut être donné à d'autres utilisateurs en utilisant la
commande GRANT
.
pg_stat_statements(showtext boolean) returns setof record
La vue pg_stat_statements
est basée sur une
fonction également nommée pg_stat_statements
. Les
clients peuvent appeler la fonction
pg_stat_statements
directement, et peuvent en
spécifiant showtext := false
ne pas récupérer le
texte de la requête (ce qui veut dire que l'argument
OUT
qui correspond à la colonne
query
de la vue retournera des NULL). Cette
fonctionnalité est prévue pour le support d'outils externes qui
pourraient vouloir éviter le surcoût de récupérer de manière répétée
les textes des requêtes de longueur indéterminées. De tels outils
peuvent à la place eux-même mettre le premier texte de requête récupéré
pour chaque entrée, puisque c'est déjà ce que fait
pg_stat_statements
lui-même, et ensuite récupérer
les textes de requêtes uniquement si nécessaire. Puisque le serveur
stocke les textes de requête dans un fichier, cette approche pourrait
réduire les entrée/sorties physiques pour des vérifications répétées
des données de pg_stat_statements
.
pg_stat_statements.max
(integer
)
pg_stat_statements.max
est le nombre maximum d'ordres
tracés par le module (c'est-à-dire le nombre maximum de lignes dans la
vue pg_stat_statements
). Si un nombre
supérieur d'ordres SQL distincts a été observé, c'est l'information sur
les ordres les moins exécutés qui est ignorée. Le nombre de fois où une
telle information est ignorée est consultable dans la vu
pg_stat_statements_info
. La valeur par défaut
est 5000. Ce paramètre peut uniquement être positionné au démarrage du
serveur.
pg_stat_statements.track
(enum
)
pg_stat_statements.track
contrôle quels sont les
ordres comptabilisés par le module. Spécifiez top
pour suivre les ordres de plus haut niveau (ceux qui sont soumis
directement par les clients), all
pour suivre
également les ordres imbriqués (tels que les ordres invoqués dans les
fonctions) ou none
pour désactiver la récupération
des statistiques sur les requêtes. La valeur par défaut est
top
. Seuls les superutilisateurs peuvent changer ce
paramétrage.
pg_stat_statements.track_utility
(boolean
)
pg_stat_statements.track_utility
contrôle si les
commandes utilitaires sont tracées par le module. Les commandes
utilitaires sont toutes les commandes SQL sauf
SELECT
, INSERT
,
UPDATE
, DELETE
et
MERGE
. La valeur par
défaut est on
. Seuls les superutilisateurs peuvent
modifier cette configuration.
pg_stat_statements.track_planning
(boolean
)
pg_stat_statements.track_planning
contrôle si les
opérations d'optimisation/planification et leur durée sont tracées par
ce module. Activer ce paramètre pourrait résulter en une perte visible
de performance, spécialement quand les requêtes avec des structures
identiques sont exécutées par de nombreuses connexions concurrentes, ce
qui pousse à mettre à jour un petit nombre d'entrées de
pg_stat_statements
. La valeur par défaut est
off
. Seuls les superutilisateurs peuvent modifier
cette configuration.
pg_stat_statements.save
(boolean
)
pg_stat_statements.save
précise s'il faut sauvegarder
les statistiques lors des arrêts du serveur. S'il est
off
, alors les statistiques ne sont pas sauvegardées
lors de l'arrêt ni rechargées au démarrage du serveur. La valeur par
défaut est on
. Ce paramètre peut uniquement être
positionné dans le fichier postgresql.conf
ou sur
la ligne de commande du serveur.
Le module a besoin de mémoire partagée supplémentaire proportionnelle à
pg_stat_statements.max
. Notez que cette mémoire est
consommée quand le module est chargé, même si
pg_stat_statements.track
est positionné à
none
.
Ces paramètres doivent être définis dans
postgresql.conf
. Un usage courant pourrait être :
# postgresql.conf shared_preload_libraries = 'pg_stat_statements' compute_query_id = on pg_stat_statements.max = 10000 pg_stat_statements.track = all
bench=# SELECT pg_stat_statements_reset(); $ pgbench -i bench $ pgbench -c10 -t300 bench bench=# \x bench=# SELECT query, calls, total_exec_time, rows, 100.0 * shared_blks_hit / nullif(shared_blks_hit + shared_blks_read, 0) AS hit_percent FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 5; -[ RECORD 1 ]---+-------------------------------------------------------------------- query | UPDATE pgbench_branches SET bbalance = bbalance + $1 WHERE bid = $2 calls | 3000 total_exec_time | 25565.855387 rows | 3000 hit_percent | 100.0000000000000000 -[ RECORD 2 ]---+-------------------------------------------------------------------- query | UPDATE pgbench_tellers SET tbalance = tbalance + $1 WHERE tid = $2 calls | 3000 total_exec_time | 20756.669379 rows | 3000 hit_percent | 100.0000000000000000 -[ RECORD 3 ]---+-------------------------------------------------------------------- query | copy pgbench_accounts from stdin calls | 1 total_exec_time | 291.865911 rows | 100000 hit_percent | 100.0000000000000000 -[ RECORD 4 ]---+-------------------------------------------------------------------- query | UPDATE pgbench_accounts SET abalance = abalance + $1 WHERE aid = $2 calls | 3000 total_exec_time | 271.232977 rows | 3000 hit_percent | 98.8454011741682975 -[ RECORD 5 ]---+-------------------------------------------------------------------- query | alter table pgbench_accounts add primary key (aid) calls | 1 total_exec_time | 160.588563 rows | 0 hit_percent | 100.0000000000000000 bench=# SELECT pg_stat_statements_reset(0,0,s.queryid) FROM pg_stat_statements AS s WHERE s.query = 'UPDATE pgbench_branches SET bbalance = bbalance + $1 WHERE bid = $2'; bench=# SELECT query, calls, total_exec_time, rows, 100.0 * shared_blks_hit / nullif(shared_blks_hit + shared_blks_read, 0) AS hit_percent FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 5; -[ RECORD 1 ]---+-------------------------------------------------------------------- query | UPDATE pgbench_tellers SET tbalance = tbalance + $1 WHERE tid = $2 calls | 3000 total_exec_time | 20756.669379 rows | 3000 hit_percent | 100.0000000000000000 -[ RECORD 2 ]---+-------------------------------------------------------------------- query | copy pgbench_accounts from stdin calls | 1 total_exec_time | 291.865911 rows | 100000 hit_percent | 100.0000000000000000 -[ RECORD 3 ]---+-------------------------------------------------------------------- query | UPDATE pgbench_accounts SET abalance = abalance + $1 WHERE aid = $2 calls | 3000 total_exec_time | 271.232977 rows | 3000 hit_percent | 98.8454011741682975 -[ RECORD 4 ]---+-------------------------------------------------------------------- query | alter table pgbench_accounts add primary key (aid) calls | 1 total_exec_time | 160.588563 rows | 0 hit_percent | 100.0000000000000000 -[ RECORD 5 ]---+-------------------------------------------------------------------- query | vacuum analyze pgbench_accounts calls | 1 total_exec_time | 136.448116 rows | 0 hit_percent | 99.9201915403032721 bench=# SELECT pg_stat_statements_reset(0,0,0); bench=# SELECT query, calls, total_exec_time, rows, 100.0 * shared_blks_hit / nullif(shared_blks_hit + shared_blks_read, 0) AS hit_percent FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 5; -[ RECORD 1 ]---+----------------------------------------------------------------------------- query | SELECT pg_stat_statements_reset(0,0,0) calls | 1 total_exec_time | 0.189497 rows | 1 hit_percent | -[ RECORD 2 ]---+----------------------------------------------------------------------------- query | SELECT query, calls, total_exec_time, rows, $1 * shared_blks_hit / + | nullif(shared_blks_hit + shared_blks_read, $2) AS hit_percent+ | FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT $3 calls | 0 total_exec_time | 0 rows | 0 hit_percent |
Takahiro Itagaki <itagaki.takahiro@oss.ntt.co.jp>
. La
normalisation des requêtes a été ajoutée par Peter Geoghegan
<peter@2ndquadrant.com>
.