PostgreSQLLa base de données la plus sophistiquée au monde.
Documentation PostgreSQL 15.9 » Annexes » Modules supplémentaires fournis » pg_stat_statements

F.32. pg_stat_statements

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.

F.32.1. La vue 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.20.

Tableau F.20. Colonnes de pg_stat_statements

Type de colonne

Description

userid oid (référence pg_authid.oid)

OID de l'utilisateur qui a exécuté la requête

dbid oid (référence pg_database.oid)

OID de la base dans laquelle a été exécutée la requête

toplevel bool

True si la requête a été exécutée comme instruction de haut niveau (toujours true si pg_stat_statements.track est configuré à top)

queryid bigint

Code de hachage, calculé à partir de l'arbre d'analyse de la requête

query text

Texte d'une requête représentative

plans bigint

Nombre d'optimisations de la requête (si pg_stat_statements.track_planning est activé, sinon zéro)

total_plan_time double precision

Durée totale passée à optimiser la requête, en millisecondes (si pg_stat_statements.track_planning est activé, sinon zéro)

min_plan_time double precision

Durée minimale passée à optimiser la requête, en millisecondes (si pg_stat_statements.track_planning est activé, sinon zéro)

max_plan_time double precision

Durée maximale passée à optimiser la requête, en millisecondes (si pg_stat_statements.track_planning est activé, sinon zéro)

mean_plan_time double precision

Durée moyenne passée à optimiser la requête, en millisecondes (si pg_stat_statements.track_planning est activé, sinon zéro)

stddev_plan_time double precision

Déviation standard de la durée passée à optimiser la requête, en millisecondes (si pg_stat_statements.track_planning est activé, sinon zéro)

calls bigint

Nombre d'exécutions de la requête

total_exec_time double precision

Durée totale passée à exécuter la requête, en millisecondes

min_exec_time double precision

Durée minimale passée à exécuter la requête, en millisecondes

max_exec_time double precision

Durée maximale passée à exécuter la requête, en millisecondes

mean_exec_time double precision

Durée moyenne passée à exécuter la requête, en millisecondes

stddev_exec_time double precision

Déviation standard de la durée passée à exécuter la requête, en millisecondes

rows bigint

Nombre total de lignes récupérées ou affectées par la requête

shared_blks_hit bigint

Nombre total de blocs lus dans le cache partagé par la requête

shared_blks_read bigint

Nombre total de blocs lus hors cache partagé par la requête

shared_blks_dirtied bigint

Nombre total de blocs modifiés dans le cache partagé par la requête

shared_blks_written bigint

Nombre total de blocs du cache partagé écrit sur disque par la requête

local_blks_hit bigint

Nombre total de blocs lus dans le cache local par la requête

local_blks_read bigint

Nombre total de blocs lus hors du cache local par la requête

local_blks_dirtied bigint

Nombre total de blocs modifiés dans le cache local par la requête

local_blks_written bigint

Nombre total de blocs du cache local écrit sur disque par la requête

temp_blks_read bigint

Nombre total de blocs lus dans les fichiers temporaires par la requête

temp_blks_written bigint

Nombre total de blocs écrits dans les fichiers temporaires par la requête

blk_read_time double precision

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)

blk_write_time double precision

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)

temp_blk_read_time double precision

Durée totale des lectures des blocs de fichiers temporaires en millisecondes (si track_io_timing est activé, sinon zéro)

temp_blk_write_time double precision

Durée totale des écritures des blocs de fichiers temporaires en millisecondes (si track_io_timing est activé, sinon zéro)

wal_records bigint

Nombre total d'enregistrements générés dans les WAL par la requête

wal_fpi bigint

Nombre total d'images complètes de blocs (full page images) générés dans les WAL par la requête

wal_bytes numeric

Nombre total d'octets générés dans les WAL par la requête

jit_functions bigint

Nombre total de fonctions compilées par JIT pour cette requête

jit_generation_time double precision

Durée totale passée par la requête sur la génération de code JIT, en millisecondes

jit_inlining_count bigint

Nombre de fois où les fonctions ont été incluses

jit_inlining_time double precision

Durée totale passée par la requête sur l'inclusion de fonctions, en millisecondes

jit_optimization_count bigint

Nombre de fois où la requête a été optimisée

jit_optimization_time double precision

Durée totale passée sur l'optimisation de la requête, en millisecondes

jit_emission_count bigint

Nombre de fois où du code a été émis

jit_emission_time double precision

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) 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 requêtes utilitaires (c'est-à-dire toutes les autres) ne sont considérées comme unique que lorsqu'elles sont égales au caractère près.

Note

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.

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éplicats 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éplicats 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.

F.32.2. La vue 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.21.

Tableau F.21. Colonnes de pg_stat_statements_info

Type de colonne

Description

dealloc bigint

Nombre total de fois où les enregistrements de pg_stat_statements pour les requêtes les moins exécutées ont été désallouées parce que plus de pg_stat_statements.max requêtes distinctes ont été observées

stats_reset timestamp with time zone

Horodatage de la dernière réinitialisation de toutes les statistiques de pg_stat_statements.


F.32.3. Fonctions

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.

F.32.4. Paramètres de configuration

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
  

F.32.5. Exemple de sortie

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     |

  

F.32.6. Auteurs

Takahiro Itagaki . La normalisation des requêtes a été ajoutée par Peter Geoghegan .