MySQL vs PostgreSQL : sauvegardes et restaurations — qui vous remet en ligne le plus vite

Cet article vous a aidé ?

Quand la base de données est indisponible, personne ne se préoccupe de l’élégance de votre schéma. On veut que la page de paiement réapparaisse vite, que le canal d’incident se calme, et que votre affirmation « nous avons des sauvegardes » survive au contact avec la réalité.

Ceci est un examen pratique et orienté production des sauvegardes et restaurations MySQL et PostgreSQL à travers un seul prisme : le temps pour revenir en ligne. Pas le « temps pour terminer une restauration un jour », mais le temps pour un service capable de reprendre en toute sécurité le trafic — avec la perte de données minimale que vous pouvez honnêtement promettre.

La vraie question : que signifie « revenir en ligne » ?

Les sauvegardes et les restaurations ne sont pas un concours de beauté. Le seul système de notation qui compte est celui que votre entreprise utilise pendant une panne :

  • RTO (Recovery Time Objective) : combien de temps vous pouvez rester en panne.
  • RPO (Recovery Point Objective) : quelle quantité de données vous pouvez perdre.
  • Sécurité : si le système restauré est cohérent et ne risque pas de se corrompre sous charge.
  • Confiance : si le chemin de restauration est répété et éprouvé ou seulement théorique.

« Revenir en ligne » a aussi des niveaux. Soyez explicite.

  • Niveau 0 : le processus de base de données démarre.
  • Niveau 1 : peut accepter du trafic en lecture seule en toute sécurité.
  • Niveau 2 : peut accepter du trafic en lecture/écriture complet, réplication configurée, monitoring au vert.
  • Niveau 3 : rattrapé jusqu’à un horodatage cible (PITR), l’application s’est reconnectée, la file d’attente se vide.

Mon avis ferme : si vos runbooks ne définissent pas le niveau vers lequel vous restaurez, vous n’avez pas de runbooks — vous avez des impressions.

Réponse rapide : qui est plus rapide, quand ?

Pour la plupart des systèmes en production, les sauvegardes physiques gagnent

Si vous cherchez à minimiser le temps d’indisponibilité, les sauvegardes physiques sont la valeur par défaut pour MySQL et PostgreSQL :

  • MySQL : Percona XtraBackup (pour InnoDB) ou équivalents fournisseurs ; plus les binlogs pour le PITR.
  • PostgreSQL : pgBackRest (ou similaire) plus archivage WAL pour le PITR ; ou sauvegardes filesystem/base avec WAL.

Les dumps logiques (mysqldump, pg_dump) ont leur utilité — migrations, restaurations partielles, jeux de données plus petits, portabilité — mais rarement ils sont le moyen le plus rapide pour revenir après un incident majeur.

Quand MySQL vous remet généralement en ligne plus vite

  • Quand vous pouvez promouvoir une réplique existante : MySQL + réplication semi-sync/async peut rendre un basculement quasi instantané si vous avez déjà une réplique saine et que vous acceptez un certain risque RPO. (C’est vrai aussi pour PostgreSQL, mais les outils et pratiques opérationnelles diffèrent.)
  • Quand votre restauration est surtout « copier les fichiers + crash recovery » et que la rattrapage de binlog est court. En pratique, beaucoup de restaurations MySQL sont dominées par la vitesse de copie I/O puis par une étape de crash recovery limitée.
  • Quand votre chaîne d’outils de sauvegarde est mature (XtraBackup en particulier est éprouvé et optimisé pour de grandes fermes InnoDB).

Quand PostgreSQL vous remet généralement en ligne plus vite

  • Quand vous avez besoin d’un PITR précis et fiable jusqu’à un horodatage ou une frontière de transaction. La récupération basée sur WAL de PostgreSQL est simple, bien définie, et l’écosystème (pgBackRest, WAL-G, etc.) est excellent.
  • Quand vous pouvez restaurer et commencer à servir des lectures pendant que la réexécution continue (selon l’architecture et les tolérances). Le comportement de récupération et la visibilité côté PostgreSQL peuvent être plus prévisibles avec les bons paramètres et le bon monitoring.
  • Quand vous comptez sur les checksums + discipline WAL et voulez des garde-fous plus solides contre la corruption silencieuse. Ça n’accélère pas magiquement les restaurations, mais cela évite des incidents « restauration réussie, données pourries » qui coûtent des heures.

La vérité inconfortable

Dans de véritables pannes, le moteur de base de données est rarement le principal retard. L’horloge est souvent tuée par :

  • la copie de téraoctets sur un lien saturé,
  • la décompression sur un profil CPU inadapté,
  • la réexécution de journaux sur un I/O aléatoire lent,
  • l’attente que DNS/pools de connexions/déploiements d’applications remarquent le nouveau primaire,
  • ou la découverte que vos « sauvegardes » manquaient un élément crucial (WAL/binlogs, clés, configs, utilisateurs, grants).

Choisissez la base de données que vous aimez. Mais pour la vitesse de récupération, choisissez l’architecture de restauration : répliques, sauvegardes physiques, conservation WAL/binlog, et répétitions.

Faits intéressants et contexte historique (court et utile)

  1. InnoDB est devenu le défaut de MySQL dans MySQL 5.5, et avec lui une longue période où les outils de sauvegarde physiques importaient plus que les dumps logiques pour les gros systèmes.
  2. PostgreSQL a introduit la réplication en streaming dans la version 9.0, ce qui a poussé beaucoup d’organisations à passer de la mentalité « restaurer depuis une sauvegarde » à « promouvoir une primaire de secours ».
  3. WAL (PostgreSQL) et binlogs (MySQL) résolvent des problèmes similaires, mais les attentes opérationnelles diffèrent : l’archivage WAL est souvent traité comme une exigence PITR de première classe ; la gestion des binlogs est parfois vue comme « optionnelle jusqu’à ce que ce ne le soit plus ».
  4. Percona XtraBackup a popularisé les sauvegardes physiques à chaud pour MySQL à grande échelle, surtout quand arrêter pour sauvegarder n’était pas acceptable.
  5. Le modèle base backup + replay WAL de PostgreSQL est stable depuis des années, c’est pourquoi les outils se concurrencent surtout sur l’ergonomie, l’efficacité de stockage et la vérification — moins sur la correction.
  6. Le support des checksums dans PostgreSQL (checksums au niveau du cluster) a poussé davantage d’équipes à détecter la corruption plus tôt ; restaurer rapidement, c’est bien, restaurer des données correctes, c’est mieux.
  7. Les GTID MySQL ont changé la manière dont les équipes raisonnent sur la promotion et la récupération basée sur les binlogs — moins « quel fichier/position », plus « quel ensemble de transactions ». Cela peut accélérer la prise de décision en cas de basculement si c’est bien utilisé.
  8. La compression est devenue un impôt au moment de la restauration à mesure que les sauvegardes ont migré vers le stockage objet. Le niveau de stockage le moins cher est souvent la chose la plus lente entre vous et votre RTO.

Types de sauvegardes qui comptent pour la vitesse de récupération

Sauvegardes physiques (au niveau fichier) : votre arme principale contre l’indisponibilité

Les sauvegardes physiques copient les fichiers réels de la base de données (ou des deltas au niveau page) et restaurent en les remettant en place. La vitesse de restauration tend à évoluer selon :

  • le débit séquentiel lecture/écriture (stockage et réseau),
  • le CPU pour la décompression et les checksums,
  • la vitesse d’application des journaux (WAL/binlog),
  • et le nombre de fichiers créés (les métadonnées filesystem sont un véritable fléau).

Chez MySQL, les sauvegardes physiques signifient typiquement des copies de datadir produites par XtraBackup plus la gestion redo/undo. Chez PostgreSQL, cela signifie une base backup plus des segments WAL pour atteindre un point cible.

Sauvegardes logiques (dumps SQL) : l’option lente mais portable

Les restaurations logiques sont plus lentes parce que vous reconstruisez les données via des instructions SQL. Cela implique :

  • le parsing SQL,
  • la reconstruction d’index et de contraintes,
  • l’écriture de nombreuses petites transactions sauf si vous optimisez,
  • et potentiellement la génération intense de WAL/binlog pendant la restauration.

Ils restent utiles pour :

  • restaurations partielles (un schéma, une table),
  • migrations inter-version/inter-moteur,
  • instantanés audités et lisibles par l’humain,
  • et comme filet de sécurité contre les bugs d’outils de sauvegarde physiques.

Récupération basée sur la réplication : la restauration la plus rapide est l’absence de restauration

Si vous pouvez promouvoir une instance de secours/réplique, vous pouvez souvent battre toute restauration à partir d’une sauvegarde. Mais vous payez ce luxe par :

  • une maintenance opérationnelle continue (lag de réplication, changements de schéma, maintenance),
  • des modes de panne où les réplicas partagent la même corruption ou les mêmes écritures erronées,
  • et le besoin de récupération d’erreurs logiques (PITR) quand les données sont fausses mais cohérentes.

Blague #1 : Les sauvegardes, c’est comme une assurance — coûteuses, ennuyeuses, et le jour où vous en avez besoin n’est jamais le bon jour pour apprendre les détails de la police.

Chemin de récupération MySQL : ce qui prend réellement du temps

Que signifie « restaurer » dans une panne MySQL

Pour MySQL (axé InnoDB, ce qui couvre la plupart des systèmes réels), la récupération se décompose généralement ainsi :

  1. Obtenir une copie physique cohérente du datadir (depuis le stockage de sauvegarde) sur un serveur cible.
  2. Préparer/appliquer le redo si votre outil de sauvegarde l’exige (préparation XtraBackup).
  3. Démarrer mysqld et laisser la récupération après crash se terminer.
  4. Appliquer les binlogs si vous faites du PITR au-delà du snapshot de la sauvegarde.
  5. Reconstruire la topologie de réplication (GTID ou fichier/position), vérifier, et basculer le trafic.

Les plus gros consommateurs de temps (MySQL)

  • Décompression + écritures de fichiers : si votre sauvegarde est compressée et que votre hôte de restauration a moins de cœurs (ou un CPU faible), la lenteur vous surprendra.
  • Nombreux petits fichiers : les tablespaces par table (fichiers .ibd) peuvent provoquer une tempête de métadonnées lors de la restauration. Le tuning XFS/EXT4 aide, mais c’est de la physique et des appels système.
  • Application des logs redo : les workloads à forte écriture génèrent plus de redo ; la « préparation » peut durer, tout comme la récupération après crash au démarrage.
  • Rattrapage de binlog : si vous appliquez des heures de binlogs, vous refaites en gros votre trafic. Le goulot peut être l’application mono-thread, la latence disque, ou le volume tout simplement.

Ce que MySQL fait bien pour la vitesse de récupération

MySQL peut démarrer très rapidement si vous restaurez une sauvegarde préparée sur un stockage rapide et acceptez que la récupération après crash et le warmup du buffer pool se fassent à chaud. Avec la bonne topologie, vous pouvez aussi éviter les restaurations en promouvant une réplique.

Angles vifs MySQL ressentis pendant les restaurations

  • Hypothèses sur la rétention des binlogs : « on garde les binlogs 24 heures » est un plan jusqu’à ce que vous ayez besoin de 30 heures.
  • Mauvaise configuration GTID : un basculement basé sur GTID est rapide quand il est correct et confus quand il est à moitié correct.
  • Dérive des paramètres InnoDB : restaurer sur un serveur avec innodb_log_file_size, chiffrement, ou taille de page différents peut transformer une restauration propre en longue session de debug.

Chemin de récupération PostgreSQL : ce qui prend réellement du temps

Que signifie « restaurer » dans une panne PostgreSQL

La récupération physique PostgreSQL est conceptuellement claire :

  1. Restaurer une base backup (les fichiers du cluster).
  2. Fournir les segments WAL pour le faire avancer (depuis l’archive ou la source streaming).
  3. Rejouer les WAL jusqu’à la cible (dernier, horodatage, LSN, ou point de restauration nommé).
  4. Promouvoir et rattacher clients/réplicas.

Les plus gros consommateurs de temps (PostgreSQL)

  • Restauration de la base backup : même physique — réseau, débit disque, décompression.
  • Rejeu des WAL : les systèmes à forte écriture génèrent beaucoup de WAL ; le replay peut être limité par l’I/O aléatoire et le comportement fsync.
  • Comportement des checkpoints : la configuration et la charge influencent la douleur de la récupération après crash et du replay.
  • Objets volumineux et index gonflés : la restauration peut réussir, mais « revenir en ligne » peut nécessiter VACUUM ou REINDEX après coup.

Ce que PostgreSQL fait bien pour la vitesse de récupération

Le PITR est traité comme une fonctionnalité de première classe. Vous pouvez choisir « restaurer au moment avant le déploiement » avec moins d’acrobaties, et l’écosystème d’outils tend à valider la continuité WAL de manière agressive. Cela réduit le temps perdu à se demander « on a restauré… pourquoi il manque des transactions ? »

Angles vifs PostgreSQL ressentis pendant les restaurations

  • Lacunes dans l’archive WAL : un segment manquant et vous ne faites pas du PITR ; vous faites de l’archéologie.
  • Justesse du restore_command : un bug subtil de quoting/chemin peut bloquer la récupération indéfiniment.
  • Confusion de timeline : les basculements répétés créent des timelines ; restaurer la mauvaise timeline est un classique « ça démarre, mais c’est incorrect ».

Une idée paraphrasée de Werner Vogels (CTO d’Amazon) : Tout échoue, tout le temps ; les systèmes résilients l’anticipent et récupèrent vite (idée paraphrasée).

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

Voici les tâches que j’exécute réellement pendant les restaurations et les drills. Chaque tâche inclut : la commande, ce que signifie la sortie, et la décision à prendre.

Task 1: Measure disk throughput on the restore target (Linux)

cr0x@server:~$ sudo fio --name=restore-write --filename=/var/lib/db-restore.test --size=8G --bs=1M --rw=write --iodepth=16 --direct=1
restore-write: (g=0): rw=write, bs=(R) 1024KiB-1024KiB, (W) 1024KiB-1024KiB, (T) 1024KiB-1024KiB, ioengine=psync, iodepth=16
...
  write: IOPS=720, BW=720MiB/s (755MB/s)(8192MiB/11374msec)

Signification : Votre bande passante d’écriture séquentielle est d’environ 720MiB/s. C’est le plafond pour « restaurer une base backup » si vous êtes lié par l’I/O.

Décision : Si le BW est <200MiB/s pour des restaurations de plusieurs To, cessez de prétendre que votre RTO est de l’ordre des minutes. Déplacez la restauration vers des disques plus rapides, restaurez depuis des snapshots locaux, ou promotionnez une réplique.

Task 2: Check filesystem free space and inode pressure

cr0x@server:~$ df -h /var/lib/mysql
Filesystem      Size  Used Avail Use% Mounted on
/dev/nvme0n1p2  3.5T  1.2T  2.3T  35% /var/lib/mysql
cr0x@server:~$ df -i /var/lib/mysql
Filesystem       Inodes  IUsed    IFree IUse% Mounted on
/dev/nvme0n1p2  244M    18M     226M    8% /var/lib/mysql

Signification : Vous avez de l’espace et beaucoup d’inodes. Sur MySQL avec fichiers par table, l’épuisement d’inodes est un vrai tueur de restauration.

Décision : Si l’utilisation d’inodes est élevée (>80%), restaurez sur un filesystem avec plus d’inodes ou consolidez les tablespaces (solution long terme). Ne « sentez pas que ça rentrera ».

Task 3: Verify backup integrity metadata (pgBackRest)

cr0x@server:~$ pgbackrest --stanza=prod check
stanza: prod
status: ok

Signification : Le repo et la stanza sont joignables et suffisamment cohérents pour que pgBackRest ait confiance.

Décision : Si cela échoue pendant un incident, basculez immédiatement vers votre emplacement secondaire de sauvegarde ou stratégie de snapshot. Ne déboguez pas la config du repo pendant que le CEO rafraîchit les dashboards.

Task 4: List available PostgreSQL backups and pick the right one

cr0x@server:~$ pgbackrest --stanza=prod info
stanza: prod
    status: ok
    cipher: none
    db (current)
        wal archive min/max (15): 0000001200000A1B000000C0/0000001200000A1C0000002F
        full backup: 2025-12-28-010001F
            timestamp start/stop: 2025-12-28 01:00:01 / 2025-12-28 01:12:33
            database size: 2.1TB, backup size: 820GB
            repo1: backup set size: 820GB, backup size: 820GB

Signification : Vous avez une sauvegarde pleine récente, et la couverture WAL existe entre min/max.

Décision : Choisissez la sauvegarde la plus récente qui possède encore le WAL complet jusqu’à votre temps de récupération cible. Si WAL max est en retard par rapport à l’horodatage de l’incident, votre RPO vient de s’aggraver — dites-le tôt.

Task 5: Restore PostgreSQL to a target timestamp (PITR)

cr0x@server:~$ sudo pgbackrest --stanza=prod --delta --type=time --target="2025-12-28 14:32:00" restore
INFO: restore command begin 2.53: --delta --stanza=prod --target=2025-12-28 14:32:00 --type=time
INFO: restore size = 2.1TB, file total = 2143
INFO: restore command end: completed successfully

Signification : Les fichiers sont restaurés ; la base va rejouer les WAL au démarrage jusqu’au temps cible.

Décision : Si la restauration est « réussie » mais que le démarrage bloque, votre goulot est probablement la récupération des WAL ou l’I/O de replay. Passez au playbook de diagnostic au lieu de relancer les restaurations.

Task 6: Watch PostgreSQL recovery progress

cr0x@server:~$ sudo -u postgres psql -c "select pg_is_in_recovery(), pg_last_wal_replay_lsn(), pg_last_xact_replay_timestamp();"
 pg_is_in_recovery | pg_last_wal_replay_lsn | pg_last_xact_replay_timestamp
-------------------+------------------------+------------------------------
 t                 | 0/A1C3F9B0             | 2025-12-28 14:29:17+00
(1 row)

Signification : Toujours en récupération ; vous pouvez quantifier le retard via l’horodatage/LSN.

Décision : Si l’horodatage n’avance pas, suspectez un restore_command bloqué, un WAL manquant, ou une saturation I/O. S’il avance lentement, optimisez l’environnement de replay WAL (disques plus rapides, moins de goulots) plutôt que de toucher au SQL.

Task 7: Check for missing WAL segments (PostgreSQL logs)

cr0x@server:~$ sudo tail -n 20 /var/log/postgresql/postgresql-16-main.log
2025-12-28 14:35:02 UTC [21741] LOG:  entering standby mode
2025-12-28 14:35:02 UTC [21741] LOG:  restored log file "0000001200000A1B000000F1" from archive
2025-12-28 14:35:05 UTC [21741] LOG:  could not restore file "0000001200000A1B000000F2" from archive: No such file or directory
2025-12-28 14:35:05 UTC [21741] LOG:  waiting for WAL to become available at 0/A1BF0000

Signification : Lacune d’archive. La récupération est bloquée.

Décision : Localisez le WAL manquant (archive secondaire, problème de lifecycle sur le stockage objet), ou acceptez un RPO plus dégradé et restaurez à un point antérieur. N’attendez pas indéfiniment « au cas où ».

Task 8: Validate MySQL binlog availability for PITR

cr0x@server:~$ mysql -uroot -p -e "SHOW BINARY LOGS;"
+------------------+-----------+
| Log_name         | File_size |
+------------------+-----------+
| binlog.000812    | 1073741824|
| binlog.000813    | 1073741824|
| binlog.000814    |  512345678|
+------------------+-----------+

Signification : Des binlogs existent sur ce serveur (ou l’endroit que vous vérifiez). La liste suggère une rétention.

Décision : Confirmez que vous avez les binlogs couvrant la fenêtre d’incident. Si binlog.000811 est requis mais manquant, vous ne pouvez pas faire de PITR à travers cette frontière.

Task 9: Determine MySQL GTID state before re-pointing replication

cr0x@server:~$ mysql -uroot -p -e "SHOW VARIABLES LIKE 'gtid_mode'; SHOW MASTER STATUS\G"
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| gtid_mode     | ON    |
+---------------+-------+
*************************** 1. row ***************************
             File: binlog.000814
         Position: 83422119
     Executed_Gtid_Set: 4a9b1c9c-8b60-11ee-9f4e-0242ac120002:1-987654321

Signification : GTID est activé ; vous avez un ensemble exécuté spécifique à comparer entre serveurs.

Décision : Utilisez l’auto-positionnement basé sur GTID si possible. Si GTID est DÉSACTIVÉ sur des parties de votre flotte, le basculement devient plus lent et risqué — planifiez de corriger cela hors incident.

Task 10: Prepare a MySQL XtraBackup restore (apply redo)

cr0x@server:~$ sudo xtrabackup --prepare --target-dir=/backups/mysql/full-2025-12-28
xtrabackup: starting shutdown with innodb_fast_shutdown = 1
xtrabackup: completed OK!

Signification : La sauvegarde est cohérente et prête à être copiée. Si cette étape est lente, l’application du redo est lourde.

Décision : Si la préparation prend une éternité, acceptez un RTO plus long ou changez la cadence/stratégie de sauvegarde (incrémentales, full plus fréquents, ou récupération basée sur réplique).

Task 11: Copy back MySQL datadir and set permissions correctly

cr0x@server:~$ sudo systemctl stop mysql
cr0x@server:~$ sudo rsync -aHAX --info=progress2 /backups/mysql/full-2025-12-28/ /var/lib/mysql/
      1.23T  42%  610.23MB/s    0:35:12 (xfr#214, to-chk=1290/2143)
cr0x@server:~$ sudo chown -R mysql:mysql /var/lib/mysql

Signification : La restauration est limitée par l’I/O ; la vitesse rsync vous donne une ETA. Les permissions sont corrigées pour que mysqld puisse lire les fichiers.

Décision : Si la vitesse rsync est bien inférieure à la vitesse disque attendue, suspectez le chemin réseau, un throttling, ou l’overhead des petits fichiers. Envisagez une restauration depuis un snapshot local ou un volume attaché plutôt que sur le réseau.

Task 12: Start MySQL and confirm crash recovery behavior

cr0x@server:~$ sudo systemctl start mysql
cr0x@server:~$ sudo tail -n 20 /var/log/mysql/error.log
2025-12-28T14:40:01.120345Z 0 [Note] InnoDB: Starting crash recovery.
2025-12-28T14:40:12.551231Z 0 [Note] InnoDB: Crash recovery finished.
2025-12-28T14:40:13.883412Z 0 [Note] mysqld: ready for connections.

Signification : La récupération après crash s’est terminée rapidement ; le service accepte des connexions.

Décision : Si la récupération prend longtemps ou boucle, vos logs/redo peuvent être incompatibles (restauration incorrecte, mauvais paramètres innodb, copie partielle). Arrêtez et validez les étapes de sauvegarde/restauration au lieu de redémarrer en boucle.

Task 13: Apply MySQL binlogs to reach a target point

cr0x@server:~$ mysqlbinlog --start-datetime="2025-12-28 14:00:00" --stop-datetime="2025-12-28 14:32:00" /var/lib/mysql/binlog.000812 /var/lib/mysql/binlog.000813 | mysql -uroot -p
cr0x@server:~$ echo $?
0

Signification : L’application des binlogs a réussi (code de sortie 0). C’est votre étape PITR.

Décision : Si cela échoue, décidez vite : corriger et relancer (si possible), ou accepter un point de restauration différent. N’appliquez pas des binlogs partiels et appelez cela « assez bon » à moins d’aimer les séances de thérapie de l’intégrité des données.

Task 14: Verify PostgreSQL can accept writes and is on the expected timeline

cr0x@server:~$ sudo -u postgres psql -c "select pg_is_in_recovery(); select timeline_id from pg_control_checkpoint();"
 pg_is_in_recovery
-------------------
 f
(1 row)

 timeline_id
------------
         19
(1 row)

Signification : La récupération est terminée, et vous pouvez suivre sur quelle timeline vous êtes.

Décision : Si les clients échouent encore, la base est probablement saine et votre goulot est ailleurs : DNS, load balancer, pool de connexions, secrets, ou règles de pare-feu.

Task 15: Find replication lag / apply delay (PostgreSQL standby)

cr0x@server:~$ sudo -u postgres psql -c "select client_addr, state, sync_state, write_lag, flush_lag, replay_lag from pg_stat_replication;"
 client_addr |  state  | sync_state | write_lag | flush_lag | replay_lag
-------------+---------+------------+-----------+-----------+-----------
 10.0.2.41    | streaming | async    | 00:00:00  | 00:00:01  | 00:00:03
(1 row)

Signification : Le standby est presque rattrapé ; le replay lag est minime.

Décision : Si le replay lag est de minutes/heures, promouvoir ce standby peut compromettre votre RPO. Choisissez un autre standby, ou restaurez à partir d’une sauvegarde avec WAL vers un point plus sûr.

Task 16: Confirm MySQL replication status after failover

cr0x@server:~$ mysql -uroot -p -e "SHOW SLAVE STATUS\G" | egrep "Slave_IO_Running|Slave_SQL_Running|Seconds_Behind_Master|Last_Error"
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Seconds_Behind_Master: 0
Last_Error:

Signification : La réplique est saine et rattrapée.

Décision : Si le thread SQL est arrêté avec une erreur, ne sautez pas aveuglément des transactions. Décidez si l’intégrité des données ou la récupération rapide est prioritaire, documentez le compromis et planifiez le nettoyage.

Playbook de diagnostic rapide : trouver le goulot en quelques minutes

Les restaurations échouent de façons ennuyeuses. L’objectif est d’identifier laquelle de ces façons ennuyeuses vous concerne aujourd’hui.

Première étape : êtes-vous bloqué sur des journaux manquants (WAL/binlogs) ?

  • PostgreSQL : vérifiez les logs pour « could not restore file … waiting for WAL. » Si présent, vous n’êtes pas lent — vous êtes bloqué.
  • MySQL : confirmez la couverture des binlogs pour l’intervalle cible. Si vous ne trouvez pas les binlogs nécessaires, le PITR est impossible à travers cette lacune.

Deuxième étape : le goulot est-il le débit (réseau/disque) ou le replay (CPU/I/O aléatoire) ?

  • Si la copie de la base backup est lente, mesurez le débit réseau et disque. Surveillez iostat, résultats fio, progression rsync.
  • Si la copie est rapide mais que le démarrage/récupération est lent, c’est le replay/checkpoint/crash recovery. Mesurez la progression du replay WAL (PostgreSQL) ou les logs de crash recovery et la latence I/O (MySQL).

Troisième étape : la base est-elle « up » mais le service ne l’est pas ?

  • Pools de connexions pointant vers d’anciens hôtes.
  • TTL DNS et caches obsolètes.
  • Secrets non déployés dans l’environnement de restauration.
  • Pare-feu/groupes de sécurité manquants pour la réplication/l’accès applicatif.

Quatrième étape : la performance est-elle le nouvel état de panne ?

  • Cache froid : buffer pools et shared buffers vides.
  • Autovacuum, checkpoints ou purge rattrapant leur retard.
  • Index manquants parce que quelqu’un a restauré depuis un dump logique partiel.

Blague #2 : La restauration la plus rapide est celle que vous avez testée la semaine dernière — étrangement, la base de données aime qu’on s’occupe d’elle.

Trois mini-histoires d’entreprise issues du terrain

Mini-histoire 1 : L’incident causé par une fausse hypothèse

L’entreprise avait un primaire MySQL et deux réplicas. Ils avaient aussi des « sauvegardes de binlogs », ce qui signifiait qu’un cron copiait les fichiers binlog vers un stockage objet. Tout le monde dormait bien.

Puis un déploiement a mal tourné. Un batch s’est exécuté en production avec la mauvaise clause WHERE. Les données étaient cohérentes, la réplication a fidèlement propagé les dégâts, et au moment où quelqu’un a remarqué, le problème était déjà sur chaque nœud.

L’équipe a décidé d’un PITR : restaurer la sauvegarde physique de la nuit précédente, puis appliquer les binlogs jusqu’« juste avant le déploiement ». Facile. Sauf que les binlogs avaient été tournés et purgés agressivement sur le primaire pour économiser de l’espace, et le cron ne copiait que les binlogs « en cours ». Les anciens étaient partis. Pas « difficiles à trouver ». Parti.

Ils ont restauré la sauvegarde de la nuit et accepté un RPO qu’ils n’avaient jamais écrit. La partie douloureuse n’était pas la perte de données ; c’était que la perte était évitable. L’hypothèse était : « si on sauvegarde les binlogs, on peut faire du PITR. » La réalité était : « si on conserve une chaîne continue de binlogs, on peut faire du PITR. »

La correction fut ennuyeuse : rétention explicite des binlogs basée sur le RPO, plus un job de vérification qui contrôlait la continuité et alertait en cas de lacunes. Le prochain incident fut moins dramatique parce qu’il fut moins surprenant.

Mini-histoire 2 : L’optimisation qui s’est retournée contre eux

Une équipe PostgreSQL voulait des sauvegardes plus rapides. Ils ont augmenté la compression et déplacé les sauvegardes vers du stockage moins cher. Les fenêtres de sauvegarde ont rétréci, la facture mensuelle a baissé, et quelqu’un a eu des applaudissements en réunion de planification.

Des mois plus tard, une panne de stockage a éliminé le primaire et le standby dans le même rack. Ils ont restauré sur un hôte neuf et découvert une nouvelle vérité : le temps de restauration n’est pas le temps de sauvegarde. La restauration signifiait tirer des téraoctets depuis un stockage un peu froid, décompresser des archives fortement compressées sur une instance CPU plus petite, puis rejouer une montagne de WAL.

Ils ont récupéré la base. Ça a juste pris assez de temps pour faire repenser à l’entreprise ce que « haute disponibilité » signifiait réellement.

La décision post-mortem fut simple : garder une « couche de restauration rapide » pour la sauvegarde pleine la plus récente et le WAL le plus récent, même si la rétention long terme reste bon marché et compressée. Ils ont aussi verrouillé les profils des hôtes de restauration pour correspondre aux besoins de décompression. La compression est un outil, pas une vertu en soi.

Mini-histoire 3 : La pratique ennuyeuse mais correcte qui a sauvé la mise

Une plateforme de paiements utilisait PostgreSQL avec pgBackRest. Chaque semaine, un ingénieur exécutait un drill de restauration dans un environnement isolé. Pas un grand exercice avec les dirigeants — juste un exercice tranquille : restaurer la sauvegarde de la nuit précédente, rejouer les WAL jusqu’à un horodatage choisi, lancer un jeu de requêtes de validation, puis supprimer l’environnement.

Ça paraissait presque ridicule. La restauration fonctionnait toujours. Les requêtes collaient toujours aux attentes. Les tickets se répétaient.

Puis une mise à jour a introduit un problème intermittent de permissions d’archive WAL. Le WAL était généré, mais archive_command échouait parfois. Le drill l’a détecté en quelques jours parce que l’instance restaurée ne pouvait pas atteindre le temps cible. La correction a été appliquée avant qu’un incident réel ne dépende de ces WAL.

Plus tard, une panne réelle est survenue : un humain a supprimé un attachement de volume dans la mauvaise console. Le playbook de drill de restauration était déjà rédigé, le profil de l’hôte de restauration déjà connu, et l’équipe n’a pas eu à redécouvrir son propre système à 2 h du matin. La récupération fut rapide précisément parce qu’elle était ennuyeuse.

Erreurs courantes : symptômes → cause racine → correctif

1) « Restauration réussie » mais PostgreSQL n’atteint pas le temps cible

Symptômes : pgBackRest restore termine ; Postgres démarre ; les logs montrent waiting for WAL ; pg_last_xact_replay_timestamp cesse d’avancer.

Cause racine : Segment(s) WAL manquant(s) dû à une lacune d’archive, suppression par lifecycle, ou archive_command/restore_command mal configuré.

Correctif : Trouver le WAL dans une archive secondaire ; réparer restore_command ; ajuster la rétention pour couvrir le RPO ; ajouter des contrôles continus de lacune WAL dans le monitoring.

2) MySQL est up, mais la récupération après crash prend une éternité

Symptômes : mysqld démarre mais reste indisponible ; le log d’erreur montre une récupération prolongée ; la latence disque monte en flèche.

Cause racine : Restauration sur stockage lent ; redo logs lourds ; sauvegarde non préparée correctement ; ou paramètres innodb différents provoquant du travail supplémentaire.

Correctif : Valider l’étape XtraBackup prepare ; restaurer sur des disques plus rapides ; s’assurer que innodb_log_file_size et paramètres pertinents correspondent à l’environnement de sauvegarde ; éviter les redémarrages répétés qui relancent la récupération.

3) La restauration logique « tourne » mais l’ETA est de jours

Symptômes : pg_restore/mysqldump import traîne ; CPU occupé à parser ; WAL/binlog croît rapidement ; la réplication prend du retard.

Cause racine : Mauvais outil pour l’objectif RTO ; restauration mono-thread ; index/contraintes reconstruits dans le pire ordre ; tuning insuffisant.

Correctif : Utiliser des sauvegardes physiques pour le DR ; si vous devez utiliser logique, parallélisez pg_restore quand possible, chargez les données avant les index/contraintes, désactivez le logging inutile quand c’est sûr, et restaurez sur une instance isolée.

4) Des réplicas existent, mais le basculement prend quand même une éternité

Symptômes : Un standby est « là », mais la promotion est retardée ; les erreurs applicatives continuent ; la configuration de réplication est désordonnée.

Cause racine : Pas de cutover automatisé ; TTL DNS trop élevé ; les pools de connexions ne se rafraîchissent pas ; certificats/secrets liés aux noms d’hôtes ; ou la réplication avait un lag au-delà du RPO.

Correctif : S’exercer au basculement ; réduire les TTL de manière sensée ; utiliser des VIP/proxies stables ; s’assurer que les applications se reconnectent ; monitorer le lag de réplication avec des alertes liées au RPO.

5) La restauration est rapide, mais la performance est si mauvaise que c’est pratiquement une panne

Symptômes : La base répond, mais la latence p95 est terrible ; CPU et I/O explosent ; caches froids ; autovacuum/purge provoquent des tempêtes.

Cause racine : Cache froid plus maintenance en arrière-plan plus charge applicative lourde immédiatement après la restauration.

Correctif : Étager le trafic : lecture seule d’abord, puis montée progressive des écritures ; préchauffer les tables/index critiques ; tuner la maintenance en arrière-plan ; envisager de retarder les jobs non critiques jusqu’à stabilisation.

Listes de contrôle / plan pas à pas

Décidez votre stratégie de récupération par type d’incident

  • Perte de matériel/nœud : préférer le basculement vers une réplique/standby (plus rapide), puis reconstruire depuis les sauvegardes.
  • Corruption logique (mauvais déploiement, deletes accidentels) : préférer le PITR depuis une sauvegarde physique + logs.
  • Suspicion de corruption silencieuse : restaurer dans un environnement isolé, valider, puis basculer ; ne pas promouvoir une réplique possiblement corrompue.

Checklist pré-incident (faire ça au calme)

  1. Définir RTO/RPO par service (l’écrire, le mettre dans le runbook).
  2. Implémenter des sauvegardes physiques (XtraBackup / pgBackRest) avec vérification.
  3. S’assurer de la rétention continue des logs : binlogs/WAL couvrent au moins le RPO + marge.
  4. Garder une « couche de restauration rapide » des sauvegardes récentes + logs sur un stockage capable d’atteindre le débit requis.
  5. Provisionner des hôtes de restauration avec assez de CPU pour la décompression et assez d’IOPS pour le replay.
  6. Automatiser ou au moins scripter : restauration, requêtes de validation, et étapes de cutover.
  7. Exécuter des drills de restauration régulièrement ; enregistrer les temps réels et les goulots.

Plan pas à pas pendant l’incident (avec time-box)

  1. Time-box triage (5–10 minutes) : décider si vous basculez ou restaurez. Ne faites pas les deux en même temps sauf si vous avez assez de monde et de discipline.
  2. Verrouiller votre cible : « le plus récent possible » vs « l’instant avant l’incident ». Communiquez l’implication RPO immédiatement.
  3. Valider la continuité des logs : couverture WAL/binlog. Si manquante, ajuster la cible et communiquer.
  4. Lancer la restauration en parallèle avec la préparation applicative : préparer DNS/LB/changes de config pendant que les octets circulent.
  5. Surveiller les métriques : débit de copie, timestamp/LSN de replay, latence disque.
  6. Remettre en ligne par niveaux : lecture seule ou trafic limité d’abord ; puis charge complète quand stable.
  7. Reconstruire la redondance : ajouter réplicas/standbys avant de déclarer victoire ; un seul primaire récupéré est une provocation.

FAQ

1) Lequel restaure plus vite : MySQL ou PostgreSQL ?

Avec des sauvegardes physiques et un stockage adapté, les deux sont « assez rapides » pour de nombreux systèmes. Le gagnant est généralement celui qui dispose de logs continus, d’un runbook testé, et d’une voie de restauration évitant le stockage lent. Si vous imposez des restaurations logiques, les deux paraîtront lentes ; MySQL souffre souvent lors de la reconstruction de schémas/index, PostgreSQL souffre de la génération WAL et du temps de construction d’index. Ne choisissez pas le moteur sur la vitesse des dumps.

2) Promouvoir une réplique est-il toujours plus rapide que restaurer depuis une sauvegarde ?

Presque toujours, oui. Mais cela peut violer le RPO si le lag de réplication existe, et ça n’aide pas en cas de corruption logique qui a été répliquée. Aussi : si les réplicas partagent la même corruption sous-jacente (bugs stockage/firmware), vous pouvez promouvoir des données pourries rapidement.

3) Quelle est la défaillance PITR la plus courante ?

Des logs manquants. Lacunes d’archive WAL chez PostgreSQL ; lacunes de rétention/collecte des binlogs chez MySQL. Les outils ne peuvent pas récupérer ce que vous n’avez pas conservé.

4) Les sauvegardes logiques sont-elles inutiles ?

Non. Elles sont utiles pour la portabilité, les restaurations partielles, les audits et les migrations. Elles ne sont simplement pas l’outil adéquat pour « nous devons être de retour en 15 minutes ». Gardez-les comme ligne de défense secondaire, pas comme plan RTO principal.

5) La compression aide-t-elle ou nuit-elle à la vitesse de restauration ?

Les deux. Elle réduit les octets transférés et stockés, mais augmente le CPU pendant la restauration. Le scénario qui se retourne souvent contre vous : stockage bon marché + forte compression + hôte de restauration sous-dimensionné = restauration la plus lente possible. Mesurez le temps de restauration, pas le temps de sauvegarde.

6) Comment savoir si je suis lié par l’I/O ou le CPU pendant une restauration ?

Surveillez le débit de restauration en parallèle de l’utilisation CPU et de la latence disque. Si le CPU est saturé et les disques calmes, la décompression/checksums dominent. Si le CPU est modéré mais la latence disque élevée et le débit faible, vous êtes I/O-bound. Alors corrigez la bonne chose au lieu de crier sur la base de données.

7) Pour PostgreSQL, puis-je servir du trafic pendant la récupération ?

Vous pouvez servir des lectures depuis un standby en recovery (hot standby) selon la configuration et le cas d’usage. Pour une restauration PITR visant un nouveau primaire, on attend généralement que la récupération atteigne la cible puis on promeut. Si vous avez besoin de lectures rapides, gardez une architecture standby ; ne l’improvisez pas en plein incident.

8) Pour MySQL, quel est le schéma de restauration le plus rapide et sûr ?

Promouvoir une réplique saine pour la disponibilité, puis reconstruire le primaire défaillant depuis une sauvegarde physique en arrière-plan. Si l’incident est une corruption logique, restaurer une sauvegarde physique sur un hôte isolé et appliquer les binlogs avec précaution jusqu’à un point sûr, puis basculer.

9) À quelle fréquence devons-nous exécuter des drills de restauration ?

Assez souvent pour que votre RTO mesuré soit réel. Hebdomadaire pour les systèmes critiques est courant ; mensuel peut suffire si votre environnement est stable. Après des changements majeurs de version, migrations de stockage, mises à jour d’outils de sauvegarde, ou modifications de politiques de rétention, lancez un drill immédiatement.

10) Que doit-on valider après une restauration en plus de « ça démarre » ?

Exécutez les requêtes critiques applicatives, validez les comptes de lignes ou checksums pour les tables clés, confirmez la correction des utilisateurs/grants, vérifiez que la réplication peut être reconstruite, et que le système atteint l’horodatage PITR prévu. « Service démarré » est le Niveau 0 ; ce n’est pas la ligne d’arrivée.

Conclusion : prochaines étapes exécutables

Si votre objectif est de revenir en ligne vite, le débat MySQL vs PostgreSQL est surtout une distraction. Les différences durables sont dans la posture opérationnelle : logs continus, sauvegardes physiques, stockage rapide pour les restaurations, et procédures répétées.

Prochaines étapes pratiques :

  1. Choisissez votre niveau de récupération (Niveau 1/2/3) pour chaque service et écrivez-le avec RTO/RPO.
  2. Standardisez sur les sauvegardes physiques (XtraBackup pour MySQL InnoDB ; pgBackRest pour PostgreSQL) et automatisez la vérification.
  3. Prouvez la continuité des logs : implémentez un job qui vérifie les chaînes WAL/binlog de bout en bout, pas seulement « les fichiers existent ».
  4. Minutage des restaurations sur un matériel réaliste. Mesurez séparément la vitesse de copie et la vitesse de replay.
  5. Gardez une voie de restauration rapide : sauvegardes récentes et logs sur un stockage capable de satisfaire votre RTO, plus hôtes de restauration dimensionnés pour la décompression et l’I/O.
  6. Répétez les drills jusqu’à ce que ce soit ennuyeux. Ennuyeux = rapide. Ennuyeux = fiable. Ennuyeux, c’est ce que vos clients paient.
← Précédent
Héritage des propriétés ZFS : la surprise qui modifie les datasets enfants
Suivant →
Corriger « IOMMU non activé » sur Proxmox pour le passthrough PCI (VT-d/AMD‑Vi) en toute sécurité

Laisser un commentaire