Le basculement est l’épreuve où les bonnes conceptions de réplication sont jugées. Tout paraît propre sur les diagrammes : un primaire, une réplique, une IP virtuelle, peut-être une couche de proxy, et un playbook que personne ne lit avant 3 heures du matin. Puis un disque cale, un réseau flanche, et soudain le « nouveau primaire » refuse les écritures, des répliques pointent vers le mauvais hôte, ou pire : deux primaires acceptent du trafic et votre entreprise invente une nouvelle forme de comptabilité.
MySQL et MariaDB « font de la réplication ». Ils ne basculent pas de la même façon en production. Les différences ne portent presque jamais sur des fonctionnalités en gros titres et sont presque toujours sur le comportement de bord : sémantique GTID, compatibilité des binlogs, tables de métadonnées, hypothèses des proxies, et ce genre de petites plaies opérationnelles qui n’apparaissent qu’en situation de stress.
Vérité fondamentale : la réplication n’est pas le basculement
La réplication déplace des modifications de données de A vers B. Le basculement transfère l’autorité de A vers B, ainsi que le trafic et des garde-fous. Ce sont ces garde-fous qui cachent les cadavres.
En production, le basculement a trois missions :
- Choisir le bon candidat (le plus proche des données actuelles, le plus sain, pas en cours de récupération après crash).
- Le rendre inscriptible (et s’assurer que l’ancien primaire ne puisse pas accepter d’écritures, même s’il revient furieux).
- Basculer les clients (proxies, DNS, VIPs, pools de connexions, caches applicatifs et la logique de réessaie « utile »).
MySQL et MariaDB supportent tous deux la réplication asynchrone. Tous deux peuvent utiliser des GTID (avec des différences importantes). Tous deux proposent des variantes semi-sync. Tous deux peuvent être gérés par des orchestrateurs ou des proxies. Et tous deux peuvent vous trahir si vous traitez la réplication comme une baguette magique au lieu d’un contrat.
Une vérité opérationnelle qui mérite d’être imprimée sur un autocollant : le basculement est une décision de cohérence. Vous tradez toujours entre disponibilité maintenant et exactitude des données plus tard. Vos outils doivent rendre cette décision explicite, pas implicite.
Blague #1 : Le retard de réplication, c’est juste votre base de données qui pratique la pleine conscience — elle vit dans le passé pour ne pas s’inquiéter du futur.
Avant de comparer MySQL et MariaDB, définissons le vocabulaire tel que l’utilisent réellement les SRE :
- Promotion : transformer une réplique en primaire inscriptible.
- Repointage : faire suivre les répliques par le nouveau primaire.
- Fencing : empêcher l’ancien primaire d’accepter des écritures (arrêt, isolement réseau, STONITH, fencing stockage).
- Split brain : deux nœuds acceptent des écritures pour le même jeu de données. Ce n’est pas « un incident », c’est « une nouvelle ère pour vos données ».
- Outil de basculement : Orchestrator, MHA, MaxScale, scripts ProxySQL, Pacemaker/Corosync, opérateurs Kubernetes, ou votre bash maison que vous prétendez « temporaire ».
Faits et historique qui comptent encore
Ce ne sont pas des anecdotes pour un quiz. Ils expliquent pourquoi le basculement de réplication diffère entre MySQL et MariaDB de façon subtile et opérationnelle.
- MariaDB a forké MySQL en 2009 après le parcours d’acquisition de MySQL (Sun, puis Oracle). La divergence a commencé lentement, puis s’est accélérée, notamment autour des GTID et des fonctionnalités de réplication.
- MySQL 5.6 a introduit les GTID comme première fonctionnalité « global transaction ID » grand public chez Oracle MySQL ; MariaDB a implémenté un format et un comportement GTID différents.
- La réplication multi-thread de MySQL a mûri plus tard (notamment 5.7+ avec des améliorations de réplication parallèle, puis des nouveautés en 8.0). MariaDB a poursuivi d’autres réglages et implémentations ; les recettes de tuning ne se transfèrent pas proprement.
- MySQL 5.7 a fait d’InnoDB le moteur par défaut et a poussé des améliorations de fiabilité autour de la récupération après crash et des métadonnées de réplication. MariaDB a aussi amélioré InnoDB (et des variantes comme XtraDB historiquement), mais les symptômes opérationnels diffèrent.
- MySQL 8.0 a changé des valeurs par défaut et supprimé d’anciens comportements (cache de requêtes parti depuis longtemps, mais plus important : plugins d’authentification et comportement des métadonnées). Un basculement entre versions peut échouer pour des raisons qui semblent sans rapport avec la réplication.
- MariaDB a introduit des « domain IDs » dans les GTID qui peuvent être utiles (multi-source) et déroutants (mauvaise gestion des domaines lors du basculement).
- Group Replication/InnoDB Cluster de MySQL est une piste HA de premier plan chez Oracle MySQL ; MariaDB a Galera et sa propre histoire HA. Les gens comparent la réplication asynchrone tout en voulant secrètement un système de consensus.
- Les écosystèmes ont développé des outils opérationnels séparément : Orchestrator est né dans le monde MySQL mais peut fonctionner avec MariaDB ; MaxScale est le proxy de MariaDB avec des comportements orientés MariaDB. Mixer les outils en supposant une parité fonctionnelle est un échec fréquent.
Ce qui casse pendant un basculement (MySQL vs MariaDB)
1) Basculement GTID : « On a les GTID, donc c’est sûr »… jusqu’à ce que ça ne le soit pas
Le basculement basé sur GTID est censé simplifier la promotion : choisir la réplique la plus à jour, la promouvoir et repointer les autres en utilisant l’auto-positionnement GTID. En pratique, GTID ne veut pas dire « pas de perte de données », ça veut dire « meilleur suivi ».
Comportement GTID MySQL tourne généralement autour de gtid_executed, gtid_purged et des ensembles GTID. Les outils de basculement comparent souvent les ensembles GTID de MySQL pour trouver la réplique la plus avancée.
Comportement GTID MariaDB utilise un format GTID différent et inclut des concepts comme les domain IDs et les numéros de séquence. C’est puissant, mais cela rend le « juste comparer les GTID » moins uniforme sur des flottes mixtes.
Ce qui casse :
- Incompatibilités de stricte interprétation GTID : une réplique qui ne peut pas accepter certains GTID après promotion à cause de trous, d’un purge d’historique ou de différences de configuration.
- Outils qui supposent les variables GTID de MySQL et échouent sur MariaDB (ou les interprètent mal).
- Basculement inter-versions : la « meilleure » réplique est sur une version légèrement différente avec des valeurs par défaut différentes ; la promotion réussit, les clients échouent.
2) Les métadonnées de réplication se dérèglent sous stress
Pendant un basculement, vous effectuez beaucoup de transitions d’état rapidement : arrêter la réplication, réinitialiser les infos de réplication, changer de primaire, démarrer la réplication, et parfois reconstruire les relay logs. Les détails diffèrent entre MySQL et MariaDB, y compris la syntaxe des commandes et les champs d’état.
Dans MySQL 8.0, les commandes de réplication ont changé (START REPLICA au lieu de START SLAVE), les noms des statuts ont évolué (SHOW REPLICA STATUS), et les métadonnées sont davantage dans des tables transactionnelles. MariaDB est resté plus longtemps avec la dénomination traditionnelle et a son propre comportement autour des relay logs et des GTID.
Ce qui casse :
- Des runbooks écrits pour une syntaxe qui échouent sur l’autre au milieu de la nuit et ne font rien en silence.
- Les tables d’info de réplication deviennent incohérentes après des commandes partielles (surtout lorsque l’automatisation expire en plein changement).
- Des corruptions de relay log apparaissent après un crash, et votre « promotion simple » se transforme en reconstruction.
3) Semi-sync : latence et réglages de sécurité ne correspondent pas à votre modèle mental
La réplication semi-sync réduit le « gap d’ack » en exigeant qu’au moins une réplique accuse réception avant de renvoyer le commit. Ça ressemble à de la durabilité. Ce n’est pas identique à la durabilité face aux pannes, sauf si vous comprenez la sémantique des acks et les timeouts.
Le plugin semi-sync de MySQL et l’implémentation semi-sync de MariaDB se comportent de façon analogue dans l’esprit, mais opérationnellement vous verrez des compteurs d’état différents et des signatures de défaillance différentes. Le risque est le même : lorsque le semi-sync retombe en asynchrone pendant un événement de turbulence, votre RPO peut changer silencieusement au pire moment.
Ce qui casse :
- Le basculement suppose « aucune perte de données » parce que semi-sync était activé, mais il était déjà dégradé en asynchrone à cause de timeouts.
- Des timeouts trop agressifs provoquent des retours fréquents à l’asynchrone, augmentant la gigue de latence de commit et provoquant des réessaies applicatifs qui amplifient la charge.
4) Proxies et VIPs : la base peut être saine, mais le trafic non
La plupart des basculements échouent au niveau client. Les proxies cachent l’état des backends, les apps cachent le DNS, les pools de connexions fixent les sessions, et certains drivers se comportent comme des enfants quand leur endpoint préféré disparaît.
Les différences MySQL vs MariaDB apparaissent quand vous utilisez des outils de l’écosystème :
- MaxScale (le proxy de MariaDB) peut faire du basculement piloté par un moniteur avec des suppositions spécifiques à MariaDB. Il peut fonctionner avec MySQL, mais vérifiez soigneusement les limites de support.
- Orchestrator (courant dans les flottes MySQL) est excellent mais opiniâtre. Le support MariaDB existe, pourtant des fonctions comme l’interprétation des GTID peuvent différer selon les versions.
- ProxySQL est assez agnostique mais repose sur des health checks et des règles de requêtes ; vous devez définir correctement « writer » et « reader » et garder cela synchronisé avec la logique de promotion.
Ce qui casse : vous promouvez correctement, mais le proxy continue de router les écritures vers l’ancien primaire pendant une minute. Félicitations, vous avez construit un split brain avec des étapes supplémentaires.
5) Non-déterminisme et fantômes de la réplication par instructions
Si vous utilisez encore la réplication par instructions (SBR), vous choisissez la douleur. La réplication par lignes (RBR) est le défaut moderne pour une raison : elle évite les comportements non déterministes qui transforment le basculement en exercice d’expertise judiciaire.
Certaines déploiements MariaDB conservent des formats mixtes pour des raisons historiques. Certaines déploiements MySQL aussi, généralement après une longue migration où le « temporaire » est devenu permanent.
Ce qui casse :
- La réplique applique une instruction différemment à cause du fuseau horaire, de la collation, du SQL mode, ou du comportement d’une fonction entre versions.
- Le basculement révèle une dérive cachée : le nœud promu possède des données subtilement différentes, pas forcément corrompues de façon évidente.
6) « read_only » n’est pas du fencing
MySQL et MariaDB supportent read_only et super_read_only (disponibilité selon la version). Mais ce sont des contrôles consultatifs, pas un fence dur. Des utilisateurs avec des privilèges suffisants peuvent encore écrire dans de nombreux scénarios (surtout sans super_read_only), et des tâches en arrière-plan peuvent aussi écrire.
Les plans de basculement qui reposent sur la mise de read_only=ON sur l’ancien primaire sont des plans qui supposent que l’ancien primaire coopérera. Les anciens primaires ne coopèrent pas. Ils boudent puis acceptent des écritures dès qu’une connexion se faufile.
7) Sauvegarde/restauration pendant le resemis : GTID et paramètres de binlog comptent
Après un basculement, vous devez souvent reconstruire une réplique. Si votre outil de sauvegarde ne préserve pas correctement l’état GTID, ou si vous restaurez avec de mauvais paramètres de binlog, vous vous retrouvez avec une réplique qui ne peut pas rejoindre proprement.
La divergence MySQL vs MariaDB apparaît dans la gestion de l’état GTID, plus des différences de configuration par défaut et de variables de réplication. Des sauvegardes « compatibles MySQL » ne sont pas toujours « compatibles avec le positionnement GTID de MariaDB lors d’un basculement », surtout si vous mélangez des versions.
Réalité des GTID : même acronyme, contrat différent
On dit « on utilise les GTID » comme si c’était une question oui/non. Ce n’est pas le cas. C’est « on utilise les GTID, avec ces contraintes, et on sait ce qui se passe quand ces contraintes cassent ».
GTID MySQL : ensembles et auto-positionnement
Les GTID MySQL sont généralement gérés avec :
gtid_mode=ONenforce_gtid_consistency=ONlog_slave_updates=ON(pour que les répliques puissent devenir primaires)MASTER_AUTO_POSITION=1(auto-positionnement)
Les outils de basculement aiment les GTID MySQL car ils peuvent comparer des ensembles GTID. Mais il y a une nuance : les ensembles GTID vous disent seulement ce qui a été exécuté, pas ce que vos clients croyaient avoir commis si l’ancien primaire est mort en plein vol. Le semi-sync aide, mais il peut se dégrader.
GTID MariaDB : domain IDs et arêtes opérationnelles
Les GTID MariaDB sont différents. Les domain IDs peuvent représenter différents domaines de réplication. Cela peut être utile pour la réplication multi-source et pour suivre des flux. Cela peut aussi introduire des modes de défaillance où un nœud promu a un état GTID qui semble « complet » mais ne correspond pas à ce que d’autres répliques attendent.
MariaDB a aussi des réglages comme le mode strict des GTID (selon la version) qui changent la tolérance des trous. C’est bon pour la correction, mais cela peut rendre les promotions incapables de réussir au moment même où vous souhaitez un atterrissage en douceur.
Que faire avec cette connaissance
Si vous êtes homogène (tout MySQL ou tout MariaDB), choisissez la voie GTID native et engagez-vous. Ne traitez pas les GTID comme une case à cocher de migration. Traitez-les comme une conception : méthode de sauvegarde, reconstruction de réplique, procédure de promotion et surveillance doivent toutes correspondre.
Si vous êtes mixte (MySQL et MariaDB), arrêtez de prétendre que le basculement de réplication est symétrique. Soit découplez l’automatisation de promotion par moteur, soit standardisez sur un seul moteur pour le rôle writer. La réplication inter-moteurs peut fonctionner pour certains cas, mais le basculement est l’endroit où les incompatibilités apparaissent en premier.
Choix de topologie qui changent votre rayon d’impact
Primaire-réplique asynchrone avec basculement manuel : la ligne de base honnête
C’est le système le plus simple qui puisse fonctionner. Il force aussi les humains à comprendre les compromis parce qu’un humain appuie sur le bouton.
Avantages : facile à raisonner ; moins de pièces mobiles. Inconvénients : récupération plus lente ; erreurs humaines ; procédure incohérente sauf si vous vous entraînez.
Asynchrone avec basculement orchestré : l’automatisation qui peut vous blesser plus vite
Le basculement orchestré est excellent quand il est conçu avec du fencing et une sélection prudente des candidats. Il est dangereux quand il se résume à « le moniteur voit le primaire down → promouvoir quelque chose ». Le moniteur n’est pas votre couche de correction des données.
La sélection des candidats devrait considérer :
- Le lag de réplication et le lag d’application (pas seulement que le thread IO tourne)
- Les transactions errantes (surtout avec GTID)
- La santé du serveur (disque plein, récupération après crash, erreurs de système de fichiers)
- La connectivité réseau depuis les clients (un nœud promu dans un sous-réseau mort est une panne très rapide)
Rêves « multi-primaire » : Group Replication, Galera et vérifications de réalité
Si vous avez vraiment besoin d’un basculement automatique sans perte de données, vous cherchez probablement un système basé sur le consensus. Dans l’écosystème MySQL, c’est typiquement Group Replication/InnoDB Cluster. Dans celui de MariaDB, c’est souvent des solutions basées sur Galera.
Mais : ce ne sont pas « de la réplication avec basculement », ce sont des bêtes différentes avec des modes de panne, des caractéristiques de performance et des exigences opérationnelles différentes.
La réplication asynchrone avec basculement peut être parfaitement acceptable si vous acceptez RPO>0 et que vous avez un bon fencing. Elle peut aussi ruiner une carrière si vous prétendez qu’il s’agit de cohérence forte.
Plan de diagnostic rapide
Quand un basculement tourne mal, vous devez trouver rapidement le goulot d’étranglement. Pas le goulot philosophique. La chose concrète qui vous empêche de rétablir le service en écriture de façon sûre.
Première étape : établir l’autorité et prévenir le split brain
- Qui est actuellement inscriptible ? Vérifiez
read_only/super_read_onlyet si les clients écrivent effectivement. - Fencer l’ancien primaire (arrêt, isolation réseau, désactiver la VIP, bloquer au proxy). Si vous ne pouvez pas fencer, vous n’avez pas de basculement ; vous avez un pari.
- Arrêter les boucles d’automatisation qui pourraient re-promouvoir, reparenter ou basculer des VIPs sans fin.
Deuxième étape : trouver le candidat le plus correct
- Comparer les positions GTID (ou binlog file/pos si pas de GTID). Identifier la/les réplique(s) la/les plus proches de l’ancien primaire.
- Vérifier l’état du thread SQL : applique-t-il ? est-il bloqué ? erreur ? attend-il un verrou de métadonnée ?
- Vérifier la santé d’InnoDB : récupération après crash en cours, backlog de flush de pages sales, saturation IO.
Troisième étape : basculer le trafic sans mentir à l’app
- Mettre à jour proxies/VIP/DNS et vérifier depuis plusieurs sous-réseaux applicatifs.
- Confirmer la séparation writer/reader dans les proxies ; s’assurer que les écritures arrivent réellement sur le nouveau primaire.
- Surveiller les taux d’erreur et la latence. Un basculement « réussi » qui provoque un pic de latence ×10 reste une panne — juste plus discrète.
Quatrième étape : réparer l’ensemble de répliques
- Repointer les répliques vers le nouveau primaire et valider qu’elles répliquent proprement.
- Reconstruire toute réplique ayant une corruption de relay log ou des transactions errantes.
- Confirmer que les sauvegardes et la rétention des binlogs suffisent pour de futures reconstructions.
Tâches pratiques : commandes, sorties et la décision que vous prenez
Voici les tâches que vous exécutez réellement pendant un basculement chaotique. Chaque tâche inclut : la commande, ce que signifie une sortie typique, et la décision à prendre. Les commandes sont montrées comme si vous aviez un accès shell et le client MySQL.
Task 1: Identify engine/version quickly (because syntax and variables differ)
cr0x@server:~$ mysql -N -e "SELECT VERSION(), @@version_comment;"
10.6.18-MariaDB-log MariaDB Server
Ce que cela signifie : Vous êtes sur MariaDB 10.6. Ne collez pas des commandes de réplication MySQL 8.0 sans réfléchir.
Décision : Utiliser les champs d’état MariaDB (SHOW SLAVE STATUS) et les variables GTID MariaDB. Ajuster la branche du runbook.
Task 2: Check whether the server is currently writable
cr0x@server:~$ mysql -N -e "SELECT @@read_only, @@super_read_only;"
0 0
Ce que cela signifie : Ce nœud peut accepter des écritures.
Décision : Si ce n’est pas le primaire prévu pendant l’incident, vous devez le fencer ou le basculer en lecture seule immédiatement (et aussi corriger le routage du trafic).
Task 3: Confirm replication threads and lag (MySQL 8 naming shown)
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: 2
Last_SQL_Error:
Ce que cela signifie : La réplication tourne avec un lag minimal.
Décision : Cette réplique est un bon candidat de promotion du point de vue du lag. Vérifier néanmoins l’état GTID et la santé.
Task 4: Same check on MariaDB (field names differ)
cr0x@server:~$ mysql -e "SHOW SLAVE STATUS\G" | egrep "Slave_IO_Running|Slave_SQL_Running|Seconds_Behind_Master|Last_SQL_Error"
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Seconds_Behind_Master: 0
Last_SQL_Error:
Ce que cela signifie : La réplique MariaDB est rattrapée (autant que cette métrique peut l’indiquer).
Décision : Vérifier quand même qu’elle ne ment pas : contrôler les positions GTID/binlog et les logs d’erreurs si le primaire est mort de façon non propre.
Task 5: Check GTID execution state (MySQL)
cr0x@server:~$ mysql -N -e "SELECT @@gtid_executed\G"
*************************** 1. row ***************************
@@gtid_executed: 3E11FA47-71CA-11E1-9E33-C80AA9429562:1-984321
Ce que cela signifie : Ce nœud a exécuté des GTID jusqu’à 984321 pour cet UUID de serveur.
Décision : Comparer entre répliques ; celle avec le superset est généralement le meilleur candidat (sous réserve de transactions errantes et de santé).
Task 6: Check GTID state (MariaDB)
cr0x@server:~$ mysql -N -e "SELECT @@gtid_current_pos, @@gtid_slave_pos;"
0-1-542118 0-1-542118
Ce que cela signifie : Domaine 0, server_id 1, séquence 542118 ; la réplique a appliqué jusqu’à cette position.
Décision : Comparer les répliques candidates. Si vous voyez plusieurs domaines de façon inattendue, arrêtez-vous et cartographiez ce qu’ils représentent avant la promotion.
Task 7: Detect replication break due to missing row (common after unsafe settings)
cr0x@server:~$ mysql -e "SHOW SLAVE STATUS\G" | egrep "Last_SQL_Errno|Last_SQL_Error"
Last_SQL_Errno: 1032
Last_SQL_Error: Could not execute Delete_rows event on table app.orders; Can't find record in 'orders', Error_code: 1032; handler error HA_ERR_KEY_NOT_FOUND
Ce que cela signifie : Il existe une dérive de données ; la réplique ne peut pas appliquer des événements correctement.
Décision : Ne promouvez pas cette réplique sauf si vous acceptez la corruption. Reconstruisez à partir d’une sauvegarde propre ou réparez la dérive délibérément (approches pt-table-checksum ou équivalentes selon votre environnement).
Task 8: Confirm binlog format and durability knobs (promotion readiness)
cr0x@server:~$ mysql -N -e "SELECT @@log_bin, @@binlog_format, @@sync_binlog, @@innodb_flush_log_at_trx_commit;"
1 ROW 1 1
Ce que cela signifie : Logging binaire activé, format row, et réglages plutôt durables (sync_binlog=1, flush à chaque commit).
Décision : Bien. Si vous voyez sync_binlog=0 ou innodb_flush_log_at_trx_commit=2, supposez un risque plus élevé de perte de transactions commises en cas de crash — cela change vos attentes RPO pour le basculement.
Task 9: Check if semi-sync is actually active (MySQL plugin counters)
cr0x@server:~$ mysql -e "SHOW STATUS LIKE 'Rpl_semi_sync_master_status';"
+-----------------------------+-------+
| Variable_name | Value |
+-----------------------------+-------+
| Rpl_semi_sync_master_status | OFF |
+-----------------------------+-------+
Ce que cela signifie : Le semi-sync n’est pas actuellement actif, indépendamment de la configuration. Il est probablement retombé en asynchrone.
Décision : Pendant le basculement, supposez qu’il peut manquer des transactions sur les répliques. Choisissez le candidat sur la base de l’état réellement appliqué, pas de la sécurité espérée.
Task 10: Validate the “old primary” is truly fenced
cr0x@server:~$ ssh db-old-primary "systemctl is-active mysql || systemctl is-active mariadb"
inactive
Ce que cela signifie : Le service MySQL/MariaDB est arrêté sur l’ancien primaire.
Décision : Bien, mais ce n’est pas suffisant si l’hôte peut revenir. Si vous avez des redémarrages automatiques, désactivez-les ou isolez le nœud au niveau réseau aussi.
Task 11: Confirm who clients are actually connected to (catch proxy lies)
cr0x@server:~$ mysql -N -e "SELECT SUBSTRING_INDEX(USER(),'@',-1) AS client_host, COUNT(*) FROM information_schema.processlist GROUP BY 1 ORDER BY 2 DESC LIMIT 5;"
10.12.4.31 87
10.12.4.18 54
10.12.4.22 41
Ce que cela signifie : Beaucoup de connexions depuis des sous-réseaux applicatifs — ce nœud reçoit du trafic.
Décision : Si vous avez promu ce nœud, parfait. Si ce n’est pas le cas, corrigez immédiatement le routage et considérez si des écritures ont eu lieu ici de façon inattendue.
Task 12: On candidate replica, stop replication cleanly before promotion
cr0x@server:~$ mysql -e "STOP REPLICA;"
Query OK, 0 rows affected (0.02 sec)
Ce que cela signifie : La réplication est arrêtée (syntaxe MySQL 8).
Décision : Vous pouvez maintenant évaluer l’état en toute sécurité et promouvoir sans qu’elle continue d’appliquer des événements d’une source possiblement morte.
Task 13: Make the promoted node writable (and verify)
cr0x@server:~$ mysql -e "SET GLOBAL super_read_only=OFF; SET GLOBAL read_only=OFF;"
Query OK, 0 rows affected (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
cr0x@server:~$ mysql -N -e "SELECT @@read_only, @@super_read_only;"
0 0
Ce que cela signifie : Le nœud est inscriptible.
Décision : Ne le faites qu’après avoir fencéd l’ancien primaire. Si vous ne pouvez pas fencer, maintenez le candidat en lecture seule et basculez vers une interruption contrôlée pendant que vous résolvez le fencing.
Task 14: Repoint another replica using GTID auto-positioning (MySQL)
cr0x@server:~$ mysql -e "STOP REPLICA; CHANGE REPLICATION SOURCE TO SOURCE_HOST='db-new-primary', SOURCE_USER='repl', SOURCE_PASSWORD='***', SOURCE_AUTO_POSITION=1; START REPLICA;"
Query OK, 0 rows affected (0.01 sec)
Query OK, 0 rows affected (0.04 sec)
Query OK, 0 rows affected (0.01 sec)
Ce que cela signifie : La réplique suit désormais le nouveau primaire avec positionnement GTID.
Décision : Vérifier immédiatement l’état pour les erreurs et le lag ; si elle ne rattrape pas, vous avez peut-être des transactions errantes ou des GTID purgés.
Task 15: Repoint a MariaDB replica (classic syntax and GTID)
cr0x@server:~$ mysql -e "STOP SLAVE; CHANGE MASTER TO MASTER_HOST='db-new-primary', MASTER_USER='repl', MASTER_PASSWORD='***', MASTER_USE_GTID=slave_pos; START SLAVE;"
Query OK, 0 rows affected (0.01 sec)
Query OK, 0 rows affected (0.05 sec)
Query OK, 0 rows affected (0.01 sec)
Ce que cela signifie : La réplique MariaDB est reconfigurée pour utiliser GTID à partir de sa propre position appliquée.
Décision : Si des erreurs de réplication mentionnent la stricte interprétation des GTID ou des doublons, arrêtez-vous et réconciliez les positions GTID ; ne faites pas du « skip counter » un mode de vie.
Task 16: Verify new primary binlog is enabled (or replicas can’t follow)
cr0x@server:~$ mysql -N -e "SELECT @@log_bin;"
1
Ce que cela signifie : Le binaire logging est activé.
Décision : Si ceci retourne 0, les répliques ne peuvent pas répliquer depuis ce nœud. Soit il est mal configuré, soit vous avez promu le mauvais type d’hôte (profil « read replica »). Corrigez la config et redémarrez, ou choisissez un autre candidat.
Task 17: Check for long transactions blocking apply (often mistaken for “replication lag”)
cr0x@server:~$ mysql -e "SHOW ENGINE INNODB STATUS\G" | egrep -n "TRANSACTIONS|LOCK WAIT|history list length" | head
3:TRANSACTIONS
45:---TRANSACTION 18446744073709551615, not started
112:History list length 987654
Ce que cela signifie : Une longueur de history list très grande peut indiquer un retard de purge, des transactions longues ou une forte charge d’écriture ; cela corrèle souvent avec des difficultés d’application de réplication.
Décision : Avant de promouvoir une réplique en retard, comprenez si elle est à la traîne pour saturation IO, locks ou purge. La « plus avancée mais surchargée » est un piège.
Task 18: Confirm disk saturation (the silent failover killer)
cr0x@server:~$ iostat -x 1 3
Linux 5.15.0 (db-replica-2) 12/29/2025 _x86_64_ (16 CPU)
avg-cpu: %user %nice %system %iowait %steal %idle
12.10 0.00 4.20 38.50 0.00 45.20
Device r/s w/s rkB/s wkB/s await aqu-sz %util
nvme0n1 120.0 980.0 5200.0 42000.0 35.4 18.2 99.8
Ce que cela signifie : Le disque est saturé (%util ~100), await élevé. L’application de réplication et les écritures clients vont souffrir.
Décision : Ne promouvez pas cet hôte si vous avez un autre candidat avec une IO plus saine. S’il est le seul candidat, limitez la charge, augmentez la sécurité du buffer pool et préparez-vous à une récupération lente.
Trois mini-histoires d’entreprise issues du terrain
Incident causé par une mauvaise supposition : « read_only veut dire pas d’écritures »
Ils avaient une configuration standard à deux nœuds : un primaire, une réplique, plus un proxy. Le plan de basculement tenait sur une seule page : si le primaire meurt, mettre l’ancien primaire en read_only=ON, promouvoir la réplique, et basculer le point d’écriture du proxy.
Le jour arriva. Une mise à jour du noyau a rebooté le primaire de façon inattendue. La supervision a sonné, l’automatisation s’est déclenchée, et la réplique a été promue. Le proxy a basculé. Tout le monde a respiré.
Puis le primaire est revenu. Systemd a redémarré le service de base de données automatiquement. Le proxy avait encore un pool de connexions stale vers l’ancien primaire datant d’avant le basculement, et certains nœuds applicatifs avaient des fallback codés en dur. Un utilisateur privilégié existait pour la « maintenance », avec assez de droits pour contourner le comportement read_only de façons que l’équipe ne comprenait pas. Des écritures ont afflué dans l’ancien primaire pendant plusieurs minutes.
Quand ils ont repointé cet ancien primaire comme réplique, il a refusé : des transactions errantes existaient. C’est le mode de défaillance poli. Le mode impoli, c’est quand il accepte la réplication et que vous livrez des ordures en silence.
La correction n’a pas été « rendre read_only plus fort ». La correction a été le fencing : soit arrêter définitivement l’ancien primaire, soit l’isoler au niveau réseau pour qu’il ne puisse pas recevoir de trafic client. Ils ont aussi supprimé les hôtes de fallback « utiles » des configs applicatives et fait du proxy la source unique de vérité.
Une optimisation qui s’est retournée contre eux : « Rendons les commits plus rapides »
Une équipe courait après la latence. Service à forts volumes d’écriture et pics de lag de réplication occasionnels. Quelqu’un a proposé d’assouplir la durabilité : innodb_flush_log_at_trx_commit=2 et sync_binlog=0. L’argument : « On peut tolérer de perdre une seconde de données ; on a la réplication de toute façon. »
Ça a marché — jusqu’à ce que ça ne marche plus. Un primaire a crashé pendant un hic de stockage. La base a redémarré proprement, mais la dernière fenêtre de transactions « confirmées » n’était pas réellement durables sur disque. Certaines transactions avaient été accusées côté client mais manquaient du binlog et de l’état redo d’InnoDB.
Le basculement s’est produit vers la réplique la plus avancée. L’application est revenue. Puis l’incident est devenu un problème de conformité parce que des systèmes en aval avaient reçu des confirmations pour des événements qui n’existaient plus dans la base. Le rapport de bug ressemblait à une théorie du complot, car l’application avait des logs « success », et la base n’avait aucun enregistrement.
Ils ont finalement rétabli la cohérence en rejouant des événements depuis une queue externe et en réconciliant. Personne ne s’est amusé. L’optimisation n’a pas seulement augmenté le RPO ; elle a rendu les pannes non intuitives. C’est le vrai coût.
Le compromis final : garder des réglages durables sur le primaire, améliorer l’indexation et le batching, et investir dans le semi-sync (avec surveillance pour détecter les retours à l’asynchrone). Les optimisations de perf qui changent la correction doivent être traitées comme une décision produit, pas un gain rapide.
Une pratique ennuyeuse mais correcte qui a sauvé la mise : « On s’est entraînés »
Une autre entreprise avait ce qui semblait être excessif : un exercice de basculement mensuel, un runbook écrit avec des branches spécifiques au moteur, et un petit script qui réalisait des probes lecture/écriture via le même chemin que l’application.
Pendant un incident réel (partition réseau entre zones de disponibilité), le primaire est devenu injoignable depuis la plupart des apps mais restait joignable depuis quelques-unes. C’est le prélude au split brain. L’automatisation s’est arrêtée parce que les health checks n’étaient pas d’accord selon les points de vue — c’était voulu.
L’on-call a suivi le playbook : fencer le primaire au niveau réseau, puis promouvoir la meilleure réplique en se basant sur l’état GTID et la santé disque. Le script de probe a vérifié que le trafic writer atteignait vraiment le nouveau primaire, pas seulement que le TCP était ouvert.
La panne n’a pas été amusante, mais elle a été courte et contrôlée. Plus tard, en postmortem, la fierté de l’équipe n’était pas « cinq neuf ». C’était le fait que personne n’a contesté ce qui s’était passé. Les preuves étaient propres parce qu’ils s’étaient entraînés à collecter les preuves, pas seulement à exécuter le basculement.
L’entraînement est ennuyeux. Il est aussi l’une des rares choses qui convertissent un design HA théorique en un design pratique.
Erreurs courantes : symptômes → cause racine → correction
1) Symptom: promotion “succeeds,” but application still writes to the old primary
Cause racine : Pas de fencing ; le proxy route toujours ou les apps ont des listes de fallback ; DNS/pools de connexions stale.
Correction : Fencer l’ancien primaire (arrêt + isolation réseau), faire du proxy le point d’écriture unique, et valider avec une sonde d’écriture via le chemin applicatif.
2) Symptom: replicas won’t connect to new primary after promotion
Cause racine : Le nœud promu a log_bin=OFF, absence de grants pour l’utilisateur de réplication, ou server_id incorrect.
Correction : Assurer que les répliques candidates sont construites avec une config « prête à la promotion » : binlog activé, server_id unique, utilisateur de réplication présent, log_slave_updates configuré si nécessaire.
3) Symptom: GTID-based reparent fails with duplicate or missing GTIDs
Cause racine : Transactions errantes sur une réplique, GTID purgés de façon incohérente, ou modes GTID mixtes dans la flotte.
Correction : Standardiser la configuration GTID partout. Pendant l’incident, choisir un candidat qui est un superset des transactions exécutées ; reconstruire les répliques inconsistantes plutôt que de les forcer avec des skips.
4) Symptom: Seconds_Behind_* is small but data is missing after failover
Cause racine : La métrique ment dans certaines conditions (thread SQL arrêté, apply multi-threaded, dérive d’horloge) ou le semi-sync était retombé en asynchrone.
Correction : Vérifier avec des comparaisons GTID/binlog et des signaux de réconciliation au niveau applicatif. Surveiller les compteurs d’état semi-sync, pas seulement les flags de configuration.
5) Symptom: replication lag explodes right after failover
Cause racine : Le nœud promu était déjà saturé IO, ou le buffer pool est froid, ou une longue transaction bloque la purge et cause de la contention.
Correction : Choisir les candidats de promotion sur la base de la santé (IO, CPU, métriques InnoDB), pas seulement « le plus à jour ». Chauffer les répliques, utiliser un dimensionnement sensé du buffer pool, et éviter de basculer vers un hôte qui fond.
6) Symptom: failover script works on MySQL but fails on MariaDB (or vice versa)
Cause racine : Différences de syntaxe (START REPLICA vs START SLAVE), différences de variables, et différences de sémantique GTID.
Correction : Maintenir des chemins d’automatisation spécifiques au moteur. Détecter le moteur/la version avant d’exécuter. Considérer « compatible à peu près » comme un risque, pas un confort.
7) Symptom: after failover, replicas show “connecting” forever
Cause racine : ACL/firewall réseau non mis à jour, mauvaise adresse source, ou proxy/VIP déplacé mais la réplication utilise des hostnames directs.
Correction : Garder les chemins de trafic de réplication explicites et testés. Utiliser des hostnames dédiés pour la réplication routables depuis les répliques. Valider la connectivité avec des checks TCP depuis les sous-réseaux des répliques.
8) Symptom: replica SQL thread stops with “DDL mismatch” or metadata errors
Cause racine : Versions mixtes / SQL modes / collations ; réplication par instructions ; ou un DDL exécuté différemment.
Correction : Utiliser la réplication par lignes. Standardiser SQL mode et collation. Éviter le basculement cross-version quand c’est possible ; si inévitable, tester le comportement de réplication des DDL avant la production.
Listes de contrôle / plan pas à pas
Checklist de préparation au basculement (faire avant d’en avoir besoin)
- Le fencing existe et est testé : vous pouvez empêcher l’ancien primaire d’accepter du trafic même s’il redémarre.
- Config prête à la promotion sur les répliques : binlog activé,
server_idunique, utilisateurs de réplication présents, bons réglages GTID. - Position de durabilité cohérente : décider si vous acceptez une perte en cas de crash ; ne pas laisser cela devenir un tweak de perf accidentel.
- Réplication par lignes :
binlog_format=ROWsauf raison prouvée contraire. - Runbook avec branches moteur : différences MySQL vs MariaDB capturées et répétées.
- Signaux de santé : application de réplication, latence disque, métriques InnoDB, statut semi-sync actif, état du routage proxy.
- Outil de sonde d’écriture : un script minimal qui confirme que le nouveau writer accepte réellement les écritures via le chemin client réel.
Plan d’incident pour le basculement (version contrôlée)
- Arrêter l’hémorragie : mettre en pause l’automatisation, réduire le trafic d’écriture si possible, et geler les changements de schéma.
- Fencer l’ancien primaire : arrêt + isolation réseau. Vérifier qu’il est down et injoignable depuis les clients.
- Choisir un candidat : comparer positions GTID/binlog ; rejeter toute réplique avec erreurs SQL ou dérive ; vérifier la santé IO.
- Arrêter la réplication sur le candidat : l’empêcher d’appliquer d’autres événements depuis une source compromise.
- Promouvoir : désactiver read-only ; s’assurer que le binlog est activé ; vérifier qu’il peut accepter des écritures.
- Basculer le trafic : mettre à jour proxy/VIP/DNS ; vérifier avec une sonde d’écriture ; surveiller les taux d’erreur.
- Reparenter les répliques : utiliser GTID si possible ; vérifier que chaque réplique rattrape et reste stable.
- Nettoyage post-basculement : reconstruire les répliques cassées, valider les sauvegardes, et documenter exactement ce que vous avez observé.
Quand choisir MySQL vs MariaDB pour des environnements axés sur le basculement
Choisir MySQL (notamment 8.0+) si :
- Vous voulez une histoire HA « fournisseur unique » plus claire avec Group Replication/InnoDB Cluster comme option éventuelle.
- Vos outils et la mémoire opérationnelle de votre équipe sont déjà orientés MySQL (Orchestrator, MySQL shell, sémantiques 8.0).
- Vous valorisez un comportement d’ensembles GTID prévisible et des patterns opérationnels communs entre offres managées.
Choisir MariaDB si :
- Vous investissez dans l’écosystème MariaDB comme MaxScale et comprenez profondément son comportement de basculement.
- Vous bénéficiez de fonctionnalités spécifiques à MariaDB et êtes prêt à traiter GTID et basculement comme natifs MariaDB, pas « semblables à MySQL ».
- Vous planifiez autour des capacités de réplication de MariaDB et votre flotte est suffisamment homogène pour éviter les pièges de compatibilité.
Évitez le basculement entre moteurs mixtes sauf si vous avez testé les versions exactes, le mode GTID, les paramètres du binlog et le comportement des outils sous panne. « Ça réplique en staging » n’est pas une preuve ; c’est une rumeur de départ.
FAQ
1) Puis-je basculer entre répliques MySQL et MariaDB ?
Parfois on peut répliquer entre eux avec certaines combinaisons de versions et de configurations, mais le basculement est risqué. Les sémantiques GTID diffèrent et les outils supposent souvent un seul moteur. Si vous devez, gardez l’écrivain sur une seule famille de moteur et traitez l’autre comme consommateur en lecture seule, pas comme cible de promotion.
2) Les GTID garantissent-ils l’absence de perte de données lors d’un basculement ?
Non. Les GTID aident à identifier ce qui a été exécuté où et facilitent le reparent des répliques. La perte dépend de quelles transactions ont été acquittées par l’ancien primaire avant sa mort et si les répliques les ont reçues/appliquées.
3) Le semi-sync suffit-il pour garantir zéro perte de données ?
Pas à lui seul. Le semi-sync peut retomber en asynchrone sous charge ou problèmes réseau. Vous devez surveiller s’il est actif au moment de la panne et comprendre les exigences d’ack que vous avez configurées.
4) Pourquoi « Seconds_Behind_Master » ment-il parfois ?
Parce que c’est une estimation basée sur des timestamps et l’état des threads. Si le thread SQL est arrêté, si les relay logs sont retardés, si les horloges dévient, ou si l’application parallèle est en jeu, le chiffre peut être trompeur. Utilisez des comparaisons GTID/binlog et l’état d’erreur comme signaux principaux.
5) Quel est le contrôle de basculement le plus important ?
Le fencing. Si vous ne pouvez pas garantir que l’ancien primaire n’acceptera pas d’écritures, tout le reste n’est que théâtralité.
6) Dois-je utiliser la réplication statement-based ou row-based pour le basculement ?
Row-based. Statement-based et les formats mixtes créent une dérive non déterministe et dépendante des versions. Le basculement est le moment où la dérive devient visible — et coûteuse.
7) Quels outils devrais-je utiliser pour le basculement ?
Utilisez un outil qui comprend votre moteur et votre mode GTID, et qui supporte le fencing ou s’intègre avec un composant capable de fencer (ou à défaut qui met les promotions en pause jusqu’à confirmation humaine). Orchestrator est courant dans les flottes MySQL ; MaxScale est courant dans les flottes MariaDB ; ProxySQL peut servir de couche de routage mais nécessite une logique de santé correcte.
8) Comment choisir la meilleure réplique à promouvoir ?
Choisissez la réplique qui a (a) l’ensemble de transactions exécutées/appliquées le plus complet, (b) pas d’erreurs de réplication, et (c) une bonne santé hôte (latence disque, récupération après crash non en cours). « Le plus avancé mais surchargé » est un piège.
9) Pourquoi les promotions échouent-elles parfois à cause de « transactions errantes » ?
Parce qu’une réplique a exécuté des transactions absentes du flux de réplication du primaire (écritures manuelles, automatisation défaillante, ou split brain précédent). Les GTID rendent cela visible. La correction consiste souvent à reconstruire ou à réconcilier chirurgicalement — généralement reconstruire.
10) À quelle fréquence devons-nous répéter des exercices de basculement ?
Mensuellement si possible, trimestriellement si besoin. L’entraînement est la façon de découvrir que votre proxy met en cache le DNS 10 minutes, ou que votre « fencing » est en fait une requête polie.
Conclusion : prochaines étapes à faire cette semaine
Le basculement ne casse pas parce que MySQL ou MariaDB est « mauvais ». Il casse parce que vous vous êtes reposé sur des suppositions que vous n’avez jamais vérifiées en condition de panne. Les moteurs diffèrent dans la sémantique des GTID, la surface des commandes de réplication et les attentes des outils d’écosystème. Ces différences sont gérables — si vous les reconnaissez.
Faites ceci ensuite, dans l’ordre :
- Rédigez votre contrat de basculement : RPO/RTO acceptables, qui décide de perdre des données (si nécessaire), et ce que « succès » signifie (pas « la base est up », mais « les écritures vont exactement en un seul endroit »).
- Mettez en place un vrai fencing : pas seulement read_only. Quelque chose qui empêche les écritures de l’ancien primaire même s’il redémarre.
- Standardisez le mode de réplication : row-based, SQL mode/collation cohérents, réglages GTID cohérents par moteur.
- Construisez un profil de réplique prêt à la promotion : binlog activé, server_id unique, utilisateurs et privilèges de réplication présents, et surveillance.
- Faites un exercice de basculement et collectez les preuves comme vous le feriez en réel : sorties d’état, stats IO, état du proxy. Corrigez la première surprise que vous trouvez. Il y aura des surprises.
Idée paraphrasée de John Allspaw : la fiabilité vient de l’apprentissage continu, pas de la prétention que les systèmes sont prévisibles sous pression
.
Blague #2 : La seule chose plus optimiste que « le basculement est automatique » est « le runbook est à jour ».