PostgreSQL est livré avec une pléthore d'options de configuration, mais la modification des paramètres par défaut de certaines de ces options améliore considérablement l'observabilité de votre serveur PostgreSQL. Vous souhaiterez définir et configurer ces options avant que des problèmes n'apparaissent en production, car elles peuvent fournir des informations essentielles pour comprendre et résoudre ces problèmes.
Lisez la suite pour en savoir plus sur les paramètres et les extensions qui exposent des métriques et des informations sur le fonctionnement interne de votre serveur PostgreSQL.
Préfixe de ligne de journal
Le log_line_prefix L'option de configuration détermine ce que PostgreSQL écrit au début de chaque ligne de journal. La valeur par défaut dépend de la distribution Linux spécifique ou de la solution gérée que vous utilisez, mais le plus souvent, elle n'inclut pas quelques éléments qui peuvent s'avérer très utiles pour traquer les clients qui se comportent mal. Essayez ce log_line_prefix :
log_line_prefix = '%m [%p] %a %u %d %h '
Il inclut l'horodatage (%m ), le PID du processus backend (%p ), le nom de l'application (%a ) du client, le nom d'utilisateur avec lequel le client s'est connecté (%u ), la base de données à laquelle le client s'est connecté (%d ) et le nom d'hôte ou l'IP d'où provient la connexion (%h ). Cela se traduit par des lignes de connexion comme celle-ci :
2021-01-30 05:06:03.675 UTC [73] psql postgres bench 172.17.0.1 ERROR: relation "pgbench_akkounts" does not exist at character 15
2021-01-30 05:06:03.675 UTC [73] psql postgres bench 172.17.0.1 STATEMENT: select * from pgbench_akkounts;
qui sont beaucoup plus utiles que la valeur par défaut. Vous pouvez voir qu'un client s'est connecté depuis 172.17.0.1 en tant qu'utilisateur postgres à la base de données banc , et l'application était psql . Certainement une amélioration par rapport à l'option par défaut, qui ne montre que ceci :
2021-01-30 05:13:22.630 UTC [63] ERROR: relation "pgbench_akkounts" does not exist at character 15
2021-01-30 05:13:22.630 UTC [63] STATEMENT: select * from pgbench_akkounts;
Journaliser les requêtes lentes
PostgreSQL peut être configuré pour consigner les requêtes dont l'exécution prend plus d'un certain temps. Ceux-ci vont dans le même fichier journal; il n'y a pas de fichier journal séparé pour les requêtes lentes comme dans MySQL.
Pour consigner les instructions qui prennent plus d'une seconde à s'exécuter, utilisez la log_min_duration_statement option comme celle-ci :
log_min_duration_statement = 1s
Notez que log_min_duration_statement prendra en compte toutes les instructions (y compris, par exemple, les instructions d'administration de longue durée telles que REINDEX TABLE ) et pas seulement des requêtes (SELECT ). Voici quelques entrées de journal produites par cette option :
2021-01-30 08:42:57.473 UTC [62] psql postgres postgres 172.17.0.1 LOG: duration: 1016.283 ms statement: select pg_sleep(1);
2021-01-30 08:52:00.541 UTC [62] psql postgres postgres 172.17.0.1 LOG: duration: 1118.277 ms statement: select pg_sleep(1.1);
Si cela entraîne trop de journaux d'instructions similaires, vous pouvez indiquer à Postgres de n'en consigner qu'un pourcentage, en utilisant :
log_min_duration_statement = -1
log_min_duration_sample = 1s
log_statement_sample_rate = 0.25
Cela enregistre seulement 25 % des instructions éligibles pour la journalisation (celles qui ont pris plus d'une seconde pour s'exécuter). La sortie du journal est la même qu'auparavant. Il n'y a aucun moyen de savoir combien de déclarations éligibles n'ont pas été enregistrées.
Pour enregistrer toutes les instructions, ainsi que le temps nécessaire à leur exécution, utilisez le log_statement option à la place :
log_statement = mod
log_duration = on
L'option 'mod' indique à Postgres de consigner les DDL et les instructions de modification des données. Il en résulte des journaux comme ceux-ci :
2021-01-30 08:35:08.985 UTC [64] pgbench postgres bench 172.17.0.1 LOG: statement: insert into pgbench_tellers(tid,bid,tbalance) values (10,1,0)
2021-01-30 08:35:08.985 UTC [64] pgbench postgres bench 172.17.0.1 LOG: duration: 0.241 ms
Soyez averti que ce n'est pas possible d'échantillonner la journalisation des déclarations activée de cette façon, toutes les déclarations seront enregistrées et vous vous retrouverez avec des tonnes d'entrées de journal.
Verrouillages et interblocages de journalisation
Les requêtes peuvent attendre trop longtemps pour acquérir un verrou. En règle générale, une limite supérieure de la durée d'attente est définie à l'aide de l'option lock_timeout , généralement du côté client. Si une requête attend depuis si longtemps pour acquérir un verrou, Postgres annulera l'exécution de cette requête et consignera une erreur :
2021-01-30 09:35:52.415 UTC [67] psql postgres testdb 172.17.0.1 ERROR: canceling statement due to lock timeout
2021-01-30 09:35:52.415 UTC [67] psql postgres testdb 172.17.0.1 STATEMENT: cluster t;
Supposons que vous souhaitiez définir un délai de verrouillage de 1 minute, mais que les requêtes de journal attendent les verrous pendant plus de, disons, 30 secondes. Vous pouvez le faire en utilisant :
log_lock_waits = on
deadlock_timeout = 30s
Cela créera des journaux comme celui-ci :
2021-01-30 09:49:22.331 UTC [70] psql postgres testdb 172.17.0.1 LOG: process 70 still waiting for ShareLock on transaction 493 after 30009.004 ms
2021-01-30 09:49:22.331 UTC [70] psql postgres testdb 172.17.0.1 DETAIL: Process holding the lock: 68. Wait queue: 70.
2021-01-30 09:49:22.331 UTC [70] psql postgres testdb 172.17.0.1 CONTEXT: while locking tuple (0,3) in relation "t"
2021-01-30 09:49:22.331 UTC [70] psql postgres testdb 172.17.0.1 STATEMENT: select * from t for update;
L'utilisation de deadlock_timeout n'est pas une faute de frappe :il s'agit de la valeur utilisée par le mécanisme de verrouillage de la journalisation d'attente. Idéalement, il aurait dû y avoir quelque chose comme log_min_duration_lock_wait , mais malheureusement, ce n'est pas le cas.
En cas de blocages réels, Postgres abandonnera les transactions bloquées après deadlock_timeout durée, et enregistrera les déclarations incriminées. Aucune configuration explicite n'est nécessaire.
2021-01-30 09:55:37.724 UTC [68] psql postgres testdb 172.17.0.1 LOG: process 68 detected deadlock while waiting for ShareLock on transaction 496 after 30007.633 ms
2021-01-30 09:55:37.724 UTC [68] psql postgres testdb 172.17.0.1 DETAIL: Process holding the lock: 70. Wait queue: .
2021-01-30 09:55:37.724 UTC [68] psql postgres testdb 172.17.0.1 CONTEXT: while locking tuple (0,3) in relation "t"
2021-01-30 09:55:37.724 UTC [68] psql postgres testdb 172.17.0.1 STATEMENT: select * from t where a=4 for update;
2021-01-30 09:55:37.725 UTC [68] psql postgres testdb 172.17.0.1 ERROR: deadlock detected
2021-01-30 09:55:37.725 UTC [68] psql postgres testdb 172.17.0.1 DETAIL: Process 68 waits for ShareLock on transaction 496; blocked by process 70.
Process 70 waits for ShareLock on transaction 495; blocked by process 68.
Process 68: select * from t where a=4 for update;
Process 70: select * from t where a=0 for update;
2021-01-30 09:55:37.725 UTC [68] psql postgres testdb 172.17.0.1 HINT: See server log for query details.
2021-01-30 09:55:37.725 UTC [68] psql postgres testdb 172.17.0.1 CONTEXT: while locking tuple (0,3) in relation "t"
2021-01-30 09:55:37.725 UTC [68] psql postgres testdb 172.17.0.1 STATEMENT: select * from t where a=4 for update;
Enregistrement des aspirateurs automatiques
Le processus de vide automatique démarre lorsque Postgres détermine que les données d'une table ont suffisamment changé pour justifier un vide et une analyse. Pour garder un œil sur ce processus, activez la journalisation des exécutions d'autovacuum :
log_autovacuum_min_duration = 250ms
Voici un exemple d'entrée qui a été provoqué par des modifications excessives apportées à une table :
2021-01-30 10:23:33.201 UTC [63] LOG: automatic vacuum of table "postgres.public.t": index scans: 0
pages: 0 removed, 95 remain, 0 skipped due to pins, 0 skipped frozen
tuples: 8991 removed, 10000 remain, 0 are dead but not yet removable, oldest xmin: 492
buffer usage: 215 hits, 4 misses, 4 dirtied
avg read rate: 1.885 MB/s, avg write rate: 1.885 MB/s
system usage: CPU: user: 0.01 s, system: 0.00 s, elapsed: 0.01 s
WAL usage: 244 records, 1 full page images, 67984 bytes
2021-01-30 10:23:33.222 UTC [63] LOG: automatic analyze of table "postgres.public.t" system usage: CPU: user: 0.01 s, system: 0.00 s, elapsed: 0.01 s
Notez que le vide automatique déclenchera généralement une analyse après le vide, et cela sera également enregistré.
Ces journaux vous aideront à déterminer la meilleure façon de régler les paramètres d'autovacuum et vous aideront à déterminer si et quand l'autovacuum n'est pas aussi efficace que vous le pensiez.
Points de contrôle de journalisation
Le point de contrôle est le processus qui consiste à pousser les modifications enregistrées par WAL dans les fichiers réels qui sauvegardent les tables. Idéalement, les points de contrôle doivent se produire à intervalles réguliers et pas trop fréquents, car il s'agit d'un processus intensif en termes de CPU et de disque. Pour diverses raisons, les points de contrôle sont également forcés de se produire avant la prochaine heure planifiée, ce qui entraîne une réduction des performances des requêtes.
Pour garder un œil sur la fréquence et l'efficacité des points de contrôle, activez la journalisation des points de contrôle :
log_checkpoints = on
Cela indique à PostgreSQL de consigner les éléments suivants chaque fois qu'un point de contrôle se produit :
2021-01-30 10:05:57.085 UTC [56] LOG: checkpoint starting: immediate force wait
2021-01-30 10:05:57.159 UTC [56] LOG: checkpoint complete: wrote 0 buffers (0.0%); 0 WAL file(s) added, 0 removed, 0 recycled; write=0.000 s, sync=0.000 s, total=0.074 s; sync files=0, longest=0.000 s, average=0.000 s; distance=0 kB, estimate=0 kB
La première ligne contient les drapeaux que le backend a transmis au pointeur de contrôle. Vous pouvez voir que la "force" a provoqué un point de contrôle même s'il n'y avait pas de modifications en attente au point de contrôle. Si "immédiat" n'avait pas été spécifié, le point de contrôle aurait pointé jusqu'à checkpoint_completion_target .
Autres paramètres côté serveur
Il existe quelques autres paramètres que vous pouvez activer dans votre configuration PostgreSQL pour vous aider à diagnostiquer les problèmes :
- track_io_timing - mettre ceci sur on vous permet de voir le temps passé en E/S sur le disque pour chaque requête (combiné avec l'extension pg_stat_statements décrite ci-dessous). Voir les docs à propos d'une mise en garde pour l'activer, mais devrait être sûr sur presque tous les Linux modernes. Il est impossible de voir le coût d'E/S disque d'une requête sans l'activer.
- track_commit_timestamp - mettre ceci sur on peut être utile pour déboguer les retards de réplication et d'autres problèmes liés à la réplication.
Demander des statistiques via pg_stat_statements
L'extension pg_stat_statements est un accessoire indispensable pour tout déploiement PostgreSQL. Il collecte et enregistre des statistiques pour chaque requête exécutée, et les présente sous la forme d'une vue appelée « pg_stat_statements ». Il s'agit d'une extension, ce qui signifie que vous devez l'installer explicitement dans chaque base de données pour laquelle vous souhaitez des données, à l'aide de la commande :
CREATE EXTENSION pg_stat_statements;
Puisque l'extension repose sur un .so , vous devrez le charger à l'aide de shared_preload_libraries :
shared_preload_libraries = 'pg_stat_statements'
Cela nécessite malheureusement un redémarrage du serveur PostgreSQL; alors assurez-vous de le faire avant de passer en direct.
Si vous avez mis à niveau depuis une version précédente de PostgreSQL, assurez-vous de mettre également à niveau votre extension pg_stat_statement en utilisant :
ALTER EXTENSION pg_stat_statements UPDATE;
L'extension pg_stat_statements n'enregistre rien, elle s'utilise en interrogeant la vue du même nom. Pour plus de détails, consultez la documentation officielle.
Demander les plans d'exécution via auto_explain
auto_explain est une autre extension présente dans le noyau PostgreSQL. Il peut enregistrer les plans d'exécution des requêtes lentes. Il suffit de l'ajouter à shared_preload_libraries , et n'a pas besoin d'être installé en tant qu'extension. Il comporte également quelques autres options qui doivent généralement être définies sur des valeurs autres que celles par défaut :
shared_preload_libraries = 'pg_stat_statements,auto_explain'
auto_explain.log_min_duration = 1s
auto_explain.log_analyze = on
auto_explain.log_buffers = on
auto_explain.log_triggers = on
auto_explain.log_timing = on
auto_explain.log_verbose = on
auto_explain.log_format = json
Ce qui précède enregistre le plan d'exécution pour toute requête qui prend plus d'une seconde pour se terminer. Voici un exemple de sortie :
2021-01-30 11:28:25.977 UTC [64] psql postgres postgres 172.17.0.1 LOG: duration: 1.305 ms plan:
{
"Query Text": "SELECT n.nspname as \"Schema\",\n c.relname as \"Name\",\n CASE c.relkind WHEN 'r' THEN 'table' WHEN 'v' THEN 'view' WHEN 'm' THEN 'materialized view' WHEN 'i' THEN 'index' WHEN 'S' TH
EN 'sequence' WHEN 's' THEN 'special' WHEN 'f' THEN 'foreign table' WHEN 'p' THEN 'table' WHEN 'I' THEN 'index' END as \"Type\",\n pg_catalog.pg_get_userbyid(c.relowner) as \"Owner\"\nFROM pg_catalog.pg_class c
\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\nWHERE c.relkind IN ('r','p','v','m','S','f','')\n AND n.nspname <> 'pg_catalog'\n AND n.nspname <> 'information_schema'\n AND
n.nspname !~ '^pg_toast'\n AND pg_catalog.pg_table_is_visible(c.oid)\nORDER BY 1,2;",
"Plan": {
"Node Type": "Sort",
"Parallel Aware": false,
"Startup Cost": 32.93,
"Total Cost": 33.01,
"Plan Rows": 32,
"Plan Width": 224,
"Actual Startup Time": 1.292,
"Actual Total Time": 1.298,
"Actual Rows": 0,
[... lots of text snipped ...]
Pour en savoir plus sur auto_explain, consultez la documentation officielle.
Les extensions pg_stat_statements et auto_explain sont les deux seules options largement prises en charge par PostgreSQL pour la gestion des performances des requêtes et la gestion du plan de requête. Il est utile de se familiariser avec ces deux fonctionnalités et de planifier à l'avance leur utilisation en production.
Nom de l'application
Le nom de l'application est un paramètre côté client et peut généralement être défini dans les DSN ou les chaînes de connexion de style libpq que votre application utilise pour les informations de connexion. De nombreux outils et utilitaires de l'écosystème PostgreSQL comprennent le nom de l'application, et il est utile de le définir sur une valeur significative, par exemple :
application_name = weekly-revenue-report
Ceci serait défini pour chaque application cliente qui se connecte à votre serveur PostgreSQL.