PostgreSQL vs Percona Server : opérations — quoi est plus simple à gérer à 3 h du matin

Cet article vous a aidé ?

03:07. Le pager sonne. L’application est « down », le canal exécutif est « up », et quelqu’un tape
« did we lose data? » en MAJUSCULES. Vous ne gagnez pas de points pour l’architecture maintenant.
Vous gagnez des points en restaurant le service et en sachant quoi croire.

C’est une comparaison pratique de l’exploitation de PostgreSQL versus Percona Server (compatible MySQL) quand vous êtes fatigué,
sous pression, et que le rayon d’impact est réel. Pas pour savoir lequel est « meilleur ». Plutôt lequel est plus simple à exploiter à 3 h du matin,
avec les boutons, les logs et les chemins de récupération qui existent réellement en production.

Ce que « plus simple à 3 h du matin » signifie vraiment

« Plus simple » ne veut pas dire moins de fonctionnalités. Cela signifie moins d’inconnues dans le chemin de défaillance. À 3 h du matin vous avez besoin :
(1) une visibilité claire sur ce qui est cassé, (2) un ou deux leviers de récupération fiables,
(3) un comportement de performance prévisible sous charge, et (4) des sauvegardes qui restaurent du premier coup.

PostgreSQL et Percona Server peuvent tous deux être bien exploités. Mais ils ont tendance à punir des erreurs différentes.
PostgreSQL punit la négligence (autovacuum, tables gonflées, mauvaises requêtes qui déversent sur le disque).
Percona Server punit les hypothèses floues sur la réplication et l’attitude « c’est juste MySQL » quand le sous-système InnoDB
crie silencieusement.

Mon parti pris, basé sur des incidents : PostgreSQL est souvent plus simple à récupérer correctement parce que les sémantiques de durabilité
sont explicites et la réplication est cohérente. Percona Server est souvent plus simple pour monter en capacité rapidement car son
écosystème (plus les outils Percona) facilite les schémas MySQL courants — jusqu’à ce que vous rencontriez une dérive subtile de réplication,
une confusion GTID, ou des fantasmes multi-writer.

Un autre parti pris, aussi mérité : la base de données pour laquelle vous avez déjà de bons runbooks est plus simple. « Familier » bat « meilleur »
sur le moment, ce qui explique pourquoi la maturité opérationnelle compte plus que les benchs.

Une petite blague courte, comme il se doit : À 3 h du matin, chaque base de données est un système distribué, parce que vos coéquipiers sont distribués
entre fuseaux horaires et opinions.

Faits intéressants et contexte historique (les éléments qui expliquent les cicatrices)

  • La lignée de PostgreSQL : PostgreSQL provient du projet POSTGRES à l’UC Berkeley dans les années 1980, et son ADN « bien faire les choses » se voit dans les sémantiques transactionnelles et l’extensibilité.
  • Les bagages par défaut de MySQL : MySQL a historiquement été livré avec des valeurs par défaut non transactionnelles (comme MyISAM), ce qui a entraîné une génération à traiter la durabilité comme optionnelle. InnoDB a changé la donne, mais les échos culturels persistent.
  • L’origine de Percona Server : Percona a construit une distribution autour de MySQL avec instrumentation et améliorations de performance parce que les opérateurs voulaient plus de visibilité que celle fournie en amont.
  • WAL vs binlog : le write-ahead log (WAL) de PostgreSQL est fondamental pour la sécurité après crash et la réplication. Le binary log (binlog) de MySQL est à la fois le carburant de la réplication et une histoire logique — puissant, mais il peut aussi être source de dérive si on l’utilise à la légère.
  • MVCC partout, mais pas identique : Les deux systèmes utilisent des idées MVCC, mais l’exigence de vacuum de PostgreSQL est une corvée opérationnelle de premier ordre, tandis que le comportement undo/redo et purge d’InnoDB tend à se manifester par la « longueur de la history list » ou des problèmes d’undo tablespace.
  • Évolution de la réplication : La réplication MySQL a commencé basée sur des statements, puis row-based, puis mixte. Cette histoire compte parce que les habitudes statement-based fuient toujours dans les hypothèses sur la déterminisme.
  • Maturité de la réplication physique PostgreSQL : La réplication en streaming et les replication slots ont rendu l’archivage continu et les répliques plus robustes, mais ont introduit un nouveau piège : les slots peuvent retenir du WAL indéfiniment si vous les oubliez.
  • Impact des outils de sauvegarde Percona : XtraBackup est devenu l’ami de l’opérateur pour les sauvegardes physiques à chaud en MySQL, surtout quand les dumps logiques étaient trop lents. Mais cela a aussi créé un écart « sauvegarde réussie, restauration… peut-être » si les drills de restauration ne sont pas pratiqués.
  • Conservatisme des configs par défaut : Les valeurs par défaut de PostgreSQL sont volontairement conservatrices ; vous devez régler la mémoire et le comportement des checkpoints pour des charges réelles. Les valeurs par défaut MySQL se sont aussi améliorées, mais vous verrez encore des systèmes en production avec des buffers InnoDB dangereusement grands et des réglages de flush risqués parce que quelqu’un a couru après un graphe.

Modèles mentaux opérationnels : comment chaque base échoue

PostgreSQL : « tout va bien jusqu’à ce que l’autovacuum ne le soit plus »

PostgreSQL échoue généralement de manière diagnostiquable : forte charge, requêtes longues, contention sur les verrous, attente I/O,
pics de checkpoints, ou bloat rendant tout plus lent. Quand il plante, la récupération est en général déterministe :
rejouer le WAL, revenir. La partie délicate n’est pas la récupération après crash ; c’est rester en dehors de la spirale lente
où le bloat et les mauvais plans causent plus d’I/O, ce qui cause des requêtes plus longues, ce qui génère plus de dead tuples et aggrave le vacuum.

Les leviers à 3 h du matin pour PostgreSQL sont souvent : annuler les requêtes hors de contrôle, réduire la contention sur les verrous, corriger les index/plans,
ajuster la mémoire et les checkpoints, et s’assurer que l’archivage WAL est sain. C’est un système qui récompense
l’hygiène opérationnelle ennuyeuse.

Percona Server : « la réplication est facile jusqu’à ce qu’elle ne le soit plus »

Percona Server hérite du modèle opérationnel MySQL : un primaire (ou « source »), des répliques (« replica »/« slave »),
réplication asynchrone, et une boîte à outils d’options. Percona ajoute de la visibilité (améliorations du performance schema,
compatibilité avec Percona Toolkit, et patches opérationnels selon la version).

À 3 h du matin, votre pire ennemi n’est pas toujours « la base est down ». C’est « la base est up, mais pas cohérente »,
ou « les répliques sont en retard », ou « nous avons basculé et maintenant les écritures vont au mauvais endroit ».
InnoDB peut aussi se retrouver dans des états où il est vivant mais effectivement bloqué sur l’I/O, la pression des redo logs, ou des transactions longues empêchant la purge.

Une citation (idée paraphrasée), comme demandé : paraphrased idea: In reliability engineering, hope isn’t a strategy; systems need feedback loops and tested recovery. — inspirée par les pratiques SRE courantes.

Mode d’emploi pour un diagnostic rapide (premier/deuxième/troisième)

Premier : est-ce le CPU, la mémoire ou l’I/O ?

  • Vérifiez la charge et la saturation : une forte charge n’implique pas que le CPU est le problème. Ça peut être l’attente I/O ou le chaos de la file exécutable.
  • Regardez l’attente I/O : si le disque est saturé, l’optimisation des requêtes n’aidera pas tant que vous n’arrêtez pas l’hémorragie (limiter, tuer les coupables, mettre en pause les jobs batch, ajouter de la capacité, réduire les pics de checkpoint/flush).
  • Vérifiez la pression mémoire : le swap sur un hôte de base de données est une panne au ralenti.

Deuxième : sommes-nous bloqués par des verrous ou attendons-nous le stockage ?

  • PostgreSQL : trouvez les PIDs bloquants, les transactions longues, et l’autovacuum coincé derrière des verrous. Si la réplication est impliquée, vérifiez WAL sender/receiver et le backlog des slots.
  • Percona Server : vérifiez les transactions actives, les deadlocks, les attentes de verrou de ligne InnoDB, et l’état des threads de réplication. Validez que vous écrivez bien sur le primaire prévu.

Troisième : le système diverge-t-il (réplication, corruption ou défaillance partielle) ?

  • Le retard de réplication change votre réponse d’incident. Si votre réplique a 30 minutes de retard, basculer peut entraîner une perte de données.
  • Vérifiez les logs d’erreur pour des erreurs CRC, échecs d’fsync, disque plein, ou échecs d’archivage redo/WAL. Ce ne sont pas des problèmes « pour plus tard ».
  • Confirmez que les sauvegardes sont valides avant d’effectuer des modifications destructrices. Dans les deux écosystèmes, il est trop facile de supposer.

Tâches opérationnelles pratiques avec commandes (et ce que vous décidez d’après la sortie)

Voici le type de commandes que vous lancez vraiment quand le monde brûle. Chaque tâche inclut :
la commande, ce que signifie la sortie, et la décision suivante.

Tâche 1 (hôte) : confirmer attente I/O vs saturation CPU

cr0x@server:~$ iostat -xz 1 3
Linux 6.5.0 (db01)  12/30/2025  _x86_64_  (16 CPU)

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          12.31    0.00    4.88   35.22    0.00   47.59

Device            r/s     w/s   rkB/s   wkB/s  await  aqu-sz  %util
nvme0n1         220.0   780.0  8800.0 54000.0   18.4    7.12   98.7

Signification : %iowait est élevé et le dispositif est proche de 100% d’utilisation. Vous êtes lié par l’I/O, pas par le CPU.
Décision : arrêtez de générer plus d’I/O : tuez les pires requêtes, mettez en pause les jobs batch, réduisez la pression de checkpoint (Postgres) ou la pression de flush (InnoDB), et vérifiez disque plein et erreurs RAID/NVMe.

Tâche 2 (hôte) : repérer le swap et la pression mémoire

cr0x@server:~$ free -m
               total        used        free      shared  buff/cache   available
Mem:           64000       54000        1200         600        8800        6200
Swap:           8192        3900        4292

Signification : le swap est utilisé. Sur un hôte de base, c’est généralement une auto-infliction.
Décision : réduisez les consommateurs de mémoire maintenant (tempêtes de connexions, work_mem/sort buffers gigantesques, buffer pool trop grand). Si vous ne pouvez pas, déplacez la charge hors du nœud ou ajoutez de la RAM. Swapping plus forte attente I/O est un cocktail classique d’incident.

Tâche 3 (PostgreSQL) : voir ce qui tourne et ce qui attend

cr0x@server:~$ psql -XAtc "select pid, usename, state, wait_event_type, wait_event, now()-query_start as age, left(query,80) from pg_stat_activity where state <> 'idle' order by age desc limit 10;"
9231|app|active|Lock|transactionid|00:12:33.18291|update orders set status='paid' where id=$1
8120|app|active|IO|DataFileRead|00:09:10.09121|select * from order_items where order_id=$1
...

Signification : vous avez une attente de verrou (transactionid) et des attentes I/O (DataFileRead). La requête la plus ancienne bloque probablement les autres.
Décision : trouvez le bloqueur et décidez s’il faut l’annuler/terminer. Vérifiez aussi si l’attente I/O est systémique (voir iostat) ou due à une seule mauvaise requête/index.

Tâche 4 (PostgreSQL) : trouver rapidement les bloqueurs

cr0x@server:~$ psql -XAtc "select blocked.pid as blocked_pid, blocker.pid as blocker_pid, now()-blocker.query_start as blocker_age, left(blocker.query,80) as blocker_query from pg_locks blocked join pg_locks blocker on blocker.locktype=blocked.locktype and blocker.database is not distinct from blocked.database and blocker.relation is not distinct from blocked.relation and blocker.page is not distinct from blocked.page and blocker.tuple is not distinct from blocked.tuple and blocker.virtualxid is not distinct from blocked.virtualxid and blocker.transactionid is not distinct from blocked.transactionid and blocker.classid is not distinct from blocked.classid and blocker.objid is not distinct from blocked.objid and blocker.objsubid is not distinct from blocked.objsubid and blocker.pid <> blocked.pid join pg_stat_activity blocked_act on blocked_act.pid=blocked.pid join pg_stat_activity blocker on blocker.pid=blocker.pid where not blocked.granted and blocker.granted limit 5;"
9231|7011|00:48:02.01123|alter table orders add column foo text

Signification : un ALTER TABLE tient des verrous depuis 48 minutes. Cela peut geler votre application.
Décision : terminez le DDL s’il n’est pas sûr, puis mettez en place une politique : changements de schéma en ligne, timeouts de lock, et fenêtres DDL.

Tâche 5 (PostgreSQL) : vérifier le retard de réplication et le backlog WAL

cr0x@server:~$ psql -XAtc "select application_name, state, sync_state, pg_size_pretty(pg_wal_lsn_diff(sent_lsn, replay_lsn)) as byte_lag from pg_stat_replication;"
replica01|streaming|async|512 MB

Signification : votre réplique a ~512Mo de retard de WAL. Ça peut être des secondes ou des minutes selon le débit d’écriture.
Décision : ne basculez pas aveuglément. Vérifiez si le retard diminue. S’il augmente, vous avez un problème en aval (réseau, disque, replay).

Tâche 6 (PostgreSQL) : vérifier les replication slots pour rétention WAL excessive

cr0x@server:~$ psql -XAtc "select slot_name, active, pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) as retained from pg_replication_slots;"
logical_slot_1|f|97 GB

Signification : un slot logique inactif retient 97Go de WAL. Disque plein devient maintenant un incident programmé.
Décision : réactivez le consommateur ou supprimez le slot s’il est vraiment mort. Ajoutez ensuite de la surveillance/alertes pour la rétention de slot.

Tâche 7 (PostgreSQL) : vérifier la pression des checkpoints

cr0x@server:~$ psql -XAtc "select checkpoints_timed, checkpoints_req, round(checkpoint_write_time/1000.0) as write_s, round(checkpoint_sync_time/1000.0) as sync_s from pg_stat_bgwriter;"
120|980|8420|3110

Signification : les checkpoints demandés (980) écrasent les timed (120). Le système force des checkpoints fréquents, généralement à cause du volume WAL et d’un max_wal_size trop petit.
Décision : augmentez max_wal_size et ajustez checkpoint_completion_target ; puis vérifiez que le stockage peut supporter l’écriture continue. Ne « corrigez » pas cela en désactivant fsync à moins d’aimer évoluer professionnellement sur le marché du travail.

Tâche 8 (Percona Server) : identifier le primaire et la santé de la réplication

cr0x@server:~$ mysql -NBe "SHOW SLAVE STATUS\G" | egrep "Slave_IO_Running|Slave_SQL_Running|Seconds_Behind_Master|Last_SQL_Error"
Slave_IO_Running: Yes
Slave_SQL_Running: No
Seconds_Behind_Master: NULL
Last_SQL_Error: Error 'Duplicate entry' on query. Default database: 'app'. Query: 'INSERT INTO ...'

Signification : le thread IO tourne, le thread SQL est arrêté, le lag est inconnu. La réplique est cassée, pas juste en retard.
Décision : ne promouvez pas cette réplique. Réparez la réplication (skip/repair avec une extrême prudence), ou reconstruisez-la. Diagnostiquez aussi pourquoi un doublon est survenu — souvent un multi-writer mal configuré ou des statements non déterministes.

Tâche 9 (Percona Server) : confirmer le mode GTID et la sécurité du basculement

cr0x@server:~$ mysql -NBe "SHOW VARIABLES LIKE 'gtid_mode'; SHOW VARIABLES LIKE 'enforce_gtid_consistency';"
gtid_mode	ON
enforce_gtid_consistency	ON

Signification : GTID est activé et la consistance est forcée. Cela rend les outils de basculement et le repointing des répliques plus propres.
Décision : pour un basculement planifié/non planifié, privilégiez des topologies avec GTID. Si c’est désactivé, attendez-vous à des positions binlog manuelles et plus de risques à 3 h du matin.

Tâche 10 (Percona Server) : lire le statut du moteur InnoDB pour verrou et purge

cr0x@server:~$ mysql -NBe "SHOW ENGINE INNODB STATUS\G" | sed -n '1,120p'
...
TRANSACTIONS
------------
Trx id counter 123456789
Purge done for trx's n:o < 123450000 undo n:o < 0 state: running but idle
History list length 987654
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 123456111, ACTIVE 1865 sec
...

Signification : la length de la history list est énorme ; la purge ne suit pas, souvent à cause de transactions longues.
Décision : trouvez et terminez les transactions longues (ou corrigez le comportement applicatif). Sinon, l’undo grossit, la performance se dégrade, et vous finirez par « optimiser » en redémarrant — alias éteindre et rallumer avec des étapes supplémentaires.

Tâche 11 (Percona Server) : détecter les deadlocks et décider quoi tuer

cr0x@server:~$ mysql -NBe "SHOW ENGINE INNODB STATUS\G" | egrep -n "LATEST DETECTED DEADLOCK|TRANSACTION|WAITING FOR THIS LOCK"
2345:LATEST DETECTED DEADLOCK
2361:*** (1) TRANSACTION:
2388:*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
2410:*** (2) TRANSACTION:

Signification : des deadlocks se produisent. InnoDB résout généralement en rollbackant une transaction.
Décision : si les deadlocks augmentent, cherchez de nouveaux chemins de code ou des index manquants. Tuer des threads au hasard est rarement la solution ; corriger les patterns d’accès l’est.

Tâche 12 (MySQL/Percona) : confirmer la pression sur le buffer pool et le hit rate

cr0x@server:~$ mysql -NBe "SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_reads'; SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read_requests';"
Innodb_buffer_pool_reads	9876543
Innodb_buffer_pool_read_requests	1234567890

Signification : les lectures hors buffer pool sont significatives ; comparez les taux de croissance au fil du temps. Si les lectures disque montent rapidement, votre cache ne contient pas le working set.
Décision : augmentez le buffer pool (si la RAM le permet), réduisez le working set (indexes, corrections de requêtes), ou cessez de prétendre que les disques rotatifs « ça va parce que c’est surtout des lectures ».

Tâche 13 (PostgreSQL) : trouver les requêtes les plus coûteuses au total (pg_stat_statements)

cr0x@server:~$ psql -XAtc "select round(total_exec_time) as total_ms, calls, round(mean_exec_time,2) as mean_ms, left(query,90) from pg_stat_statements order by total_exec_time desc limit 5;"
983412|1203|817.33|select * from events where user_id=$1 order by created_at desc limit 50
...

Signification : la requête avec le total le plus élevé est à la fois fréquente et plutôt lente. C’est votre ROI.
Décision : ajoutez l’index approprié, corrigez le tri/pagination, ou changez la forme de la requête. Puis re-vérifiez. Ne touchez pas aux paramètres kernel avant d’avoir corrigé la requête évidente.

Tâche 14 (PostgreSQL) : vérifier signaux de bloat (dead tuples) et autovacuum

cr0x@server:~$ psql -XAtc "select relname, n_live_tup, n_dead_tup, round(100.0*n_dead_tup/nullif(n_live_tup+n_dead_tup,0),2) as dead_pct from pg_stat_user_tables order by n_dead_tup desc limit 5;"
orders|12000000|4800000|28.57
events|90000000|11000000|10.89

Signification : grand nombre de dead tuples. Si l’autovacuum ne suit pas, la performance des requêtes se dégradera et les index gonfleront aussi.
Décision : ajustez autovacuum pour les tables chaudes, envisagez un VACUUM manuel (ou VACUUM FULL dans des fenêtres contrôlées), et corrigez les patterns de transaction qui empêchent le vacuum (transactions longues).

Tâche 15 (Percona Server) : voir les requêtes en cours et leur durée

cr0x@server:~$ mysql -NBe "SHOW FULL PROCESSLIST;" | head
12345	app	10.0.2.15:44210	appdb	Query	35	Sending data	SELECT ... FROM big_table ...
12346	app	10.0.2.16:55122	appdb	Sleep	1200		NULL

Signification : une requête tourne depuis 35 secondes et est « Sending data » (souvent scan ou tri). Il y a aussi des connexions en sommeil longues.
Décision : tuez ou optimisez les coupables, limitez max connections ou utilisez du pooling, et investiguez pourquoi des connexions restent en sommeil (fuites applicatives, mauvaise gestion du pool).

Tâche 16 (hôte) : confirmer disque plein avant de chasser des fantômes

cr0x@server:~$ df -h /var/lib
Filesystem      Size  Used Avail Use% Mounted on
/dev/nvme0n1p3  900G  890G   10G  99% /var/lib

Signification : vous êtes pratiquement à court d’espace disque. PostgreSQL et InnoDB se comportent mal ici, chacun selon un dialecte de douleur différent.
Décision : libérez de l’espace immédiatement (anciens logs, anciennes sauvegardes, WAL/binlogs tournés avec prudence), augmentez le volume, puis faites un postmortem sur les paramètres de rétention.

Sauvegardes & récupération : la vérité à 3 h du matin

PostgreSQL : les sauvegardes sont simples, les restaurations sont là où vous gagnez la confiance

PostgreSQL propose deux modes de sauvegarde grand public :
logique (pg_dump) et physique (base backup + WAL). Les sauvegardes logiques sont portables,
lentes à grande échelle, et excellentes pour les migrations. Les sauvegardes physiques sont rapides à restaurer et adaptées à la reprise après sinistre,
mais nécessitent l’archivage WAL (ou le streaming continu) pour obtenir une récupération point-in-time.

La simplicité opérationnelle en Postgres est conceptuelle : si vous comprenez le WAL, vous comprenez la récupération après crash,
la réplication, et le PITR. C’est un seul modèle mental avec différents outils.

Les pièges opérationnels sont aussi cohérents : si l’archivage WAL casse, le PITR casse. Si les replication slots retiennent du WAL,
les disques se remplissent. Si vous ne pratiquez pas la restauration, votre « sauvegarde » est un dossier réconfortant de mensonges.

Percona Server : XtraBackup est excellent, mais respectez la chaîne de restauration

Les opérateurs Percona Server utilisent typiquement :
dumps logiques (mysqldump/mysqlpump) et sauvegardes physiques à chaud (XtraBackup).
XtraBackup est rapide et souvent la bonne réponse pour de grands jeux de données. Il introduit aussi une réalité opérationnelle :
une sauvegarde physique n’est pas « un fichier ». C’est un arbre de répertoires, des métadonnées, et une étape de prepare/apply-log.
Si votre chemin de restauration n’est pas scripté et répété, il vous trahira au moment où vous en aurez besoin.

Le PITR MySQL/Percona repose souvent sur les binlogs plus une sauvegarde de base. C’est puissant mais opérationnellement plus délicat :
vous devez suivre la rétention des binlogs, le format de binlog, le mode GTID, et la séquence d’application.

Qui est plus simple à 3 h du matin pour la récupération ?

Si votre organisation est disciplinée avec l’archivage WAL et que vous testez les restaurations, la récupération PostgreSQL est généralement plus simple et plus prévisible.
Si votre organisation est déjà profondément dans l’univers MySQL et que vous avez automatisé des restaurations XtraBackup robustes, Percona peut être extrêmement fluide.
Le facteur décisif n’est pas la disponibilité des outils. C’est que vous ayez un runbook de restauration pratiqué et documenté et des drills de restauration en environnement de test.

Réplication & basculement : ce qui casse et à quel point ça sonne

Réplication PostgreSQL : moins de modes, moins de surprises étranges

La réplication en streaming de PostgreSQL est physique : les répliques rejouent le WAL. Cela rend la réplication cohérente avec la façon dont le primaire
valide les changements. La réplication logique existe, mais la plupart des topologies de basculement opérationnelles reposent sur la réplication physique.

Opérationnellement, c’est une bonne nouvelle : moins de formats de réplication, moins de problèmes de « cette instruction est non déterministe ».
Vous devez toujours gérer le retard de réplication, les partitions réseau, et les outils de promotion. Mais le mode d’échec typique à 3 h du matin
est évident : « la réplique est en retard » ou « la réplique ne suit pas parce que le disque est lent ».

Réplication Percona Server : flexible, mature, et facile à mal comprendre

La réplication MySQL s’est énormément améliorée : GTID, réplication row-based, options semi-sync, apply multi-thread. Percona Server suit cet écosystème et expose souvent plus d’instrumentation.

Le danger à 3 h du matin est humain : les gens supposent que la réplication asynchrone est « à peu près synchrone », ou qu’ils ont une consistance read-after-write
sur les répliques, ou qu’un basculement n’est qu’un changement DNS. Ensuite ils découvrent que le retard de réplication n’est pas une suggestion ; c’est de la physique.

Si vous exécutez Percona Server, soyez religieux sur :
le format binlog row-based, GTID partout, et un basculement automatisé qui verrouille aussi l’ancien primaire.
La partie fencing est ce qui vous évite les écritures split-brain — l’une des classes d’incidents les plus coûteuses.

Tuning de performance : changements sûrs vs regret

Tuning PostgreSQL qui aide réellement à 3 h du matin

Les incidents de performance PostgreSQL sont souvent liés au plan de requête et à l’I/O. Leviers sûrs :
ajuster le nombre de connexions (pooling), corriger les index manquants, et tweaker les paramètres checkpoint/WAL pour éviter les pics d’écriture périodiques.
Des réglages de mémoire comme work_mem peuvent aider mais sont aussi un piège courant : c’est par tri, par hash, par session.

Les changements les plus « sûrs à 3 h du matin » sont ceux qui réduisent la charge sans changer la correction : annuler une requête, ajouter un index manquant
(prudemment, peut-être concurrently), ajuster les timeouts de statement, et réduire la concurrence.

Tuning Percona Server qui aide réellement à 3 h du matin

InnoDB est généralement au centre. Leviers sûrs :
s’assurer que le buffer pool est dimensionné raisonnablement, garder innodb_flush_log_at_trx_commit et sync_binlog sur des réglages durables sauf si vous avez une acceptation de risque écrite,
et vérifier que vous n’êtes pas étranglé par trop de connexions ou l’ordonnancement des threads.

Beaucoup de « gains de performance » MySQL sont en réalité des échanges de durabilité. Ils ont l’air super jusqu’à un événement d’alimentation ou un kernel panic.
Si vous devez changer les réglages de durabilité, faites-le comme une décision métier consciente, pas comme une expérience à minuit.

Deuxième petite blague courte, comme il se doit : Désactiver fsync, c’est comme retirer le détecteur de fumée parce qu’il est bruyant — brièvement paisible, puis éducatif.

Réalités du stockage et du système de fichiers : I/O, durabilité et surprises

Les deux bases sont des moteurs de stockage avec des opinions. Si votre stockage ment, elles persisteront fidèlement ce mensonge.
La question de simplicité à 3 h du matin revient souvent à « comment cette base se comporte-t-elle sous douleur I/O ? »

PostgreSQL sous douleur I/O

  • Les pics de checkpoint peuvent créer des tempêtes de latence périodiques si mal réglés.
  • L’autovacuum peut devenir soit un héros (prévenir le bloat) soit un vilain (s’il entre en compétition avec la charge sur des disques lents).
  • L’archivage WAL est une dépendance dure pour le PITR ; un archive cassé est un incident, pas un avertissement.

InnoDB sous douleur I/O

  • La pression sur les redo logs et le comportement de flushing peuvent provoquer des blocages si le sous-système de logs est contraint.
  • Les transactions longues empêchent la purge et provoquent l’accumulation d’historique undo, ce qui cause ensuite plus d’I/O.
  • Le flushing des pages sales et les checkpoints peuvent entraîner un effondrement du débit quand les disques sont saturés.

Conseil pour l’opérateur : choisissez vos batailles de stockage

Si vous êtes sur du block storage cloud, testez vos pires IOPS, pas votre moyenne. Si vous êtes sur NVMe local,
planifiez la panne de périphérique et le comportement de rebuild. Et dans tous les cas : surveillez la latence disque, la profondeur de file, et la saturation du système de fichiers.
Les incidents disque-plein sont embarrassants et fréquents parce que les problèmes de stockage sont silencieux jusqu’à ce qu’ils ne le soient plus.

Trois mini-récits d’entreprise (réalistes, anonymisés, techniquement exacts)

1) Incident causé par une mauvaise hypothèse : « les lectures sur réplique sont toujours fraîches »

Une entreprise SaaS de taille moyenne exécutait Percona Server avec un primaire et deux répliques. Le document d’architecture disait :
« Les lectures vont sur les répliques, les écritures vont sur le primaire. » Le load balancer suivait consciencieusement cette règle.
Personne n’avait écrit la deuxième règle : « Certaines lectures doivent être cohérentes avec l’écriture récente de l’utilisateur. »

Un lancement produit a augmenté les écritures. Le retard de réplication est passé de « généralement négligeable » à « notable ».
Les tickets support ont commencé : les clients enregistraient des paramètres, rafraîchissaient, et les paramètres revenaient en arrière. L’application ne perdait pas de données.
Elle lisait simplement des données anciennes depuis les répliques. Du point de vue utilisateur, c’est indiscernable d’une perte de données.

À 3 h du matin, l’astreinte a essayé les manœuvres habituelles : redémarrer les pods applicatifs, augmenter les connexions DB, scaler les répliques.
Cela a empiré. Plus de connexions ont augmenté la contention d’écriture sur le primaire, ce qui a accru le retard, ce qui a accru l’incohérence.
Le système s’est comporté exactement comme conçu — juste pas comme assumé.

La correction a été ennuyeuse et efficace. Ils ont routé les chemins « lecture-après-écriture » vers le primaire (ou utilisé l’affinité de session),
ajouté un routage conscient du retard (ne plus envoyer de lectures à une réplique au-delà d’un seuil), et clarifié dans le code :
« la consistance éventuelle est acceptable ici, pas là ». Ils ont aussi mis en place un fencing GTID pour le basculement,
car l’incident a révélé à quel point le basculement avait été discuté sans sérieux.

Leçon opérationnelle : la réplication Percona peut être solide, mais elle ne vous sauvera pas de considérer la réplication asynchrone comme magique.
Postgres a la même physique, mais les équipes conçoivent souvent autour de cela plus tôt parce que le retard de streaming WAL est fréquemment surveillé comme « octets WAL en retard », ce qui paraît plus concret que « secondes derrière le master ».

2) Optimisation qui s’est retournée contre l’équipe : « paramètres mémoire plus grands pour trier plus vite »

Une équipe plateforme data utilisait PostgreSQL pour des charges analytiques sur un cluster partagé. Ils avaient un backlog de requêtes lentes
avec de grands tris et hashes. Quelqu’un a proposé d’augmenter significativement work_mem. L’environnement de test semblait mieux.
Les graphiques souriaient. Le changement est déployé.

Deux heures plus tard le primaire a commencé à swapper. La latence a explosé. L’autovacuum est resté à la traîne.
Puis un retard de réplication est apparu car le replay WAL sur la réplique a ralenti à cause de la contention disque.
Le canal d’incident s’est rempli des suspects habituels : « réseau ? », « bug kernel ? », « sommes-nous DDoSés ? »

Ce qui s’est passé est simple : work_mem est par opération. Sous concurrence, un work_mem élevé se multiplie
en consommation mémoire réelle. La base n’« utilisait pas plus de mémoire pour une requête ». Elle en utilisait pour des centaines.
Une fois le swap enclenché, le système a passé la nuit à thrashing, effectuant de l’I/O coûteuse pour soutenir un overcommit mémoire.

Le rollback a réglé la douleur immédiate. L’amélioration réelle a été plus nuancée : ils ont ajouté les bons index,
réduit la concurrence pour les rapports lourds, et utilisé des timeouts de requête et des files de ressources (au niveau du scheduler applicatif)
pour qu’une charge bruyante ne puisse pas prendre tout le nœud en otage.

Leçon opérationnelle : des changements qui paraissent sûrs isolément peuvent être catastrophiques sous concurrence. Dans Postgres, les réglages mémoire
sont étonnamment tranchants. Dans l’univers Percona/MySQL, le retour de bâton équivalent est souvent « rendre le buffer pool énorme »
en oubliant le cache du système de fichiers, la marge OS, ou les besoins mémoire pour backup/restore.

3) Pratique ennuyeuse mais correcte qui a sauvé la situation : drills de restauration et checklists de promotion

Une entreprise liée aux paiements utilisait PostgreSQL avec une politique stricte : drills de restauration mensuels vers un environnement staging,
et « promouvez une réplique » trimestriellement. C’était le genre de pratique qui ne rapporte jamais de prix internes parce qu’elle n’apporte pas de fonctionnalités.
Elle rend aussi les incidents plus courts et moins dramatiques, ce qui déplaît profondément à ceux qui aiment le drame.

Une nuit, un contrôleur de stockage est devenu instable et a commencé à renvoyer des erreurs I/O intermittentes. PostgreSQL a commencé à logger
des échecs d’fsync. L’équipe n’a pas débattu de la réalité des logs. Leur runbook traitait les échecs d’fsync comme un risque « stop-the-world ».
Ils ont fence le nœud, promu une réplique, et déplacé le trafic.

Le moment clé : ils savaient déjà que la réplique pouvait être promue proprement parce qu’ils l’avaient fait à plusieurs reprises.
Ils savaient aussi que leur chaîne PITR était intacte parce que les drills de restauration avaient vérifié les archives WAL et les backups de base.
Quand la direction a demandé « sommes-nous en sécurité ? » l’astreinte a pu répondre avec des preuves, pas de l’optimisme.

Leçon opérationnelle : la pratique transforme un incident à 3 h du matin en une checklist. Les outils PostgreSQL s’alignent bien avec cette discipline,
mais la même approche fonctionne pour Percona aussi — surtout si vous validez régulièrement les restaurations XtraBackup et les procédures d’application de binlog.

Erreurs courantes : symptôme → cause racine → correction

1) Symptom : l’utilisation disque grimpe sans fin sur PostgreSQL

Cause racine : slot de réplication inactif retenant du WAL, ou archivage WAL qui échoue et WAL qui s’accumule.

Correction : inspectez pg_replication_slots, supprimez les slots inutilisés, réparez le consommateur, et alertez sur la taille de WAL retenue. Validez archive_command et permissions.

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

Cause racine : checkpointing agressif dû à un max_wal_size petit ou des paramètres de checkpoint mal réglés.

Correction : augmentez max_wal_size, mettez checkpoint_completion_target près de 0.9, assurez-vous que le stockage peut soutenir des écritures soutenues, et confirmez que vous ne satuez pas l’I/O.

3) Symptom : les requêtes ralentissent au fil des jours ; les index semblent « moins efficaces » (PostgreSQL)

Cause racine : bloat de tables et d’index dû à un vacuum insuffisant ou à des transactions longues empêchant le nettoyage.

Correction : ajustez autovacuum par table chaude, éliminez les transactions longues, et planifiez reindex/vacuum de façon appropriée.

4) Symptom : la réplique affiche Seconds_Behind_Master: NULL (Percona/MySQL)

Cause racine : le thread SQL est arrêté à cause d’une erreur (duplicate key, table manquante, dérive de schéma).

Correction : vérifiez Last_SQL_Error, décidez de reconstruire ou réparer soigneusement. Préférez GTID et un déploiement de schéma cohérent pour éviter la dérive.

5) Symptom : « deadlocks augmentés » après une release (Percona/MySQL)

Cause racine : un nouveau chemin de requête change l’ordre des verrous ou un index manquant cause des verrous plus larges et des temps de maintien plus longs.

Correction : analysez les logs de deadlock, ajoutez des index, et imposez un ordre transactionnel cohérent dans le code applicatif.

6) Symptom : InnoDB se bloque avec une grande history list length (Percona/MySQL)

Cause racine : transactions longues empêchant la purge ; parfois de grosses transactions de lecture ou des transactions inactives laissées ouvertes.

Correction : identifiez et terminez les transactions longues, définissez des timeouts sensés, et corrigez la gestion des connexions applicatives. Surveillez la history list length.

7) Symptom : après un basculement, des écritures ont lieu sur deux nœuds (n’importe quel écosystème)

Cause racine : absence de fencing ; des clients restent connectés à l’ancien primaire ; risque de split-brain.

Correction : implémentez du fencing au niveau réseau/load balancer ; faites respecter l’écrivain unique via l’automatisation et les health checks ; empêchez l’ancien primaire d’accepter des écritures.

8) Symptom : les sauvegardes « réussissent », les restaurations échouent (n’importe quel écosystème)

Cause racine : sauvegardes non testées ; WAL/binlogs manquants ; permissions/chemins incorrects ; clés de chiffrement absentes ; étapes de restauration non documentées.

Correction : planifiez des drills de restauration, automatisez la restauration, vérifiez les checksums, et traitez les objectifs de temps de restauration comme des exigences de production.

Checklists / plan étape par étape

Checklist A : triage à 3 h du matin (valide pour les deux)

  1. Confirmez le périmètre d’impact : service unique ou tout ? Est-ce latence, erreurs, ou correction des données ?
  2. Vérifiez la saturation de l’hôte : CPU, mémoire, I/O wait, disque plein.
  3. Vérifiez la vivacité de la base : pouvez-vous vous connecter ? Les requêtes progressent-elles ou sont-elles bloquées ?
  4. Identifiez les principaux coupables : requêtes les plus longues, chaînes d’attente de verrous, tempêtes de connexions.
  5. Vérifiez l’état de la réplication avant tout basculement : retard, threads arrêtés, rétention WAL, etc.
  6. Arrêtez l’hémorragie : tuez les requêtes hors de contrôle, mettez en pause les jobs batch, limitez le trafic, réduisez la charge.
  7. Puis ajustez : changements de config prudents qui réduisent la pression ; évitez les compromis de correction/durabilité.
  8. Prenez une décision de récupération : rester et stabiliser vs basculer vs restaurer.

Checklist B : étapes de « stabilisation sûre » PostgreSQL

  1. Trouvez les bloqueurs et terminez-les si nécessaire (surtout DDL longs retenant des verrous).
  2. Annulez les requêtes hors de contrôle pour réduire I/O et contention de verrous.
  3. Vérifiez l’archivage WAL et la rétention des slots pour éviter des cascades disque-plein.
  4. Vérifiez la santé de l’autovacuum sur les tables les plus chaudes ; ajustez par-table si besoin.
  5. Confirmez la pression des checkpoints ; ajustez WAL/checkpoint lors d’une fenêtre calme, pas en panique à moins que l’alternative soit une panne.

Checklist C : étapes de « stabilisation sûre » Percona Server

  1. Confirmez quel nœud est le primaire écrivable ; vérifiez que l’application y écrit réellement.
  2. Vérifiez les threads de réplication ; ne promouvez pas une réplique cassée.
  3. Inspectez le statut InnoDB : transactions longues, pression de purge, deadlocks.
  4. Réduisez les tempêtes de connexions ; envisagez des caps temporaires ou des corrections de pooling.
  5. Validez les réglages de durabilité avant de les modifier ; si vous les changez, documentez le risque et le plan de retour arrière.

Étapes pratiques : pratiquer les restaurations (ce qui rend 3 h du matin vivable)

  1. Choisissez un jeu de sauvegarde production et restaurez-le dans un environnement isolé.
  2. Pour PostgreSQL : restaurez la base backup, rejouez le WAL jusqu’à un timestamp cible, exécutez des checks d’intégrité (requêtes + tests smoke applicatifs).
  3. Pour Percona/XtraBackup : restaurez l’arborescence, appliquez les logs (prepare), démarrez le serveur, appliquez les binlogs/GTID selon le besoin pour le PITR, validez avec des smoke tests.
  4. Mesurez le temps de restauration et documentez-le comme votre vrai RTO.
  5. Répétez jusqu’à ce que vous puissiez le faire depuis un runbook sans improvisation.

FAQ

1) Si je ne me préoccupe que des opérations à 3 h du matin, devrais-je choisir PostgreSQL ?

Si vous partez de zéro et que vous valorisez des sémantiques de récupération prévisibles, oui, PostgreSQL est souvent le choix le plus sûr.
Mais si votre équipe a déjà des runbooks et des outils matures MySQL/Percona, la maturité opérationnelle bat la simplicité théorique.

2) Percona Server est-il plus difficile à exploiter que « MySQL vanilla » ?

Généralement non. Percona Server est typiquement choisi parce qu’il améliore la visibilité opérationnelle et la compatibilité des outils de performance.
La complexité vient des choix de réplication et de topologie MySQL, pas de la marque Percona.

3) Lequel est le plus susceptible de me surprendre par l’utilisation disque ?

PostgreSQL surprend souvent par la rétention WAL (surtout les replication slots) et le bloat si le vacuum est négligé.
Percona/MySQL surprend par la rétention des binlogs, la croissance undo/history, et l’accumulation de sauvegardes.
Dans tous les cas : le disque est un SLO de première classe.

4) Quelle est l’approche de sauvegarde la plus fiable pour chacun ?

PostgreSQL : sauvegardes physiques de base plus archivage WAL pour PITR, avec drills de restauration réguliers.
Percona : stratégie XtraBackup full/incremental plus rétention de binlog pour PITR, avec drills de restauration réguliers et validation GTID.

5) Lequel est plus facile à basculer en toute sécurité ?

Le basculement PostgreSQL est conceptuellement propre (promouvoir une réplique physique), mais vous devez gérer le routage client et le fencing.
Le basculement Percona/MySQL peut être fluide avec GTID et l’automatisation, mais le split-brain et la rupture de réplication sont des modes d’échec plus courants.

6) Quelle est la « self-own » la plus courante à 3 h du matin pour PostgreSQL ?

Laisser s’accumuler transactions longues et problèmes de vacuum jusqu’à ce que le bloat et la contention de verrous se transforment en panne.
Le deuxième cas le plus courant est une archivage WAL mal configuré ou des replication slots oubliés remplissant le disque.

7) Quelle est la « self-own » la plus courante à 3 h du matin pour Percona Server ?

Traiter la réplication asynchrone comme synchrone et promouvoir la mauvaise réplique, ou basculer sans fencing de l’ancien primaire.
Aussi : l’« optimisation » via des compromis de durabilité qui deviennent ensuite perte de données lors d’un crash.

8) Puis-je rendre l’un ou l’autre « simple à 3 h du matin » quel que soit le choix ?

Oui. La recette est ennuyeuse : surveillance cohérente, sauvegardes testées, procédures de basculement standardisées, responsabilité claire des changements de schéma,
et un playbook d’incident qui commence par vérifier la saturation et la correction.

9) Lequel est plus facile pour déboguer la performance des requêtes sous pression ?

PostgreSQL avec pg_stat_statements et des wait events clairs est excellent pour un diagnostic ciblé.
Percona/MySQL dispose aussi d’une forte instrumentation (performance schema, InnoDB status), mais les équipes l’utilisent souvent peu.
Le système le plus simple est celui avec lequel votre équipe pratique mensuellement, pas celui que vous admirez trimestriellement.

Étapes pratiques suivantes

Si vous choisissez entre PostgreSQL et Percona Server sur la base de la « simplicité à 3 h du matin », prenez la décision sur la réalité opérationnelle :
les compétences de votre équipe, votre automatisation, et votre tolérance aux modes de défaillance spécifiques.

  • Choisissez une topologie « golden path » (un primaire, répliques définies, méthode de basculement définie) et interdisez les variations ad-hoc.
  • Rédigez le runbook 3 h du matin maintenant : contrôles de saturation, vérifications de verrous, états de réplication, procédure disque plein, et liste « ne pas faire ».
  • Pratiquez la restauration jusqu’à en faire une corvée routinière. Si ce n’est pas pratiqué, ce n’est pas une sauvegarde.
  • Instrumentez les vrais risques : WAL/slots/vacuum pour Postgres ; threads de réplication/GTID/rétention binlog/purge InnoDB pour Percona.
  • Fensez vos basculements pour empêcher d’écrire sur deux primaires. C’est la différence entre un incident et une catastrophe.

Enfin, soyez honnête sur ce que vous voulez : si vous voulez moins de pièces mobiles et des sémantiques plus cohérentes, PostgreSQL a tendance à être plus calme à 3 h du matin.
Si vous voulez l’écosystème MySQL avec des outils solides et que votre organisation le gère déjà bien, Percona Server peut être tout aussi calme — si vous respectez la réplication et la durabilité.

← Précédent
Shaders programmables : quand le « graphisme » est devenu logiciel
Suivant →
Oublier l’autocollant du refroidisseur : la cause d’overchauffe la plus drôle

Laisser un commentaire