PostgreSQL vs Percona Server : diagnostiquer les ralentissements — qui offre la meilleure visibilité

Cet article vous a aidé ?

Le pager sonne. L’application est “up” mais tout donne l’impression de nager dans la mélasse. La latence des API explose, les files d’attente se remplissent, et votre PDG découvre le bouton d’actualisation.
Vous avez deux problèmes : un ralentissement, et une incertitude. La seconde est pire.

PostgreSQL et Percona Server (compatible MySQL) peuvent tous deux être rapides à grande échelle. La différence, pendant un incident, tient à la rapidité avec laquelle ils vous permettent de prouver ce qui se passe réellement :
CPU, I/O, verrous, pression de checkpoint, défauts du buffer pool, dette d’autovacuum, blocages de réplication, mauvais plans, ou “quelqu’un a changé un petit truc”.

Le point clé : la visibilité est une fonctionnalité produit

Si vous tenez à la réponse aux incidents, PostgreSQL vous donne en général plus de “vérité explicable” dès l’installation : événements d’attente, statistiques par requête robustes avec
pg_stat_statements, et une culture consistant à exposer l’état interne via du SQL simple. Vous pouvez rapidement arriver à “nous sommes bloqués par des verrous” ou “nous sommes I/O limités en lecture”.

Le super-pouvoir de Percona Server est de prendre l’histoire d’instrumentation de MySQL (qui peut être excellente mais parfois nécessite un peu de doigté) et de la rendre plus opérationnelle :
métriques supplémentaires, bons réglages par défaut pour l’observabilité, et une chaîne d’outils éprouvée (Performance Schema, schéma sys, slow log, pt-query-digest)
qui est brutalement efficace en production.

Alors, lequel offre la meilleure visibilité ? En pratique :

  • PostgreSQL l’emporte lorsque vous avez besoin d’un récit unifié couvrant l’exécution des requêtes, les attentes, les verrous, les autovacuums et le comportement des plans — surtout avec un accès SQL uniquement.
  • Percona Server l’emporte lorsque vous voulez un riche écosystème MySQL optimisé pour l’exploitation, une instrumentation renforcée, et des workflows éprouvés “prendre le slow log, le digérer, corriger les principaux coupables”.

La vérité inconfortable : aucun des deux ne sert à grand-chose si vous n’aviez pas activé les bons réglages avant l’incident. La visibilité n’est pas une ambiance. C’est un fichier de configuration et une politique de rétention.

Faits intéressants et contexte historique (8 éléments importants)

  1. La lignée de PostgreSQL est orientée recherche. Il descend de POSTGRES (années 1980), et ça se voit : l’état interne tend à être exposé dans des tables/vues qui semblent faites pour le débogage.
  2. L’histoire de performance initiale de MySQL misait sur la simplicité. Pendant des années, “c’est rapide” signifiait parfois “c’est opaque”, et l’écosystème a compensé avec des logs et des outils externes.
  3. Performance Schema a été un tournant. Le Performance Schema de MySQL a évolué vers un vrai cadre d’instrumentation ; il est puissant, mais pas toujours activé/conservé correctement par défaut.
  4. Percona a popularisé le “MySQL opérationnel”. Percona Server et ses outils ont fait du diagnostic des problèmes MySQL en production moins un art et plus une checklist.
  5. pg_stat_statements a changé la façon de travailler des équipes Postgres. Une fois que vous disposez de statistiques de requêtes normalisées, les “top requêtes” deviennent des données, pas un débat.
  6. Les événements d’attente dans PostgreSQL sont de première classe. Le reporting des wait events de PostgreSQL (et les vues associées) fournit un récit net de “sur quoi sommes-nous en attente”.
  7. Le buffer pool d’InnoDB est à la fois une bénédiction et un piège. Bien dimensionné, tout semble parfait. Mal dimensionné, vous courrez après des “I/O mystérieux” pendant des heures.
  8. Autovacuum est le collecteur d’impôts de PostgreSQL. Vous pouvez l’ignorer un moment, mais il finira par recouvrer sa dette — avec intérêts — via le bloat, les parcours de table et des pics de latence brutaux.

Ce que signifie « meilleure visibilité » lors d’incidents réels

La visibilité n’est pas “j’ai un tableau de bord”. La visibilité est la capacité à répondre rapidement à quatre questions, avec des preuves :

  • Qu’est-ce qui a changé ? Forme des requêtes, volume de données, plan, configuration, schéma, mix de charge, comportement de l’hôte, réseau.
  • Où va le temps ? CPU, lectures disque, écritures disque, attentes de verrous, fsync, réplication, checkpoints, vacuum/purge, contention.
  • Qui est responsable ? Quels motifs de requêtes, quels utilisateurs, quelles tables/index, quel chemin de code, quel job.
  • Quelle est la mitigation la plus sûre ? Tuer, limiter, ajouter un index, changer de plan, ajuster la mémoire, réduire la durabilité, mettre en pause des jobs, basculer.

PostgreSQL et Percona Server peuvent tous deux répondre à ces questions. La vraie différence est le nombre de étapes nécessaires et la fréquence à laquelle vous êtes obligé de deviner.

Deux métriques importent plus que votre fierté :

  • Temps jusqu’à la première hypothèse crédible. Pas “c’est la base de données”, mais “c’est la contention de verrous sur la table X causée par le motif de requête Y.”
  • Temps jusqu’à une mitigation sûre. La correction n’a pas à être parfaite ; elle doit arrêter l’hémorragie sans corrompre les données ni provoquer une seconde panne.

PostgreSQL : où la lumière est vive (et où elle ne l’est pas)

Forces de Postgres : introspection SQL-native et vérité des waits

PostgreSQL expose beaucoup de “ce que le moteur fait” via des vues système. En incident, cela signifie que vous pouvez souvent rester en SQL et obtenir des réponses :
sessions, verrous, événements d’attente, texte des requêtes, chronométrage I/O (si activé), progression des vacuum, réplication et statistiques de requêtes.

Les wait events sont la fonctionnalité sous-estimée. Si des sessions sont coincées sur des attentes de type Lock, vous passez immédiatement de “pourquoi c’est lent” à “qui bloque qui”.
Si les waits indiquent une pression IO ou LWLock, vous savez si c’est le disque, un brassage du cache ou une contention interne.

Angles morts de Postgres : il faut quand même préactiver les bonnes options

PostgreSQL est généreux en introspection, mais il ne conservera pas l’historique par requête indéfiniment à moins que vous ne le lui indiquiez, et il ne capturera pas chaque instruction lente à moins que la journalisation et les statistiques ne soient configurées de façon sensée.

  • pg_stat_statements doit être installé et dimensionné. Trop petit et les requêtes les plus chaudes évinceront les preuves.
  • L’instrumentation du timing I/O a un coût. Vous pouvez l’activer, mais ne prétendez pas que c’est gratuit sur des disques très sollicités.
  • La ventilation des waits au niveau requête n’est pas toujours assez granulaire. Vous saurez sur quoi un backend attend, mais pas toujours une attribution parfaite par requête sans outils supplémentaires.

Expérience pratique de débogage sur Postgres

Être de garde avec Postgres ressemble souvent à : « Exécutez quelques requêtes SQL et le moteur avoue. » Ce n’est pas toujours vrai, mais c’est suffisamment fréquent pour que les équipes développent des automatismes autour de ça.

Le mode d’échec le plus courant n’est pas le manque de visibilité — c’est s’y noyer. Si vous n’avez pas de workflow, vous finissez par fixer
pg_stat_activity comme si ça allait cligner deux fois pour dire “le disque est plein”.

Percona Server : où il brille (et ses angles morts)

Forces de Percona : écosystème MySQL, métriques enrichies et outils d’incident

Percona Server est compatible MySQL, mais optimisé pour la production : meilleurs réglages d’observabilité par défaut, variables d’état supplémentaires, et une culture du “voici le réglage, voici la métrique, voici la méthode.”
Le workflow Percona toolkit — slow log + digest — reste l’un des chemins les plus rapides de “c’est lent” à “voici les empreintes des requêtes les plus coûteuses et leur impact.”

Performance Schema peut être spectaculaire lorsqu’il est bien configuré : résumés de statements, événements d’attente, instrumentation des verrous, stages, et metadata locks.
Combiné aux vues du schéma sys, vous obtenez un récit proche de l’histoire des waits de Postgres — juste avec plus de zones piégeuses.

Angles morts de Percona : l’instrumentation est optionnelle, et les options manquent souvent à 2h du matin

La visibilité MySQL/Percona a historiquement dépendu du fait que vous l’ayez activée. Performance Schema peut être désactivé, dimensionné trop petit, ou configuré pour ne pas conserver ce dont vous avez besoin.
Les slow logs peuvent être désactivés pour “économie de disque”, et vous vous retrouvez alors à déboguer à l’intuition et à la prière.

Il y a aussi une subtilité : MySQL propose plusieurs manières d’observer des choses similaires (slow log, Performance Schema, tables INFORMATION_SCHEMA, variables d’état).
C’est de la flexibilité — jusqu’à ce que ça ne le soit plus. En incident, vous voulez un workflow canonique.

Blague #1 : Une requête lente, c’est comme une réunion d’entreprise — personne ne sait pourquoi elle existe, mais tout le monde attend un verrou.

Playbook de diagnostic rapide (vérifier 1er/2e/3e)

C’est le playbook que je veux avoir collé au mur à côté du panneau “ne pas redémarrer la base de données”.

Premier : est-ce la base ou la machine ?

  • Vérifier la saturation de l’hôte : CPU, iowait, pression mémoire, swap, profondeur des files disque.
  • Vérifier la latence du stockage : read/write await, pics de fsync, périphériques bloqués, systèmes de fichiers pleins.
  • Vérifier le réseau : retransmissions, saturation, latence étrange vers les clients.

Décision : si l’hôte est en feu, réparez l’hôte d’abord. Aucun tuning de requête ne compense un disque qui met 80ms par lecture.

Deuxième : la charge est-elle bloquée (verrous) ou lente (ressources) ?

  • Postgres : événements d’attente + vues de verrous + pg_stat_activity.
  • Percona : waits du Performance Schema + statut des verrous/transactions InnoDB + processlist.

Décision : si c’est bloqué, identifiez le bloqueur et le type de verrou. Puis décidez de tuer, limiter ou changer le mode d’écriture.

Troisième : identifier les empreintes de requêtes principales qui causent la douleur

  • Postgres : pg_stat_statements par temps total et temps moyen ; corréler avec les comptes d’exécution et l’I/O.
  • Percona : slow log + pt-query-digest ; résumés de statements du Performance Schema.

Décision : choisissez le plus petit changement sûr qui réduit le temps total : ajouter un index, réécrire la requête, corriger la paramétrisation, mettre à jour les statistiques, réduire la concurrence, ou désactiver temporairement un job batch.

Quatrième : confirmer que ce n’est pas la maintenance ou la durabilité

  • Postgres : fréquence des checkpoints, activité d’autovacuum, retard d’application de la réplication.
  • Percona : retard de purge/longueur de history list, âge des checkpoints, stalls de flush, lag de réplication.

Décision : si la maintenance est en cause, choisissez entre performance et dette. Payer maintenant (vacuum/purge/optimisation), ou payer plus tard avec des intérêts pires.

Tâches pratiques (commandes, sorties, décisions)

Ce sont des tâches réelles que vous pouvez exécuter pendant un incident. Chacune inclut : la commande, ce que la sortie signifie, et la décision à prendre.
Je mélange contrôles au niveau hôte et contrôles au niveau base car “la base est lente” est souvent “le disque est triste”.

Task 1 — Instantané CPU et iowait de l’hôte

cr0x@server:~$ mpstat -P ALL 1 3
Linux 6.1.0 (db01)  12/30/2025  _x86_64_ (32 CPU)

11:02:10 AM  CPU   %usr  %nice   %sys %iowait  %irq  %soft  %steal  %guest  %gnice  %idle
11:02:11 AM  all  22.10   0.00   6.30   18.40  0.00   0.60    0.00    0.00    0.00  52.60
11:02:11 AM    7  85.00   0.00  10.00    0.00  0.00   0.00    0.00    0.00    0.00   5.00

Signification : un %iowait élevé sur all suggère que la latence du stockage bride la progression ; un CPU chaud suggère un hotspot mono‑thread (compression, requête unique ou flush en arrière‑plan).

Décision : si l’iowait est élevé, pivotez vers des vérifications du stockage avant de commencer à réécrire des requêtes.

Task 2 — Latence disque et profondeur de file

cr0x@server:~$ iostat -x 1 3
Linux 6.1.0 (db01)  12/30/2025  _x86_64_ (32 CPU)

Device            r/s     w/s   r_await   w_await   aqu-sz  %util
nvme0n1         820.0   210.0     18.40     22.10    14.20  98.50

Signification : r_await/w_await en dizaines de ms et %util proche de 100% : le périphérique est saturé. aqu-sz élevé : file profonde, les requêtes s’accumulent.

Décision : limiter la charge (pool de connexions, jobs batch), confirmer qu’il n’y a pas de checkpoint/flush incontrôlé, envisager un basculement si le stockage du réplica est plus sain.

Task 3 — Pression mémoire et swap

cr0x@server:~$ free -h
               total        used        free      shared  buff/cache   available
Mem:           128Gi       120Gi       1.2Gi       2.0Gi       6.8Gi       3.5Gi
Swap:           16Gi       9.5Gi       6.5Gi

Signification : l’utilisation du swap sur un hôte de base de données est rarement “acceptable”. Cela signifie généralement que votre cache est évincé et que votre latence va devenir une œuvre d’art interprétative.

Décision : réduire l’empreinte mémoire (baisser le nombre de connexions, corriger work_mem/sort buffers monstrueux, vérifier la faim du cache OS), et planifier un redémarrage seulement si vous pouvez le faire en toute sécurité.

Task 4 — PostgreSQL : trouver les requêtes actives et leurs waits

cr0x@server:~$ psql -X -c "SELECT pid, usename, state, wait_event_type, wait_event, now()-query_start AS age, left(query,120) AS query FROM pg_stat_activity WHERE state <> 'idle' ORDER BY age DESC LIMIT 10;"
 pid  | usename | state  | wait_event_type |  wait_event   |   age    |                         query
------+--------+--------+-----------------+---------------+----------+----------------------------------------------------------
 4821 | app    | active | Lock            | relation      | 00:01:42 | UPDATE orders SET status=$1 WHERE id=$2
 4977 | app    | active | IO              | DataFileRead  | 00:01:10 | SELECT * FROM orders WHERE customer_id=$1 ORDER BY created

Signification : la première session attend un verrou de relation ; la seconde est bloquée sur des lectures de fichiers de données. Ce sont deux pistes d’incident différentes : contention de verrous vs I/O limité.

Décision : si vous voyez des attentes de type Lock, allez voir les bloqueurs. Si vous voyez des waits IO, examinez le comportement du disque et du cache.

Task 5 — PostgreSQL : afficher bloqueurs et sessions bloquées

cr0x@server:~$ psql -X -c "SELECT blocked.pid AS blocked_pid, blocked.query AS blocked_query, blocker.pid AS blocker_pid, blocker.query AS blocker_query FROM pg_locks bl JOIN pg_stat_activity blocked ON blocked.pid=bl.pid JOIN pg_locks kl ON kl.locktype=bl.locktype AND kl.database IS NOT DISTINCT FROM bl.database AND kl.relation IS NOT DISTINCT FROM bl.relation AND kl.page IS NOT DISTINCT FROM bl.page AND kl.tuple IS NOT DISTINCT FROM bl.tuple AND kl.virtualxid IS NOT DISTINCT FROM bl.virtualxid AND kl.transactionid IS NOT DISTINCT FROM bl.transactionid AND kl.classid IS NOT DISTINCT FROM bl.classid AND kl.objid IS NOT DISTINCT FROM bl.objid AND kl.objsubid IS NOT DISTINCT FROM bl.objsubid AND kl.pid != bl.pid JOIN pg_stat_activity blocker ON blocker.pid=kl.pid WHERE NOT bl.granted AND kl.granted LIMIT 5;"
 blocked_pid |             blocked_query              | blocker_pid |              blocker_query
------------+----------------------------------------+------------+------------------------------------------
       4821 | UPDATE orders SET status=$1 WHERE id=$2 |       4602 | ALTER TABLE orders ADD COLUMN note text

Signification : un ALTER TABLE bloque les mises à jour de production. Ce n’est pas une “requête lente”, c’est un échec de coordination DDL.

Décision : décider de terminer le bloqueur, reporter la modification de schéma, ou rediriger les écritures. Si vous le tuez, confirmez le temps de rollback et l’impact.

Task 6 — PostgreSQL : top requêtes par temps total (nécessite pg_stat_statements)

cr0x@server:~$ psql -X -c "SELECT calls, round(total_exec_time::numeric,1) AS total_ms, round(mean_exec_time::numeric,1) AS mean_ms, rows, left(query,120) AS query FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 5;"
 calls | total_ms | mean_ms |  rows  |                         query
-------+----------+---------+--------+----------------------------------------------------------
 92000 | 540000.0 |     5.9 | 120000 | SELECT * FROM orders WHERE customer_id=$1 ORDER BY created_at DESC LIMIT $2
  4100 | 210000.0 |    51.2 |   4100 | UPDATE orders SET status=$1 WHERE id=$2

Signification : la première requête domine le temps total à cause du volume. La seconde est individuellement plus lente mais pas nécessairement le principal coupable.

Décision : optimisez l’empreinte dominante d’abord (index, index couvrant, réduire les colonnes sélectionnées, meilleure pagination), puis traitez les latences de queue.

Task 7 — PostgreSQL : vérifier la dette d’autovacuum et les signaux de bloat

cr0x@server:~$ psql -X -c "SELECT relname, n_dead_tup, n_live_tup, last_autovacuum, vacuum_count, autovacuum_count FROM pg_stat_user_tables ORDER BY n_dead_tup DESC LIMIT 5;"
  relname  | n_dead_tup | n_live_tup |     last_autovacuum     | vacuum_count | autovacuum_count
-----------+------------+------------+-------------------------+--------------+------------------
 orders    |   8420000  |   21000000 | 2025-12-29 02:14:11+00  |            1 |               14
 events    |   3200000  |    9000000 |                         |            0 |                0

Signification : beaucoup de tuples morts et un autovacuum manquant sur events suggèrent du bloat et une mauvaise couverture de la visibility map, ce qui implique plus d’I/O et des scans plus lents.

Décision : investiguer les réglages d’autovacuum, les transactions longues qui empêchent le nettoyage, et planifier des vacuum ciblés (avec précaution en production).

Task 8 — PostgreSQL : vérifier la pression de checkpoint

cr0x@server:~$ psql -X -c "SELECT checkpoints_timed, checkpoints_req, round(checkpoint_write_time/1000.0,1) AS write_s, round(checkpoint_sync_time/1000.0,1) AS sync_s, buffers_checkpoint FROM pg_stat_bgwriter;"
 checkpoints_timed | checkpoints_req | write_s | sync_s | buffers_checkpoint
------------------+-----------------+---------+--------+-------------------
             1200 |            3400 |  9800.2 |  420.1 |          81233421

Signification : beaucoup de checkpoints demandés et des buffers de checkpoint énormes peuvent corréler avec des pics de latence périodiques, surtout sur des charges d’écriture élevées.

Décision : ajuster les paramètres de checkpoint (intervalle, completion_target), et vérifier que le stockage peut absorber le motif d’écriture. Ne “corrigez” pas cela en transformant la durabilité en rumeur.

Task 9 — Percona/MySQL : voir qui tourne et qui attend

cr0x@server:~$ mysql -e "SHOW FULL PROCESSLIST\G"
*************************** 1. row ***************************
     Id: 31244
   User: app
   Host: 10.0.12.34:52210
     db: prod
Command: Query
   Time: 87
  State: Waiting for table metadata lock
   Info: ALTER TABLE orders ADD COLUMN note TEXT
*************************** 2. row ***************************
     Id: 31251
   User: app
   Host: 10.0.12.18:50122
     db: prod
Command: Query
   Time: 83
  State: updating
   Info: UPDATE orders SET status='shipped' WHERE id=?

Signification : les metadata locks peuvent figer toute une application sur MySQL/Percona. Si vous voyez “Waiting for table metadata lock”, vous n’êtes pas en train d’optimiser des requêtes — vous êtes en train de résoudre une impasse.

Décision : trouver le détenteur du verrou et tuer ou attendre ; puis corriger votre processus de migration (outils DDL en ligne, étapes plus petites, hors‑pic).

Task 10 — Percona/MySQL : instantané des transactions et verrous InnoDB

cr0x@server:~$ mysql -e "SHOW ENGINE INNODB STATUS\G" | sed -n '1,120p'
=====================================
2025-12-30 11:04:21 INNODB MONITOR OUTPUT
=====================================
TRANSACTIONS
------------
Trx id counter 845112334
Purge done for trx's n:o < 845100000 undo n:o < 0 state: running
History list length 987654
...
LATEST DETECTED DEADLOCK
------------------------
*** (1) TRANSACTION:
TRANSACTION 845112100, ACTIVE 12 sec updating or deleting
...

Signification : une “History list length” très grande indique que la purge est en retard, souvent à cause de transactions longues. Cela augmente la rétention des undo et peut nuire aux performances.
Les deadlocks sont normaux ; les motifs répétés ne le sont pas.

Décision : identifier les transactions longues (clients en sommeil, jobs batch), corriger le périmètre transactionnel de l’application, et envisager d’ajuster le comportement purge/undo si c’est chronique.

Task 11 — Percona/MySQL : statements principaux via Performance Schema

cr0x@server:~$ mysql -e "SELECT DIGEST_TEXT, COUNT_STAR, ROUND(SUM_TIMER_WAIT/1000000000000,2) AS total_s, ROUND(AVG_TIMER_WAIT/1000000000000,4) AS avg_s FROM performance_schema.events_statements_summary_by_digest ORDER BY SUM_TIMER_WAIT DESC LIMIT 5\G"
*************************** 1. row ***************************
DIGEST_TEXT: SELECT * FROM orders WHERE customer_id = ? ORDER BY created_at DESC LIMIT ?
 COUNT_STAR: 92510
   total_s: 532.44
    avg_s: 0.0058

Signification : même idée que pg_stat_statements : empreintes normalisées avec comptes et temps. Si cette table est vide, vous n’avez probablement pas configuré la rétention de Performance Schema.

Décision : optimiser le digest principal, et s’assurer que les consommateurs du Performance Schema sont activés et dimensionnés pour votre charge.

Task 12 — Percona/MySQL : digest du slow log (rapide pour savoir “qui nous a fait mal”)

cr0x@server:~$ pt-query-digest /var/log/mysql/mysql-slow.log --limit=5
# 200ms user time, 20ms system time, 30.00M rss, 120.00M vsz
# Rank Query ID           Response time Calls R/Call  V/M   Item
# ==== ================== ============= ===== ======= ===== ====
#    1 0xA1B2C3D4E5F6A7B8  540.2311 50.2% 92000 0.0059  0.02  SELECT orders
#    2 0x1111222233334444  210.1145 19.5%  4100 0.0512  0.10  UPDATE orders

Signification : vous obtenez immédiatement une liste classée de ce qui consomme la latence. C’est pour cela que le slow log est toujours pertinent en 2025 : il fonctionne sous pression.

Décision : corriger le rang #1 d’abord sauf si vous pouvez prouver que le rang #2 provoque une amplification de verrous ou des défaillances en cascade.

Task 13 — PostgreSQL : examiner un plan avec le chronométrage réel

cr0x@server:~$ psql -X -c "EXPLAIN (ANALYZE, BUFFERS, VERBOSE) SELECT * FROM orders WHERE customer_id=42 ORDER BY created_at DESC LIMIT 50;"
 Limit  (cost=0.43..25.88 rows=50 width=312) (actual time=120.114..120.220 rows=50 loops=1)
   Buffers: shared hit=120 read=1840
   ->  Index Scan using orders_customer_created_idx on public.orders  (cost=0.43..5120.33 rows=10000 width=312) (actual time=0.080..119.900 rows=50 loops=1)
         Index Cond: (orders.customer_id = 42)
 Planning Time: 0.320 ms
 Execution Time: 120.300 ms

Signification : beaucoup de buffers en read implique des misses de cache ; l’index existe mais déclenche néanmoins des I/O (peut‑être dû à une mauvaise localité, un cache froid, ou des lignes trop larges).

Décision : envisager un index couvrant, réduire les colonnes sélectionnées, améliorer le hit rate du cache (dimensionnement mémoire), ou traiter la latence du stockage.

Task 14 — Percona/MySQL : EXPLAIN et analyse du plan

cr0x@server:~$ mysql -e "EXPLAIN SELECT * FROM orders WHERE customer_id=42 ORDER BY created_at DESC LIMIT 50\G"
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: orders
   partitions: NULL
         type: ref
possible_keys: idx_customer_created
          key: idx_customer_created
      key_len: 8
          ref: const
         rows: 10000
     filtered: 100.00
        Extra: Using where; Using filesort

Signification : “Using filesort” avec un ORDER BY peut signifier que l’index ne satisfait pas le tri, ou que l’optimiseur a choisi un autre chemin d’accès pour l’ordre. C’est un multiplicateur classique de latence.

Décision : ajuster l’ordre de l’index, s’assurer que l’ORDER BY correspond à l’index, et vérifier les statistiques/cardinalités.

Task 15 — Vérification du lag de réplication (Postgres)

cr0x@server:~$ psql -X -c "SELECT application_name, state, write_lag, flush_lag, replay_lag FROM pg_stat_replication;"
 application_name |   state   | write_lag | flush_lag | replay_lag
-----------------+-----------+-----------+-----------+------------
 replica01        | streaming | 00:00:00  | 00:00:02  | 00:00:15

Signification : le replay lag indique que le réplica applique les WAL lentement. Pendant la réponse à un incident, cela compte pour la sécurité d’un basculement et les attentes read-after-write.

Décision : si le lag augmente, évitez de basculer sur ce réplica à moins d’accepter plus de perte/latence. Investiguer I/O/CPU sur le réplica.

Task 16 — Vérification du lag de réplication (Percona/MySQL)

cr0x@server:~$ mysql -e "SHOW REPLICA STATUS\G" | egrep 'Replica_IO_Running|Replica_SQL_Running|Seconds_Behind_Source|Last_SQL_Error'
Replica_IO_Running: Yes
Replica_SQL_Running: Yes
Seconds_Behind_Source: 48
Last_SQL_Error:

Signification : 48 secondes de retard n’est pas forcément fatal, mais c’est un avertissement pour le basculement et pour les attentes lecture‑après‑écriture.

Décision : si le lag corrèle avec le stockage ou les stalls de verrous, corrigez le goulot sous‑jacent ; ne “réglez” pas ça en redémarrant la réplication à l’aveugle.

Blague #2 : Rien n’accélère une analyse de cause racine comme de réaliser que le réglage de debug “temporaire” est activé depuis le dernier trimestre.

Trois mini-récits d’entreprise issus du terrain

Mini-récit 1 : l’incident provoqué par une mauvaise hypothèse

Une entreprise de taille moyenne exploitait un SaaS multi‑tenant sur PostgreSQL. Ils avaient un modèle mental propre : “Les lectures sont bon marché, les écritures sont coûteuses.”
Donc lorsqu’ils ont vu des pics de latence, ils ont supposé qu’un job d’écriture en arrière‑plan saturait le disque.

Le de garde a ouvert les métriques hôte : l’utilisation disque était élevée, certes. Mais pg_stat_activity montrait que la douleur la plus forte venait des requêtes de lecture.
Les wait events n’étaient pas “IO DataFileRead” partout non plus — de nombreuses sessions étaient coincées sur Lock, et les requêtes bloquées étaient des lectures.

L’erreur d’hypothèse était subtile : ils croyaient que les lectures ne bloquent pas. Dans PostgreSQL, les lectures peuvent tout à fait attendre si elles tentent d’accéder à un objet verrouillé par un DDL,
ou si elles ont besoin d’un buffer épinglé par autre chose, ou si elles s’alignent derrière une file d’attente de verrous. Une migration de schéma avait ajouté un index avec un mode de verrouillage qu’ils n’avaient pas complètement compris.

La correction n’a pas été “ajouter des IOPS”. La correction a été opérationnelle : changer le processus de migration pour utiliser des patterns online plus sûrs,
programmer les DDL invasifs en heures creuses, et implémenter une étape de pré‑validation qui vérifie le risque d’acquisition de verrous et le temps d’exécution attendu.

La leçon : la façon la plus rapide de perdre une heure est de déboguer le problème que vous attendiez au lieu de celui que vos waits rapportent.

Mini-récit 2 : l’optimisation qui a mal tourné

Une autre équipe exploitait Percona Server avec un buffer pool agressivement réglé et un fier “nous n’atteignons jamais le disque”.
Ils ont augmenté le buffer pool “au maximum possible” après une hausse de trafic. Les graphiques avaient l’air héroïques pendant une semaine.

Puis est venu un incident en mode ralenti : la latence tail a augmenté, mais le débit n’a pas bougé. Le CPU semblait correct. Le disque semblait “occupé mais pas délirant”.
Performance Schema a montré plus de temps en flushing InnoDB et en étapes d’I/O fichier qu’auparavant, et le slow log a commencé à rapporter des rafales de stalls d’écriture.

Le retour de manivelle était la famine mémoire OS. Avec le buffer pool absorbant presque tout, le cache de pages du noyau et le cache des métadonnées du système de fichiers ont souffert,
et le comportement des flushs en arrière‑plan est devenu plus erratique. La machine a commencé à swapper sous des pics rares — juste ce qu’il fallait pour ruiner le 99e centile.

La correction a été embarrassante d’ennui : réduire un peu le buffer pool, laisser de l’air au noyau, limiter la concurrence de connexions, et lisser le comportement de flush.
L’“optimisation” avait amélioré les moyennes tout en cassant la queue. La production vit dans la queue.

La leçon : vous ne pouvez pas gagner en prenant toute la mémoire pour la base. Le noyau a son mot à dire, et il vote avec la latence.

Mini-récit 3 : la pratique ennuyeuse mais correcte qui a sauvé la mise

Une grande équipe d’entreprise exploitait à la fois PostgreSQL et Percona sur différents produits.
Leur travail de fiabilité le plus précieux n’était pas un tuning exotique ; c’était l’instrumentation standardisée avant les incidents :
journalisation des requêtes lentes avec des seuils sensés, agrégation des statements (pg_stat_statements et digests Performance Schema), et une rétention cohérente.

Un après‑midi, un déploiement applicatif a provoqué un ralentissement soudain de la BD. Personne n’a discuté de qui était coupable parce que les données étaient déjà là.
Dans Postgres, le total time de l’empreinte top a doublé avec le même nombre d’appels ; EXPLAIN (ANALYZE, BUFFERS) montrait des lectures supplémentaires.
Dans Percona, pt-query-digest a signalé une forme de requête modifiée et un nouveau comportement “Using filesort”.

La pratique “ennuyeuse” était une vérification pré‑merge qui enregistrait les plans de requêtes pour les endpoints critiques et les comparait après les changements.
Cela a rendu la décision de rollback évidente. Ils ont revert rapidement, puis corrigé la requête et ajouté l’index manquant dans un déploiement contrôlé.

L’incident a été court non pas parce qu’ils étaient des génies, mais parce qu’ils avaient des reçus.

Erreurs courantes : symptôme → cause racine → correctif

Voici des modes de défaillance qui reviennent en production, avec des corrections spécifiques. C’est la section à lire quand votre café tremble.

1) Symptom : ralentissement global soudain, nombreuses sessions “en attente”

  • Cause racine (Postgres) : DDL bloquant ou file d’attente de verrous derrière une transaction longue.
  • Fix : identifier le bloqueur via pg_locks/pg_stat_activity, terminer ou laisser finir ; changer la stratégie de migration.
  • Cause racine (Percona) : contention de metadata lock (MDL), souvent due à un DDL.
  • Fix : trouver le propriétaire du verrou dans processlist/Performance Schema ; tuer la session ; utiliser des pratiques de changement de schéma en ligne.

2) Symptom : pics de latence périodiques toutes les quelques minutes

  • Cause racine (Postgres) : tempêtes de checkpoint (trop fréquentes ou trop abruptes), parfois combinées à un stockage lent.
  • Fix : ajuster l’intervalle de checkpoint et completion_target ; s’assurer que WAL et données sont sur un stockage adapté ; vérifier le comportement du background writer.
  • Cause racine (Percona) : tempêtes de flush, pression d’âge de checkpoint, rafales de fsync.
  • Fix : ajuster le flushing InnoDB et la taille du redo log ; lisser la charge d’écriture ; confirmer la latence d’écriture du périphérique.

3) Symptom : les requêtes de lecture ralentissent sur des jours/semaines

  • Cause racine (Postgres) : bloat de table/index dû à la dette d’autovacuum ou à des transactions longues empêchant le nettoyage.
  • Fix : trouver les tuples morts et l’historique de vacuum ; corriger les transactions longues ; ajuster les seuils d’autovacuum par table ; planifier des fenêtres de maintenance.
  • Cause racine (Percona) : fragmentation et statistiques obsolètes, ou churn du buffer pool dû à la croissance des données.
  • Fix : mettre à jour les statistiques ; ajuster le buffer pool ; envisager rebuild/optimize avec précaution ; corriger l’usage des requêtes/index.

4) Symptom : “Le CPU est bas mais les requêtes sont lentes”

  • Cause racine : attente I/O, attentes de verrous, ou contention interne. Un CPU bas n’est pas un compliment ; c’est un indice.
  • Fix : vérifier les waits (wait_event_type Postgres ; stages/waits MySQL), puis la latence stockage, puis les limites de concurrence.

5) Symptom : le lag de réplication augmente pendant le pic, puis se résorbe

  • Cause racine : le replica est I/O bound pour appliquer les changements ; ou des transactions longues/gros batches provoquent des stalls d’application.
  • Fix : ajuster le parallélisme d’application si approprié, réduire les grosses transactions, déporter les lectures lourdes du réplica, et confirmer le stockage du réplica.

6) Symptom : le “fix” d’une requête lente aide brièvement, puis se dégrade

  • Cause racine : vous avez traité un symptôme. Coupables fréquents : effets de warming du cache, changement de paramètres (instabilité des plans), ou dérive des stats.
  • Fix : confirmer avec les empreintes de requêtes dans le temps, comparer les plans pour différents paramètres, et mettre en place une routine stats/ANALYZE (ou l’améliorer) avec surveillance.

Checklists / plan pas-à-pas pour un débogage reproductible

Checklist A: Instrumentation pré‑incident (faites-le un mardi calme)

  1. Activer et dimensionner l’agrégation des requêtes. Postgres : pg_stat_statements. Percona : digest de statements Performance Schema + vues sys.
  2. Activer la journalisation des requêtes lentes avec un seuil réaliste. Ne la réglez pas à 10 secondes et appelez ça de l’observabilité.
  3. Conserver suffisamment d’historique pour couvrir votre incident. Si vous rotatez les logs toutes les heures, vous perdrez la scène du crime.
  4. Capturer les métriques waits/verrous. Wait events Postgres ; waits/stages MySQL ; vues de verrous.
  5. Standardiser les workflows EXPLAIN. Postgres : EXPLAIN (ANALYZE, BUFFERS) pour les requêtes top en staging. MySQL : EXPLAIN plus échantillonnage runtime via Performance Schema.
  6. Baseliner la latence stockage. Connaître vos r_await/w_await normaux et le comportement fsync.

Checklist B: Pendant l’incident (boucle de confinement de 15 minutes)

  1. Confirmer l’impact utilisateur visible. Quels endpoints ? Lecture vs écriture ? Locataire unique ou global ?
  2. La santé de l’hôte d’abord. CPU, iowait, mémoire/swap, saturation disque.
  3. Identifier le blocage. Graphe de verrous Postgres ; MDL/verrous InnoDB MySQL.
  4. Empreintes de requêtes top. statements Postgres ; digests MySQL ou digest slow log.
  5. Mitriger en toute sécurité. Tuer le bloqueur, limiter la concurrence, mettre en pause les jobs batch, ou basculer si un réplica est sain et consistant.
  6. Prouver l’amélioration. Refaire les mêmes vérifications. Ne déclarez pas victoire parce que le graphe “a l’air plus calme”.

Checklist C: Après l’incident (transformer la douleur en ingénierie)

  1. Rédiger la timeline d’une page. Qu’est‑ce qui a changé, quand, comment détecté, ce qui a été corrigé.
  2. Ajouter des garde‑fous. Contrôles de migration, tests de régression de plan, limites de concurrence, coupe‑circuits.
  3. Instrumenter les signaux manquants. Si vous avez dû deviner, ajoutez une métrique ou un log pour ne plus deviner la prochaine fois.
  4. Rendre la correction ennuyeuse. La meilleure réponse aux incidents est un script et un runbook, pas un héros.

FAQ

1) Si je ne peux choisir qu’une seule fonctionnalité de visibilité “indispensable”, laquelle ?

L’agrégation des empreintes de requêtes avec temps et comptes. Dans Postgres c’est pg_stat_statements. Dans Percona/MySQL ce sont les digests de statements (Performance Schema) et/ou slow log + digest.
Sans cela, vous poursuivrez des requêtes individuelles au lieu de la forme de la charge de travail.

2) Lequel est meilleur pour me dire “nous attendons des verrous” ?

PostgreSQL est généralement plus clair et plus rapide : wait_event_type plus des jointures de verrous simples vous donnent une histoire immédiate.
Percona peut absolument le faire, mais vous devez savoir où regarder (processlist states, waits Performance Schema, InnoDB status) et l’avoir activé.

3) Le slow query log est‑il obsolète si j’ai Performance Schema ?

Non. Le slow query log est un enregistreur low‑tech. Il survit souvent quand votre instrumentation sophistiquée n’était pas dimensionnée correctement.
Utilisez les deux si vous pouvez supporter la surcharge et le stockage.

4) Pourquoi je vois “Using filesort” dans MySQL alors que je pensais avoir le bon index ?

Parce que l’index peut ne pas correspondre à la direction/colonnes de l’ORDER BY, ou l’optimiseur peut choisir un autre chemin en fonction des stats.
Réglez‑le avec un index qui correspond au tri, vérifiez la cardinalité, et confirmez que vous ne sélectionnez pas tant de colonnes que cela devient cher.

5) PostgreSQL semble plus lent après l’ajout d’index. N’est‑ce pas paradoxal ?

Les index accélèrent les lectures et taxent les écritures. Plus d’index signifie plus d’amplification d’écriture, plus de travail de vacuum, et parfois un comportement de cache pire.
Gardez les index qui “vont payer le loyer”. Supprimez ceux qui ne le font pas.

6) Quelle est la raison la plus courante pour laquelle Postgres ralentit “aléatoirement” ?

Un mélange de dette d’autovacuum et de transactions longues qui bloquent le nettoyage. Ce n’est pas aléatoire ; c’est de la maintenance différée qui devient visible.

7) Quelle est la raison la plus courante pour laquelle MySQL/Percona ralentit “aléatoirement” ?

Pression de flush/I/O plus contention de verrous (y compris MDL), souvent déclenchée par un changement de charge ou une migration. Et oui, parfois c’est un buffer pool sous‑dimensionné ou surdimensionné.

8) Puis‑je déboguer des ralentissements en toute sécurité sans exécuter de commandes lourdes ?

Oui. Utilisez d’abord des instantanés légers : tables top statements, processlist/pg_stat_activity, et métriques I/O de haut niveau.
Réservez les EXPLAIN ANALYZE lourds pour des échantillons contrôlés, et évitez de les exécuter sur des systèmes déjà saturés sauf si vous connaissez le coût.

9) Lequel est plus facile à exploiter avec une expertise limitée en base de données pendant la garde ?

Si vous investissez dans des runbooks, l’un ou l’autre fonctionne. Par défaut, le modèle “interroger des vues SQL, obtenir la vérité” de PostgreSQL est plus facile pour des généralistes.
Percona peut être tout aussi diagnostiquable, mais seulement si vous standardisez Performance Schema et les workflows de slow log à l’avance.

Étapes suivantes : comment choisir et comment instrumenter

Une citation s’impose ici, car c’est la morale opérationnelle de l’histoire. La posture fiabilité largement répétée de Werner Vogels peut être résumée par cette
(idée paraphrasée) : Vous le construisez, vous l’exploitez — Werner Vogels.
La débogabilité fait partie de l’exploitation, ce n’est pas un ajout optionnel quand les choses chauffent.

Si le plus grand risque de votre organisation est “nous ne savons pas ce qui se passe pendant les incidents”, choisissez le système et la configuration qui rendent la vérité la plus facile à récupérer.
Mon biais :

  • Choisissez PostgreSQL si vous voulez une visibilité SQL-native forte, un reporting d’attente de première classe, et un chemin simple des sessions → waits → verrous → statements → plans.
    Il récompense une hygiène rigoureuse du vacuum et des statistiques.
  • Choisissez Percona Server si vous êtes engagé dans l’écosystème MySQL, voulez des améliorations opérationnelles, et que vous exécuterez réellement Performance Schema et la journalisation lente correctement.
    Il récompense des outils standardisés et une gestion prudente de la concurrence/du flush.

Étapes pratiques à réaliser cette semaine

  1. Activer les empreintes de requêtes et vérifier la rétention. Confirmer que vous pouvez répondre “top 5 des requêtes par temps total dans la dernière heure”.
  2. Activer la journalisation lente avec un seuil défendable et une rotation. Conserver suffisamment d’historique pour couvrir une durée d’incident typique.
  3. Rédiger le runbook de ralentissement 15 minutes. Vérifications hôte, vérifications de verrous, top requêtes, options de mitigation.
  4. Faire un game day. Injecter de la charge, simuler un verrou, simuler une saturation I/O, et mesurer le temps nécessaire pour que la garde atteigne une hypothèse crédible.
  5. Rendre les migrations ennuyeuses. La plupart des “ralentissements base de données” sont des échecs de coordination de changement de schéma déguisés.

La meilleure visibilité est celle à laquelle vous ne pensez pas avant d’en avoir besoin — parce que vous savez déjà où elle se trouve, et qu’elle collecte déjà les preuves.

← Précédent
Microsoft Zune : comment « pas iPod » est devenu culte
Suivant →
OpenVPN AUTH_FAILED : pourquoi des identifiants corrects échouent (et quoi vérifier)

Laisser un commentaire