🐘 Signaux d'Alertes & Paramètres Critiques
Objectif : détecter tôt les signaux faibles (cache, I/O, verrous, autovacuum, WAL, mémoire, connexions, requêtes lentes) qui font passer une architecture PostgreSQL mal réglée d’un état « ça tient » à « prod en flammes ».
Cache & I/O : hit ratio & « tables hot »
Suivre le hit ratio global & par table/index, repérer les scans massifs qui font exploser l’I/O quand le cache passe sous 98–99 % en OLTP.
Temp files & sorts/hash qui dérapent
Identifier les requêtes qui débordent en fichiers temporaires, mal dimensionnent work_mem et saturent le disque /tmp.
Verrous, deadlocks & idle in transaction
Suivre les sessions en attente de lock, les deadlocks récurrents et les transactions oubliées qui bloquent tout le monde.
Autovacuum, bloat & wraparound
Vérifier que l’autovacuum suit le rythme, éviter le bloat monstrueux et le risque de transaction ID wraparound qui peut forcer le cluster en read-only.
Mémoire, connexions & pools
Dimensionner shared_buffers, effective_cache_size, work_mem et max_connections, et imposer un pool de connexions.
Runbook « OLTP à l’agonie »
Réflexes quand la latence explose, que les verrous s’accumulent et que la prod est déjà impactée : quoi couper, où regarder, dans quel ordre.
Scripts de diag performance & signaux faibles
Pack de requêtes pour suivre hit ratio, temp files, locks, autovacuum, checkpoints, connexions, requêtes lentes & pg_stat_statements.
Mémoire globale & par session
| Paramètre | Impact | Analyse & Risque |
|---|---|---|
| shared_buffers | Global | Cache principal. ~20-25% RAM totale. Trop haut = double buffering. |
| work_mem | Par Opération | Mémoire pour tri/hash. Risque : Consommation = Valeur x N connexions x N opérations parallèles. |
| maintenance_work_mem | Maintenance | Pour VACUUM, CREATE INDEX. Peut être élevé (1GB+) car peu de sessions concurrentes. |
| temp_buffers | Session | Mémoire pour les tables temporaires. |
| effective_cache_size | Estimation | Indique au planner combien de RAM (OS Cache + Shared) est dispo. ~50-75% RAM. |
Checkpoints, WAL & Durabilité
| Paramètre | Rôle | Analyse |
|---|---|---|
| wal_level | Niveau log | replica (défaut) ou logical (pour réplication logique/CDC). |
| max_wal_size | Taille max | Déclenche un checkpoint si atteint. Augmenter (ex: 16GB) pour lisser les I/O. |
| checkpoint_timeout | Temps max | Déclencheur temps (ex: 15min). Trop court = pics d'écritures fréquents. |
| checkpoint_completion_target | Lissage | Cible 0.9. Étale l'écriture du checkpoint pour éviter de figer les I/O. |
| synchronous_commit | Sécurité | on (sûr) ou off (gain perf, risque perte tx < 1s). |
| wal_compression | Volume | on. Réduit taille WAL et charge réseau réplication. |
Autovacuum & Bloat
| Paramètre | Configuration | Surveillance |
|---|---|---|
| autovacuum | on | Ne jamais désactiver. Surveiller pg_stat_user_tables.n_dead_tup. |
| autovacuum_vacuum_scale_factor | 0.05 à 0.1 | % de lignes mortes déclenchant le nettoyage. Baisser sur grosses tables. |
| autovacuum_max_workers | 3 - 6 | Parallélisme. Attention à ne pas saturer les I/O. |
| autovacuum_vacuum_cost_limit | High (1000+) | Budget I/O. Augmenter pour que le vacuum soit plus agressif et ne prenne pas de retard. |
| vacuum_freeze_min_age | Standard | Gère le "Transaction ID Wraparound". Surveiller les logs d'alerte wraparound. |
Planification des requêtes
| Paramètre | SSD vs HDD | Impact Plan |
|---|---|---|
| random_page_cost | 1.1 (SSD) / 4.0 (HDD) | Coût d'accès aléatoire. Si trop haut sur SSD, le planner privilégiera à tort les Seq Scans. |
| seq_page_cost | 1.0 | Coût de référence accès séquentiel. |
| cpu_tuple_cost | 0.01 | Coût CPU traitement ligne. |
| default_statistics_target | 100 -> 500+ | Précision des stats (histogrammes). Augmenter si les plans sont mauvais sur colonnes complexes. |
| enable_* | on | (nestloop, hashjoin...). À modifier uniquement pour debug ou session locale. |
Processus & Concurrence
| Paramètre | Analyse | Détail |
|---|---|---|
| max_connections | vs pg_stat_activity | Si > 500, utiliser un pooler (PgBouncer). Chaque connexion consomme RAM + Kernel overhead. |
| superuser_reserved_connections | Safety | Slots réservés à l'admin pour intervenir si le serveur est saturé ("Too many clients"). |
| max_worker_processes | Global Limit | Limite globale de processus background (Parallel query + Maintenance). |
| max_parallel_workers | Parallelism | Pool pour les requêtes parallèles. |
I/O, Cache & Temporaire
| Paramètre | Usage | Signe d'alerte |
|---|---|---|
| effective_io_concurrency | SSD/RAID | Capacité à paralléliser les I/O. (200+ sur SSD, 2 sur HDD). |
| temp_file_limit | Protection | Limite taille fichiers temporaires. Empêche une requête de remplir /tmp. |
| bgwriter_lru_maxpages | Flush | A augmenter si trop de buffers nettoyés par les backends (voir pg_stat_bgwriter). |
| full_page_writes | Sécurité | Protège contre corruption (partial write) après crash. Laisser à on. |
Logs, Monitoring & Audit
| Paramètre | Valeur Cible | Analyse |
|---|---|---|
| logging_collector | on | Active la capture des logs vers stderr/csv/syslog. |
| log_min_duration_statement | 200ms - 1s | Capture les requêtes lentes. Clé pour le tuning. |
| log_checkpoints | on | Pour voir la fréquence et distance des checkpoints (tuning max_wal_size). |
| log_lock_waits | on | Affiche les transactions qui attendent un verrou > deadlock_timeout. |
| log_temp_files | 0 (All) | Logue création de fichiers temporaires (disque). Signe de manque de work_mem. |
| log_autovacuum_min_duration | 0 ou 250ms | Surveille l'activité et la durée des autovacuums. |
Paramètres Réseau & Timeouts
| Paramètre | Usage | Problème résolu |
|---|---|---|
| listen_addresses | Configuration IP | Quelles interfaces écoutent (localhost, *, IP spécifique). |
| statement_timeout | Safety Net | Tue les requêtes qui dépassent X ms (ex: 60s). Évite de saturer le serveur. |
| idle_in_transaction_... | Anti-Bloat/Lock | Tue les sessions "zombies" (BEGIN sans COMMIT) qui retiennent les ressources. |
| lock_timeout | Anti-Pileup | Abandonne si l'obtention du verrou est trop longue (évite files d'attente infinies). |
| tcp_keepalives_* | Dead connection | Détecte et nettoie les clients déconnectés "salement" (câble arraché). |
Sécurité, Auth & Chiffrement
| Paramètre / Fichier | Recommandation |
|---|---|
| ssl | on. Obligatoire en production (avec ssl_cert_file). |
| password_encryption | scram-sha-256. Plus sûr que md5. |
| pg_hba.conf | Fichier de contrôle d'accès réseau. Privilégier hostssl. |
| row_security | on. Permet d'utiliser les RLS (Row Level Security). |
Extensions & Shared Preload
| Paramètre | Détail |
|---|---|
| shared_preload_libraries | Liste des libs chargées au démarrage (ex: pg_stat_statements). Modif = Reboot. |
| pg_stat_statements | Indispensable. Configurer .track=all ou top et .max. |
| auto_explain | Logue le plan des requêtes lentes. .log_min_duration, .log_analyze (coûteux). |
| pgaudit | Pour traçabilité fine et compliance (RGPD, SOX). |
Réplication & Haute Dispo
| Paramètre | Contexte | Analyse |
|---|---|---|
| wal_level | Primaire | replica (Streaming standard) ou logical (CDC, Logical rep). |
| max_wal_senders | Primaire | Nombre max de réplicas + backups connectés simultanément. |
| max_replication_slots | Primaire | Slots pour garantir la rétention WAL pour les réplicas. |
| hot_standby | Replica | on. Permet de lire sur le réplica pendant qu'il applique les WALs. |
| max_standby_streaming_delay | Replica | Délai avant d'annuler une requête de lecture qui bloque l'application des WALs (Conflit). |
| synchronous_standby_names | Primaire | Active la réplication synchrone (0 perte de données, mais risque bloquant si réplica down). |
Stratégie de Réglage : OLTP vs OLAP
Profil OLTP (Web/App)
- Objectif : Latence faible, haute concurrence, transactions courtes.
max_parallel_workers...: Faible (0 ou 2). On évite le parallélisme sur des requêtes courtes.random_page_cost: 1.1 (SSD). Favoriser les index.work_mem: Modéré (16MB). Beaucoup de connexions = risque OOM.hit_ratio: Doit être > 99%.
Profil OLAP (Analytics)
- Objectif : Débit, grosses agrégations, peu de connexions.
max_parallel_workers...: Élevé (4+). Utiliser tous les cœurs.work_mem: Élevé (1GB+). Éviter les tris sur disque.default_statistics_target: 1000+. Besoin de stats précises pour les plans complexes.jit: on. Utile pour les requêtes longues.
1.1 Hit ratio global & par base
Où regarder : pg_stat_database (blks_hit, blks_read).
SELECT datname,
blks_hit,
blks_read,
100.0 * blks_hit / NULLIF(blks_hit + blks_read,0) AS hit_ratio
FROM pg_stat_database
ORDER BY hit_ratio ASC;
- En OLTP, viser > 98–99 % sur les bases critiques.
- En workload analytique, < 90–95 % peut être normal mais doit être compris.
Conséquences quand ça passe sous les seuils
- Explosions d’I/O disque, latence qui s’envole sous charge.
- CPU souvent bas, mais serveur « bloqué » en attente d’I/O.
- Souvent le symptôme de :
shared_bufferstrop petit,- requêtes non indexées,
- scans séquentiels massifs sur des tables « hot ».
1.1 bis Hit ratio par table/index
Où regarder : pg_statio_user_tables, pg_statio_user_indexes.
SELECT relname,
heap_blks_read, heap_blks_hit,
100.0 * heap_blks_hit
/ NULLIF(heap_blks_hit + heap_blks_read,0) AS heap_hit_ratio
FROM pg_statio_user_tables
ORDER BY heap_hit_ratio ASC
LIMIT 20;
- Met en avant quelques tables « hot » qui détruisent le cache.
- Compléter par une vue similaire sur
pg_statio_user_indexes. - Point d’entrée pour prioriser les index et les refactorings de requêtes.
1.2 Où regarder & signaux
pg_stat_database:temp_files,temp_bytes.- Logs avec
log_temp_filesactivé pour voir les sorts/hash qui sortent sur disque.
SELECT datname,
temp_files,
pg_size_pretty(temp_bytes) AS temp_bytes
FROM pg_stat_database
ORDER BY temp_bytes DESC;
- Red flag : beaucoup de temp files,
temp_bytesqui grimpe rapidement sur une base OLTP.
Paramètres impliqués
work_mem(par opération, pas par session).maintenance_work_mem(VACUUM, CREATE INDEX, etc.).
Conséquences & cas typiques
- Disque
/tmpou tablespace temporaire saturé → erreurs & crash si plus de place. - Requêtes qui passent de 50 ms à 5 s dès que la charge monte.
- Quelques requêtes mal écrites suffisent à plomber tout le cluster.
- Anti-pattern classique :
work_memtrop bas + grosORDER BY/GROUP BYsur des tables chaudes.
Surveiller les temp files est un excellent indicateur de dimensionnement mémoire et de qualité des requêtes.
2.1 Sessions qui attendent des locks
Où regarder :
pg_stat_activity:state,wait_event_type,wait_event.pg_locks: verrous détenus vs en attente.pg_stat_database.deadlocks.
SELECT a.pid, a.usename, a.application_name,
now() - a.query_start AS age,
a.state, a.wait_event_type, a.wait_event,
l.locktype, l.mode, l.granted,
a.query
FROM pg_stat_activity a
JOIN pg_locks l ON l.pid = a.pid
WHERE a.pid <> pg_backend_pid()
ORDER BY age DESC;
- Red flags :
- beaucoup de lignes avec
granted = falsesur des tables/index critiques, deadlocksqui s’accumulent danspg_stat_database.
- beaucoup de lignes avec
2.2 Transactions longues & idle in transaction
Où regarder : toujours pg_stat_activity.
SELECT pid, usename, application_name,
now() - xact_start AS tx_age,
state, state_change,
wait_event_type, wait_event,
query
FROM pg_stat_activity
WHERE xact_start IS NOT NULL
ORDER BY tx_age DESC;
- Transactions ouvertes depuis des minutes / heures avec
state = 'idle in transaction'. - Transactions READ COMMITTED qui auraient dû être très courtes.
Conséquences
- Vacuum bloqué → tuples morts qui s’accumulent → bloat & temps de scan qui explosent.
- Files d’attente monstrueuses sur les tables chaudes (queues, séquences, journaux d’événements).
- Timeouts applicatifs, erreurs HTTP 500, rollback en cascade.
- Sur réplication logique, ces transactions peuvent bloquer la progression du WAL.
3.1 Autovacuum en retard / désactivé
pg_stat_all_tables:last_vacuum,last_autovacuum,n_dead_tup.pg_stat_user_tablespour les tables applicatives clés.pg_database:age(datfrozenxid).
SELECT relname,
n_live_tup, n_dead_tup,
last_autovacuum,
now() - last_autovacuum AS autovacuum_age
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC
LIMIT 20;
- Tables très actives avec
last_autovacuumtrès ancien etn_dead_tupénorme.
Paramètres fréquemment mal réglés
autovacuum = off(oui, ça existe encore…).autovacuum_vacuum_scale_factortrop haut sur les grosses tables (0.2 par défaut).autovacuum_vacuum_cost_limit/autovacuum_vacuum_cost_delaytrop restrictifs.
Bloat & wraparound
SELECT datname,
age(datfrozenxid) AS age_xid,
current_setting('autovacuum_freeze_max_age')::bigint AS freeze_max_age
FROM pg_database
ORDER BY age_xid DESC;
- Si
age(datfrozenxid)se rapproche trop deautovacuum_freeze_max_age, risque de wraparound. - Postgres peut forcer des vacuums de secours, voire bloquer les nouvelles transactions si on ignore les warnings.
- Bloat massif (tables & index) → tout devient plus lourd à lire, cache saturé, scans 10× plus longs.
5.1 Paramètres mémoire structurels
shared_buffers: souvent ridiculement bas (128–512 Mo) sur des serveurs à 32–64 Go.effective_cache_size: laissé à 4 Go par défaut, ce qui fait croire à l’optimiseur qu’il n’y a presque pas de cache.work_mem,maintenance_work_mem: à calibrer selon les workloads (cf. temp files).
Conséquence : l’optimiseur sous-utilise les index, évite certains hash joins et choisit des plans catastrophiques alors que la machine a de la RAM.
5.2 Trop / pas assez de work_mem
- Trop bas → explosions de
temp_files/temp_bytes. - Trop haut sur un cluster avec beaucoup de connexions → surconsommation RAM, swap, voire OOM killer.
6.1 max_connections & pools
- Anti-pattern :
max_connectionsà 500 ou 1000, aucune couche de pooling. - Beaucoup de sessions
idleouidle in transaction. - Chaque connexion consomme de la mémoire (stack, buffers, structures internes).
SHOW max_connections;
SELECT state, count(*)
FROM pg_stat_activity
GROUP BY state;
Sous pic de charge, la montée en latence devient non linéaire : le noyau lutte pour gérer le nombre de processus, Postgres passe plus de temps à gérer sa propre concurrence qu’à traiter les requêtes.
- Imposer un pool type PgBouncer en mode transaction sur OLTP.
- Abaisser
max_connectionsà un niveau raisonnable et réserver des marges pour l’admin/maintenance.
Étape 1 – Constater & stabiliser
- Confirmer l’impact : latence globale, timeouts appli, erreurs HTTP 500.
- Limiter les entrées :
- Réduire les workers applicatifs / threads web.
- Couper temporairement certains batchs / jobs lourds.
- Regarder CPU vs I/O :
- CPU haut + disques OK → suspicion de locks / plans foireux.
- CPU bas + disques saturés → manque de cache, scans & temp files.
Étape 2 – Localiser le problème
pg_stat_activity: sessions les plus anciennes,wait_event_type,wait_event.pg_locks: objets avec beaucoup de verrous en attente.pg_stat_database:temp_bytes,deadlocks.pg_stat_statements: top requêtes partotal_time.
Étape 3 – Actions à chaud
- Tuer ou suspendre les transactions très longues qui bloquent des verrous stratégiques.
- Stopper les batchs gourmands (exports massifs, reporting ad-hoc) si nécessaire.
- En cas de temp files monstrueux, couper la/les requêtes fautives et relever les plans.
- Sur réplication sync lente : passer temporairement en async si le métier le tolère, le temps de corriger.
Étape 4 – Après l’incident
- Documenter les indicateurs qui ont viré au rouge : hit ratio, temp files, deadlocks, checkpoints, etc.
- Créer des vues / tableaux de bord pour les suivre en continu.
- Lier chaque signal faible à une action : tuning, ajout d’index, refonte de requêtes, modification d’architecture.
1. Cache / I/O : read ratio, temp I/O, tables « hot »
-- Hit ratio global par base
SELECT datname,
blks_hit,
blks_read,
100.0 * blks_hit / NULLIF(blks_hit + blks_read,0) AS hit_ratio
FROM pg_stat_database
ORDER BY hit_ratio ASC;
-- Hit ratio par table « hot »
SELECT relname,
heap_blks_read, heap_blks_hit,
100.0 * heap_blks_hit
/ NULLIF(heap_blks_hit + heap_blks_read,0) AS heap_hit_ratio
FROM pg_statio_user_tables
ORDER BY heap_hit_ratio ASC
LIMIT 30;
-- Temp files & temp_bytes (dimensionnement work_mem)
SELECT datname,
temp_files,
pg_size_pretty(temp_bytes) AS temp_bytes
FROM pg_stat_database
ORDER BY temp_bytes DESC;
2–3. Verrous, deadlocks, autovacuum & bloat
-- Sessions bloquées & verrous en attente
SELECT a.pid, a.usename, a.application_name,
now() - a.query_start AS age,
a.state, a.wait_event_type, a.wait_event,
l.locktype, l.mode, l.granted,
a.query
FROM pg_stat_activity a
JOIN pg_locks l ON l.pid = a.pid
WHERE a.pid <> pg_backend_pid()
ORDER BY age DESC;
-- Deadlocks par base
SELECT datname, deadlocks
FROM pg_stat_database
ORDER BY deadlocks DESC;
-- Tables avec beaucoup de tuples morts (bloat potentiel)
SELECT relname,
n_live_tup, n_dead_tup,
last_autovacuum,
now() - last_autovacuum AS autovacuum_age
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC
LIMIT 30;
-- Risque de wraparound
SELECT datname,
age(datfrozenxid) AS age_xid,
current_setting('autovacuum_freeze_max_age')::bigint AS freeze_max_age
FROM pg_database
ORDER BY age_xid DESC;
7–8. Requêtes lentes, mauvais plans & signaux faibles
-- pg_stat_statements : top requêtes par temps total
SELECT queryid,
left(query,150) AS sample_query,
calls,
round(total_time::numeric,1) AS total_ms,
round(mean_time::numeric,2) AS mean_ms,
shared_blks_hit, shared_blks_read,
temp_blks_read
FROM pg_stat_statements
ORDER BY total_time DESC
LIMIT 30;
-- Requêtes lentes loguées (si log_min_duration_statement configuré)
-- À adapter selon le format de logs + outil d’analyse (pgBadger, etc.).
-- Sessions SQL actuellement les plus lentes / anciennes
SELECT pid, usename, application_name,
now() - query_start AS age,
state, wait_event_type, wait_event,
query
FROM pg_stat_activity
WHERE state <> 'idle'
ORDER BY age DESC
LIMIT 30;
En combinant ces vues avec les indicateurs précédents (hit ratio, temp files, locks, autovacuum, checkpoints), tu obtiens une synthèse des signaux faibles : hit ratio < 98–99 %, temp_files qui explosent, deadlocks réguliers, autovacuum en retard, checkpoints trop fréquents, connexions sans pool, quelques requêtes qui monopolisent tout le temps CPU.
1. Hardware
2. Profil Métier
Mémoire Réservée (Shared Buffers)
4 GB
Cache Système (OS Cache)
12 GB
Memoire par Worker (Work Mem)
16 MB
Max théorique: 1.6 GBNote : Cette configuration est une base solide. Testez toujours avec un outil de benchmark (pgbench) avant la mise en production.
