Retour à la documentation
Rapport

Le rapport PWR expliqué en détail : l'AWR de PostgreSQL, section par section

15 min de lecture

Une visite guidée complète du rapport PWR (l'équivalent AWR pour PostgreSQL) : bandeau live, comparaison de snapshots, tendances KPI, requêtes coûteuses, historique par requête, verrous et recommandations automatiques.

PostgreSQL n'a pas d'AWR natif : à quoi sert le rapport PWR ?

Oracle propose depuis des années l'Automatic Workload Repository (AWR) : un rapport de référence pour diagnostiquer les problèmes de performance en comparant deux instantanés de l'activité de la base. PostgreSQL n'a jamais eu d'équivalent officiel — PWR comble ce manque.

Le rapport PWR reprend la même logique : capturer des snapshots réguliers de l'activité (pg_stat_statements, pg_stat_activity, pg_stat_database, pg_settings...), puis comparer deux instantanés pour isoler précisément ce qui a changé entre un "avant" et un "après". Le résultat est un document structuré en une quinzaine de sections, orienté cause racine plutôt que simple collecte de métriques.

Cet article détaille, section par section, un rapport PWR réel (base benchdb) pour que vous sachiez exactement quoi regarder, et dans quel ordre, lors d'un diagnostic.

L'en-tête et le bandeau Live Health Banner

En haut de chaque page, la barre de navigation rappelle le contexte : logo PostgreSQL, titre "Rapport PostgreSQL Workload Repository (PWR)", la base actuellement sélectionnée (ici benchdb), un bouton "Prendre un snapshot maintenant" pour capturer l'état courant sans attendre le prochain snapshot planifié, et le sélecteur de langue.

Juste en dessous, le Live Health Banner affiche l'état instantané de la base, indépendamment des snapshots historiques (source : pg_stat_database / pg_stat_activity / pg_settings via /api/kpi/live). Sur l'exemple, le score de santé global est de 95/100 (statut SAIN), calculé à partir de 5 sous-métriques pondérées : Load 30%, Blocking 25%, Connections 20%, P95 15%, Cache 10%.

Sont affichés en direct : Load (1 session active / limite CPU 8 → 0% de la capacité), connexions utilisées (7 clients / 100 max), P95 Latency (7.7 ms), débit transactionnel (TPS) et taux d'erreur, sessions bloquées, répartition des wait events (CPU / IO / Lock / LWLock / BufferPin / Network / Extension) et cache hit ratio (94.4%). Ce bandeau permet de vérifier en un coup d'œil que la base est saine avant même de lancer une comparaison de snapshots.

En-tête du rapport PWR et bandeau Live Health Banner affichant un score de santé de 95/100

Générer une comparaison : formulaire et état entre les deux snapshots

Le cœur de l'outil est le formulaire de comparaison : on choisit un Upper Snapshot (le plus récent) et un Lower Snapshot (le plus ancien) parmi l'historique, un Top N (nombre de requêtes à afficher par classement, 20 par défaut) et un critère de tri (Mean Exec Time, CPU, Calls, Rows...). Le bouton Generate produit alors le rapport complet pour cette fenêtre de temps.

Juste après génération, le panneau "State between snapshots" résume l'écart global entre les deux instants sous forme d'un badge de sévérité : ici WARNING ("Pression modérée, à surveiller") sur la période du 31/07 05:20 au 02/08 12:30. Les indicateurs affichés (Bg Proc, Total Backends, AAS, Cache Hit Ratio, connexions clientes, sessions bloquées) donnent un premier verdict avant même de lire le détail du rapport.

Deux mini-graphiques accompagnent ce panneau : l'évolution du TPS entre les deux snapshots, et la répartition moyenne des wait events (ici AAS moyen 3.80, dominé par CPU 1.88 et IO 0.71) ainsi que la répartition des requêtes lentes par tranche (5-10s / >10s) — 50 requêtes lentes détectées sur la période, dont 43 de plus de 10 secondes.

Formulaire de comparaison de snapshots et panneau d'état entre snapshots avec badge WARNING

Résumé de l'environnement : la fenêtre de comparaison

La section "Database Environment Summary" identifie sans ambiguïté ce qui est comparé : nom de la base (benchdb), DB Id, version PostgreSQL (18.4 sur Ubuntu), hôte et port. En dessous, un tableau Begin Snap / End Snap précise l'ID de chaque snapshot, sa date exacte, le nombre de sessions et de sessions actives à cet instant.

Deux métriques structurantes ferment cette section : Elapsed (durée réelle écoulée entre les deux snapshots, ici 430.27 minutes) et DB Time (temps cumulé que la base a passé à traiter des requêtes sur cette période, ici 193.96 minutes). Comparer Elapsed et DB Time est un premier réflexe de diagnostic : si DB Time dépasse largement Elapsed, cela indique une forte parallélisation ou une saturation CPU/IO plutôt qu'un simple pic isolé.

Résumé de l'environnement avec les snapshots de début et de fin, Elapsed et DB Time

Détails techniques de l'instance : version, OS et mémoire

Une seconde table "Database Environment Summary" (détaillée) complète le contexte technique : nom d'instance (18/main), heure de démarrage du serveur (Startup Time), hôte, port, la chaîne de version complète (PostgreSQL 18.4, compilé avec gcc sur Ubuntu), ainsi que les paramètres mémoire/connexions issus de pg_settings : Maxconn (100), Shbuf Mb (shared_buffers, 512 Mo), Workmem Mb (4 Mo) et Maintmem Mb (maintenance_work_mem, 64 Mo).

Ces valeurs servent de référence pour interpréter le reste du rapport : un cache hit ratio qui se dégrade ne se lit pas de la même façon selon que shared_buffers vaut 512 Mo ou plusieurs Go.

Résumé détaillé de l'instance : version PostgreSQL, OS, shared_buffers, work_mem et maintenance_work_mem

Répartition des sessions actives par classe d'attente

Ce graphique empilé décompose, snapshot par snapshot, la charge active (AAS) par classe d'événement d'attente : CPU (vert), IO (bleu), LWLock (rouge), IPC (jaune), Client (violet) et Other (gris). Chaque barre représente un snapshot ; sa hauteur totale correspond à l'AAS à cet instant, et les couleurs montrent sur quoi la base attendait réellement.

Sur l'exemple, on voit clairement un pic isolé (snapshot #31) où l'AAS dépasse largement la moyenne, avec une contribution CPU et IO dominante. Le tableau min/max/avg à droite (CPU moyen 2.09, IO moyen 1.64, LWLock 0.08...) permet de relativiser ce pic par rapport au comportement habituel de la base sur la période (7 jours ici). Survoler un segment affiche sa valeur exacte.

Graphique empilé de la répartition des sessions actives par classe d'attente (CPU, IO, LWLock, IPC, Client, Other)

Tendances KPI dans le temps : AAS et P95 Latency

La section "KPI Trends — Time Evolution" trace l'évolution de deux indicateurs clés snapshot après snapshot, avec des raccourcis de période (1h, 3h, 6h, 12h, 24h, 7 jours, All) et la possibilité de saisir un intervalle personnalisé ou deux dates précises.

Le graphique AAS affiche une ligne de seuil rouge pointillée correspondant à max_worker_processes (ici 8) : tant que l'AAS reste sous cette ligne, la base dispose de marge ; au-dessus, elle sature ses workers disponibles. Le graphique P95 Latency, à droite, montre le 95e percentile de latence instantanée des sessions actives, converti en secondes — l'indicateur le plus parlant de l'impact ressenti par les utilisateurs.

Graphiques de tendance AAS avec seuil max_worker_processes et P95 Latency dans le temps

Évolution des requêtes lentes (5-10s / >10s)

Ce graphique historise, pour chaque snapshot, le nombre de requêtes actives encore en cours au-delà de 5 secondes, ventilé entre deux tranches (5-10 secondes en orange, plus de 10 secondes en rouge) et un total (violet, en pointillé).

Sur l'exemple, on repère immédiatement un pic ponctuel autour du 31/07 vers 04h-05h, où le nombre de requêtes de plus de 10 secondes explose avant de retomber à zéro — la signature typique d'un incident isolé plutôt que d'une dégradation progressive.

Graphique d'évolution des requêtes lentes 5-10 secondes et plus de 10 secondes

TPS, cache hit ratio, sessions actives et connexions

Quatre mini-graphiques complètent le tableau de bord temporel : le débit transactionnel (Transactions per Second), le taux de cache hit (pourcentage de lectures servies depuis shared_buffers plutôt que depuis le disque, ici stable à 100% sur la période observée), les sessions actives instantanées, et la tendance des connexions (clients + background) comparée au plafond max_connections (ligne rouge pointillée, 100 ici).

Ces quatre courbes permettent de vérifier rapidement qu'aucune de ces métriques ne dérive silencieusement en dehors des pics déjà identifiés par l'AAS et les requêtes lentes.

Graphiques TPS, cache hit ratio, sessions actives et tendance des connexions vs max_connections

Activité détaillée par snapshot (DB Activity / AAS)

La table "DB Activity (AAS)" liste, snapshot par snapshot, l'ID, la date d'extraction, le temps écoulé (Elapsed), le DB Time et l'AAS calculé pour cet intervalle précis. C'est la vue brute derrière les graphiques précédents : elle permet de retrouver l'ID exact du snapshot à sélectionner dans le formulaire de comparaison si l'on souhaite zoomer sur un pic particulier (par exemple le snapshot #31, dont l'AAS calculé de 34.94 confirme le pic visible sur le graphique de répartition par classe d'attente).

Table DB Activity (AAS) listant Elapsed, DB Time et AAS pour chaque snapshot

Delta d'activité entre les deux snapshots (par base et par utilisateur)

La section "Database Activity Delta (Calls / Time / Rows)" agrège, pour chaque combinaison base/utilisateur, le nombre d'appels, le temps d'exécution cumulé et le nombre de lignes traitées entre le snapshot de début et celui de fin. Sur l'exemple, l'utilisateur bench_reader a exécuté 67 657 appels sur benchdb pour 108.76 secondes de temps CPU cumulé — l'essentiel de l'activité mesurée sur la période.

Cette vue permet d'identifier immédiatement quel couple base/utilisateur concentre la charge avant même de descendre au niveau de la requête individuelle.

Table du delta d'activité (appels, temps d'exécution, lignes) par base et par utilisateur

Top Queries : les requêtes les plus coûteuses en temps d'exécution

C'est la table la plus consultée du rapport : elle classe les requêtes par Mean Exec Time (ou selon le critère choisi dans le formulaire), avec pour chacune son utilisateur, sa base, son Queryid, le nombre d'appels, la part des appels en %, le temps CPU cumulé et sa part en %, le nombre de lignes retournées, la part d'IO en %, le temps d'exécution total et un extrait de la requête (cliquable, lien vers l'historique complet de ce Queryid).

Sur l'exemple, une seule requête (SELECT * FROM pgbench_accounts WHERE aid <= $1 ORDER BY abalance ...) domine littéralement le classement : 99.3% du CPU et 95.37% de l'IO pour seulement 322 appels — la signature typique d'un scan et d'un tri non indexés, largement plus coûteux à l'appel que le reste du trafic.

Table Top Queries classée par temps d'exécution moyen, avec CPU %, IO % et extrait de la requête

La popup "Query History" : l'historique d'exécution d'une requête précise

Dans toutes les tables de classement (Top Queries, Top by Calls, Top CPU...), l'extrait de requête est cliquable. Un clic ouvre une popup "Query History" qui affiche le Queryid concerné, le texte complet de la requête, puis un tableau listant chaque snapshot où cette requête a été capturée : date d'extraction, utilisateur, base, nombre d'appels, lignes totales/moyennes, temps d'exécution total et moyen.

C'est l'outil qui permet de répondre à la question "cette requête est-elle devenue lente d'un coup, ou se dégrade-t-elle progressivement ?". Sur l'exemple, la requête SELECT pg_advisory_unlock_all() est suivie sur plusieurs relevés (de 06:35 à 10:00) avec un Mean Exec Time qui reste stable autour de 0.05 seconde — confirmant qu'elle n'est pas la source d'une dérive, malgré ses 267 appels.

Cette vue est particulièrement utile après avoir repéré une requête suspecte dans un Top Queries : au lieu de comparer seulement deux snapshots, elle permet de rejouer toute la chronologie d'exécution de cette requête précise et de dater exactement l'apparition d'une régression.

Popup Query History affichant l'historique d'exécution d'une requête (calls, lignes, temps d'exécution par snapshot)

Top Queries by Calls : les requêtes les plus fréquentes

Ce classement change de critère : il trie par volume d'appels plutôt que par coût unitaire. Une requête peu coûteuse à l'exécution (un simple BEGIN, COMMIT ou SELECT ... WHERE aid = $1) peut représenter l'essentiel du trafic transactionnel sans jamais apparaître dans le Top CPU.

C'est le complément indispensable du Top Queries précédent : une requête rapide mais appelée des millions de fois peut, cumulée, peser aussi lourd sur la base qu'une requête lente appelée rarement. Sur l'exemple, on retrouve le cortège classique d'une charge de type pgbench (BEGIN, UPDATE, SELECT, COMMIT) avec plusieurs centaines de milliers d'appels chacune.

Table Top Queries by Calls classée par volume d'appels

Top CPU Queries : la consommation processeur en détail

Ce classement isole spécifiquement la consommation CPU cumulée par requête, indépendamment du nombre d'appels ou du temps d'exécution total. Il permet de répondre à la question : "si je devais optimiser une seule requête pour libérer du CPU, laquelle choisir ?"

Sans surprise, on retrouve en tête la même requête déjà identifiée dans le Top Queries global (SELECT ... ORDER BY abalance), confirmant qu'elle est bien la cause racine de la pression CPU observée sur la période, et non un artefact statistique lié à un critère de tri différent.

Table Top CPU Queries classée par consommation processeur cumulée

Top Cache Memory & Top Temp Memory : pression mémoire et disque temporaire

Le "Top Cache Memory Queries" reprend le même jeu de colonnes pour identifier les requêtes qui sollicitent le plus le cache mémoire (shared_buffers) — utile pour repérer une dégradation du cache hit ratio liée à une requête précise plutôt qu'à un sous-dimensionnement global.

Juste en dessous, le "Top Temp Memory Queries" liste les requêtes qui débordent sur le disque temporaire (tris ou hash joins trop volumineux pour work_mem) : sur l'exemple, les appels à perfhist.new_snapshot() et la création d'index apparaissent ici, un signal qu'il peut être pertinent d'ajuster work_mem ou de revoir ces opérations de maintenance ponctuelles.

Tables Top Cache Memory Queries et Top Temp Memory Queries

Top Users, verrous bloquants et recommandations automatiques

La section "Top Users" agrège l'activité par utilisateur base par base (total d'appels, CPU cumulé, lignes traitées) — un raccourci pour savoir quel compte applicatif ou quel rôle technique consomme le plus de ressources sur la période.

"Blocking Sessions / Locks" liste les sessions actuellement bloquées par un verrou détenu par une autre transaction (vide ici, "No data", signe qu'aucune contention n'était en cours au moment du rapport). Enfin, "Key Findings & Recommendations" traduit automatiquement les métriques en recommandations actionnables : requêtes à total_exec_time élevé à optimiser en priorité, requêtes retournant beaucoup de lignes révélant des scans ou jointures non optimisées, utilisateurs les plus consommateurs à auditer, requêtes avec temp_blks_written élevé (tris/hash joins débordant sur disque) et shared_blks_read élevé (lectures disque, index manquant ou plan sous-optimal) à corriger, avec un rappel à vérifier les Top Users associés.

Tables Top Users, Blocking Sessions / Locks et liste des Key Findings & Recommendations

De la lecture du rapport à l'action

Prises dans l'ordre, ces sections répondent à une progression logique : d'abord vérifier l'état instantané (Live Health Banner), puis quantifier l'écart global entre deux instants (State between snapshots, Environment Summary), comprendre où la base a attendu (répartition par classe d'attente, tendances AAS/P95), descendre au niveau de la requête individuelle (les différents Top Queries) pour isoler la cause racine, et enfin vérifier les verrous et lire les recommandations automatiques.

Le rapport complet utilisé dans cet article est disponible en téléchargement ci-dessous au format PDF, pour l'explorer par vous-même section par section.