| name | postgres-expert |
| description | Administration et optimisation PostgreSQL — diagnostic de performance, indexation, partitioning, tuning mémoire, VACUUM, backup/restore, JSONB, réplication. Se déclenche avec "PostgreSQL", "Postgres", "pg_stat", "JSONB", "partitioning", "VACUUM", "pg_dump" |
PostgreSQL Expert
Workflow
1. Recueillir le contexte obligatoire
Avant toute recommandation, collecter :
SELECT version();
SHOW shared_buffers;
SHOW work_mem;
SELECT pg_size_pretty(pg_database_size(current_database()));
- Workload : OLTP (latence < 5 ms), OLAP (scan massif), mixte ?
- RAM totale du serveur, SSD ou HDD, réplication active ?
2. Diagnostiquer les performances
SELECT query, calls, mean_exec_time, total_exec_time
FROM pg_stat_statements
ORDER BY mean_exec_time DESC LIMIT 20;
SELECT relname, n_dead_tup, n_live_tup,
round(n_dead_tup::numeric / nullif(n_live_tup,0)*100,1) AS bloat_pct
FROM pg_stat_user_tables
WHERE n_dead_tup > 1000
ORDER BY n_dead_tup DESC;
EXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT) <votre requête>;
Critères d'action :
| Signal | Seuil | Action |
|---|
mean_exec_time | > 100 ms | Analyser plan + index |
bloat_pct | > 20 % | VACUUM FULL ou pg_repack |
cache_hit_ratio | < 99 % | Augmenter shared_buffers |
seq_scan élevé | > 1 k/min sur grande table | Créer un index |
SELECT sum(heap_blks_hit) / nullif(sum(heap_blks_hit + heap_blks_read),0) AS cache_hit_ratio
FROM pg_statio_user_tables;
3. Indexation — choisir le bon type
| Type | Cas d'usage |
|---|
| B-tree (défaut) | =, <, >, BETWEEN, LIKE 'abc%' |
| GIN | JSONB, tableaux, full-text (tsvector) |
| GiST | Géométrie, plages (tsrange) |
| BRIN | Colonnes ordonnées naturellement (logs, séries temporelles) — très compact |
| Hash | = uniquement, rarement utile vs B-tree |
CREATE INDEX CONCURRENTLY idx_orders_status ON orders(status) INCLUDE (created_at, total);
CREATE INDEX CONCURRENTLY idx_orders_pending ON orders(created_at)
WHERE status = 'pending';
CREATE INDEX CONCURRENTLY idx_events_payload ON events USING GIN (payload jsonb_path_ops);
Toujours utiliser CONCURRENTLY en production (pas de verrou table).
4. Partitioning
Disponible nativement depuis PG 10 (partitioning déclaratif).
CREATE TABLE events (
id BIGSERIAL,
occurred_at TIMESTAMPTZ NOT NULL,
payload JSONB
) PARTITION BY RANGE (occurred_at);
CREATE TABLE events_2025 PARTITION OF events
FOR VALUES FROM ('2025-01-01') TO ('2026-01-01');
SET enable_partition_pruning = on;
Critères de choix :
- RANGE : dates, IDs croissants → purge par
DROP TABLE partition O(1)
- LIST : régions, statuts finis (< 50 valeurs)
- HASH : distribution uniforme sans sémantique de plage
5. Tuning mémoire (postgresql.conf)
Formules de référence pour un serveur dédié PostgreSQL :
shared_buffers = 25% RAM
effective_cache_size = 75% RAM
work_mem = RAM / (max_connections * 4)
maintenance_work_mem = 1–4 GB
wal_buffers = 64 MB
max_wal_size = 2 GB
checkpoint_completion_target = 0.9
max_parallel_workers_per_gather = 4
max_parallel_workers = 8
Appliquer via ALTER SYSTEM SET param = val; + SELECT pg_reload_conf(); (sans redémarrage pour la plupart).
6. VACUUM et autovacuum
VACUUM (ANALYZE, VERBOSE) ma_table;
ALTER TABLE ma_table SET (
autovacuum_vacuum_scale_factor = 0.01,
autovacuum_analyze_scale_factor = 0.005,
autovacuum_vacuum_cost_delay = 2
);
7. Backup et restore
pg_dump -Fc -Z 5 -d mabase -f mabase_$(date +%Y%m%d).dump
pg_restore -d mabase_restore --no-owner --no-acl mabase_20260624.dump
pg_basebackup -h localhost -U replicator -D /mnt/backup/base -Ft -z -Xs -P
archive_mode = on
archive_command = 'cp %p /mnt/wal_archive/%f'
Stratégie recommandée : pgBackRest pour la gestion des WAL, rétention et restore PITR automatisés.
8. JSONB — bonnes pratiques 2026
SELECT payload->>'user_id', payload#>>'{address,city}' FROM events;
SELECT * FROM events WHERE payload @> '{"status": "active"}';
SELECT * FROM events WHERE payload @? '$.items[*] ? (@.price > 100)';
SELECT jsonb_agg(payload) FROM events WHERE occurred_at > now() - interval '1 day';
Garde-fous et anti-patterns
| Anti-pattern | Risque | Alternative |
|---|
SELECT * sur grande table | Scan complet + overhead réseau | Sélectionner les colonnes nécessaires |
Index sur colonne de faible cardinalité (boolean) | Index inutilisé, overhead write | Index partiel ou pas d'index |
VACUUM FULL en production sans pg_repack | Lock exclusif, downtime | pg_repack (online) |
Désactiver autovacuum globalement | Transaction ID wraparound (DB forcée read-only) | Ajuster autovacuum_* par table |
work_mem trop élevé × max_connections | OOM | Calculer par opération, pas par connexion |
EXPLAIN sans ANALYZE | Plan estimé, pas réel | EXPLAIN (ANALYZE, BUFFERS) toujours |
| Connexions persistantes non poolées | Overhead process, verrous | PgBouncer en mode transaction pooling |
Index sans CONCURRENTLY en production | Lock table bloquant les écritures | Toujours CREATE INDEX CONCURRENTLY |
Ignorer pg_stat_replication en réplication | Lag non détecté | Monitorer write_lag, flush_lag, replay_lag |
Checklist opérationnelle rapide
SELECT relname, pg_size_pretty(pg_total_relation_size(relid)) AS total,
pg_size_pretty(pg_relation_size(relid)) AS table_only
FROM pg_stat_user_tables ORDER BY pg_total_relation_size(relid) DESC LIMIT 20;
SELECT pid, wait_event_type, wait_event, state, left(query,80)
FROM pg_stat_activity WHERE wait_event IS NOT NULL AND state != 'idle';
SELECT client_addr, write_lag, flush_lag, replay_lag FROM pg_stat_replication;