Mises à niveau MySQL vs MariaDB : comment mettre à jour sans casser la production

Cet article vous a aidé ?

Si vous avez déjà mis à niveau une base de données « vite fait » puis vu votre application sombrer dans une panique silencieuse et polie — latence en hausse, erreurs en cascade, réplicas qui dérivent — vous connaissez déjà la chute : les mises à niveau n’échouent pas bruyamment ; elles échouent de façon créative.

Ceci est un guide pour la production. Pas de la théorie. Pas du marketing éditeur. L’objectif est simple : faire avancer MySQL ou MariaDB (ou basculer entre eux) tout en gardant vos données sûres, votre latence raisonnable et votre plan de retour crédible.

Choisissez votre voie : sur place, rolling, ou migration

La plupart des mises à niveau de bases de données tournent mal pour l’une des trois raisons suivantes : vous avez choisi la mauvaise méthode, vous avez sauté la répétition, ou vous vous êtes menti sur le rollback.

Option A : mise à niveau majeure sur place (la plus rapide, la plus risquée)

Sur place signifie que vous arrêtez MySQL/MariaDB sur un hôte et que vous le redémarrez avec la nouvelle version en pointant vers le même datadir. Cela peut convenir pour des systèmes petits et non critiques. En production, c’est généralement un dernier recours car :

  • Le rollback n’est souvent pas un vrai rollback. Une fois les fichiers de données mis à jour, revenir en arrière peut devenir « restaurer depuis une sauvegarde et prier ».
  • Une indisponibilité est obligatoire.
  • Si vous découvrez une régression de performance, vous déboguez sous pression temporelle.

Si vous devez faire une mise à niveau sur place, faites-le d’abord sur une réplique. Si vous n’avez pas de réplicas, votre première « étape d’upgrade » est : construire la réplication.

Option B : mise à niveau rolling via réplication (recommandé pour la plupart)

C’est la méthode adulte. Vous mettez à niveau les réplicas d’abord, vérifiez, puis basculez. Votre temps d’arrêt devient une fenêtre de basculement contrôlée, pas un événement existentiel.

  • Meilleur pour : réplication asynchrone MySQL, semi-sync, réplication MariaDB, de nombreux scénarios Galera (avec contraintes de version).
  • Le rollback est crédible : revenir au primaire ancien (si vous l’avez gardé intact et que la réplication reste compatible).
  • Le risque passe de « format des fichiers de données » à « compatibilité de réplication et comportement des requêtes ». C’est un meilleur type de risque.

Option C : migration via dump/restauration logique ou CDC (la plus lente, la plus propre)

Si vous changez de moteur (MySQL → MariaDB ou MariaDB → MySQL), ou si vous devez sauter des versions majeures incompatibles, vous pouvez :

  • Logique : mysqldump / mydumper, restaurer dans le nouveau cluster, basculer.
  • CDC : réplication basée sur binlog (native), ou une pipeline de change data capture qui stream les changements vers le nouveau cluster.

La migration logique est lente mais déterministe. La CDC est élégante mais opérationnellement plus lourde. Dans les deux cas, vous obtenez un départ propre et moins de surprises liées au format des fichiers.

Règle opinionnée : si le business s’en soucie, faites des rolling upgrades ou une migration CDC. Les upgrades sur place en production sont la façon dont on apprend le véritable sens de « maintenance non planifiée ».

Quelques faits et historique (les parties qui piquent encore)

Les mises à niveau sont plus faciles quand vous savez pourquoi l’écosystème en est arrivé là. Voici des faits concrets qui comptent encore opérationnellement :

  1. MariaDB a bifurqué depuis MySQL en 2009 après que Oracle ait acquis Sun Microsystems. Cette séparation a créé deux feuilles de route divergentes avec « majoritairement compatible » comme cible mouvante.
  2. MySQL 5.7 → 8.0 n’est pas une mise à niveau majeure « normale ». L’optimiseur, le dictionnaire de données, les valeurs par défaut et l’authentification ont changé de façons qui se manifestent comme des bugs applicatifs, pas seulement des tâches DBA.
  3. MySQL 8.0 a déplacé les métadonnées dans un dictionnaire de données transactionnel (basé sur InnoDB). Opérationnellement : moins d’artefacts .frm, mais les upgrades réécrivent des structures internes et peuvent prendre du temps réel.
  4. MariaDB a maintenu Aria comme moteur de tables système à certains endroits et a développé des fonctionnalités indépendamment (ex. comportement JSON et optimiseur différents). Cette indépendance est une force — et un risque de compatibilité.
  5. « JSON » est un bon exemple de divergence : le JSON de MySQL est un format binaire avec fonctions et index optimisés autour de lui ; MariaDB a historiquement traité JSON comme un alias TEXT avec des fonctions JSON évoluant différemment.
  6. Les plugins d’authentification ont changé les attentes dans MySQL 8.0 (caching_sha2_password par défaut). Des clients et des proxies qui « allaient bien pendant des années » ne vont plus forcément.
  7. Les implémentations GTID diffèrent entre MySQL et MariaDB. On ne peut pas supposer qu’on peut simplement « activer GTID » et continuer entre eux sans planification.
  8. MySQL a supprimé le query cache il y a des années (8.0), tandis que MariaDB l’a gardé plus longtemps. Si vous dépendiez du query cache (même accidentellement), les upgrades ressembleront à un « mystère de performance ».
  9. Taille de page InnoDB, paramètres de redo log et comportement de récupération après crash peuvent changer les valeurs par défaut et les heuristiques entre versions. Votre objectif de temps de récupération (RTO) peut bouger sans demander votre avis.

Blague n°1 : Le seul « truc bizarre » pour les mises à niveau de bases de données est de faire une sauvegarde que vous avez réellement restaurée au moins une fois.

Carte de compatibilité : ce qui casse entre MySQL et MariaDB

« Compatible » n’est pas une propriété binaire. C’est une liste de choses qui finiront par vous nuire en production, avec une date associée.

MySQL → MySQL (même famille, toujours dangereux)

  • Mode SQL et valeurs par défaut : les changements de comportement peuvent apparaître sous forme d’erreurs de troncature ou d’inserts devenant soudainement stricts.
  • Régressions de l’optimiseur : la même requête peut choisir un nouveau plan et devenir lente sous charge.
  • Authentification et TLS : les bibliothèques clients, HAProxy/ProxySQL et les vieilles versions JDBC peuvent cesser de se connecter.
  • Filtrage de réplication et métadonnées : de petites différences de configuration peuvent casser la réplication au cutover.

MariaDB → MariaDB

  • Contraintes de version Galera : vous ne pouvez pas simplement appliquer n’importe quelle version majeure sur un cluster. Vérifiez les chemins d’upgrade rolling supportés ; sinon vous devrez reconstruire le cluster.
  • Évolution des tables système : les tables mysql.* et les schémas de privilèges évoluent ; les upgrades peuvent nécessiter des étapes postérieures explicites.
  • Histoire InnoDB vs XtraDB : les anciennes versions de MariaDB avaient des chemins de code de moteur de stockage distincts ; les versions modernes se rapprochent, mais des environnements legacy existent encore.

MySQL ↔ MariaDB (changement de moteur)

C’est là que « ça démarre correctement » devient un piège.

  • Les formats de fichiers de données et les tables système diffèrent : remplacer les binaires sur place n’est pas une stratégie ; c’est un pari.
  • Différences GTID : migrer des setups de réplication entre eux nécessite une conception minutieuse (et souvent un reseed).
  • Les fonctionnalités SQL divergent : support des fonctions de fenêtre, fonctions JSON, hints d’optimiseur et mots réservés dérivent.
  • Attentes des clients : comportement des connecteurs, paramètres d’authentification par défaut et versions TLS varient selon les distributions.

Règle opinionnée : traitez MySQL ↔ MariaDB comme une migration, pas comme une « mise à niveau ». Planifiez un fonctionnement dual, une validation et un cutover avec une fenêtre de rollback.

Playbook de diagnostic rapide : quoi vérifier en premier/deuxième/troisième

Quand une mise à niveau dérape, vous n’avez pas besoin d’une session navigateur à 40 onglets. Vous avez besoin d’une séquence de triage qui vous indique où vit le temps et la douleur.

Premier : est-ce CPU, IO, ou verrous ?

  • CPU-bound : régression de plan, index manquants, augmentation des tris/tables temporaires, ou surcharge TLS/auth sous forte churn de connexions.
  • IO-bound : comportement de flush changé, pression sur doublewrite, modifications du redo logging, buffer pool trop petit, hausse de latence stockage.
  • Bloqué par verrous : verrous de métadonnées, nouveau comportement DDL, longues transactions, ou thread d’application de réplication bouché.

Deuxième : la réplication est-elle saine et comparable ?

  • Le lag des réplicas et les erreurs d’application après upgrade sont des signaux d’alerte précoces. Ils sont souvent ignorés jusqu’au cutover. Ne le faites pas.
  • Confirmez le format de binlog, le mode GTID (si utilisé) et l’état du thread SQL du replica.

Troisième : l’application a-t-elle changé de comportement ?

  • Thrash du pool de connexions dû à un mismatch d’authentification et des boucles de reconnexion.
  • Nouvelle sévérité provoquant des retries.
  • Changements de fuseau horaire, collation ou jeu de caractères produisant des différences subtiles et des misses de cache.

Quatrième : l’observabilité a-t-elle survécu à l’upgrade ?

  • Les réglages de Performance Schema, le format du slow log et les noms de métriques peuvent changer. Si les tableaux de bord sont devenus aveugles, vous déboguez à l’aveugle.

Mouvement unique le plus utile sous pression : comparez une requête coûteuse connue sur l’ancien et le nouveau en utilisant les mêmes données et mêmes paramètres. Si le plan diffère, vous avez une direction.

Blague n°2 : « Nous n’avons rien changé » est l’équivalent base de données de « le chien a mangé mes devoirs », sauf que le chien est un optimiseur.

Tâches en production : commandes, sorties et décisions (12+)

Ce sont des tâches que vous pouvez exécuter sur de vrais serveurs. Chacune inclut : la commande, un extrait de sortie plausible, ce que cela signifie, et la décision que vous prenez.

Task 1: Identify exact server version and distribution

cr0x@server:~$ mysql -e "SELECT VERSION() AS version, @@version_comment AS distro, @@version_compile_machine AS arch\G"
*************************** 1. row ***************************
version: 8.0.36
distro: MySQL Community Server - GPL
arch: x86_64

Signification : Vous ne devinez pas. « MySQL 8 » n’est pas une version ; 8.0.36 est une version. MariaDB affichera des commentaires différents.

Décision : Épinglez les cibles d’upgrade explicitement. Si vous ne pouvez pas nommer vos versions patch actuelles et cibles, vous ne planifiez pas une mise à niveau ; vous planifiez une surprise.

Task 2: Check critical configuration diffs (effective runtime)

cr0x@server:~$ mysql -e "SHOW VARIABLES WHERE Variable_name IN ('datadir','log_bin','binlog_format','gtid_mode','enforce_gtid_consistency','innodb_buffer_pool_size','innodb_flush_log_at_trx_commit','sync_binlog','sql_mode','character_set_server','collation_server')"
+------------------------------+---------------------------+
| Variable_name                | Value                     |
+------------------------------+---------------------------+
| binlog_format                | ROW                       |
| character_set_server         | utf8mb4                   |
| collation_server             | utf8mb4_0900_ai_ci        |
| datadir                      | /var/lib/mysql/           |
| enforce_gtid_consistency     | ON                        |
| gtid_mode                    | ON                        |
| innodb_buffer_pool_size      | 34359738368               |
| innodb_flush_log_at_trx_commit| 1                        |
| log_bin                      | ON                        |
| sql_mode                     | ONLY_FULL_GROUP_BY,...    |
| sync_binlog                  | 1                         |
+------------------------------+---------------------------+

Signification : C’est votre personnalité opérationnelle : durabilité, format de réplication, sévérité, et gestion des caractères.

Décision : Toute upgrade/migration doit préserver l’intention. Si la nouvelle version change des valeurs par défaut, overridez-les explicitement dans la config, pas dans vos espoirs.

Task 3: Confirm disk space headroom before upgrade

cr0x@server:~$ df -h /var/lib/mysql
Filesystem      Size  Used Avail Use% Mounted on
/dev/nvme0n1p2  900G  720G  135G  85% /var

Signification : Les mises à niveau majeures peuvent créer des fichiers temporaires, reconstruire des structures internes et étendre redo/undo pendant la rattrapage.

Décision : Moins d’environ ~20% libre est un drapeau rouge. Élargissez le stockage ou planifiez une migration vers un nouveau volume. « On s’en sortira » n’est pas une stratégie de système de fichiers.

Task 4: Look for corrupted tables or lurking InnoDB warnings

cr0x@server:~$ sudo tail -n 60 /var/log/mysql/error.log
2025-12-29T02:14:11.102334Z 0 [Warning] [MY-010068] [Server] CA certificate ca.pem is self signed.
2025-12-29T02:15:03.981245Z 0 [Warning] [MY-012639] [InnoDB] Retry attempts for reading partial data failed.
2025-12-29T02:15:04.001103Z 0 [Note] [MY-010431] [Server] Detected data dictionary initialization

Signification : Les upgrades amplifient les dégâts existants. Si InnoDB se plaint déjà, n’empilez pas de risque par-dessus.

Décision : Investiguer et réparer d’abord les problèmes de stockage/log (SMART/NVMe, erreurs système de fichiers, messages kernel). Si nécessaire, prenez une nouvelle sauvegarde logique.

Task 5: Measure replication health on replicas (before touching anything)

cr0x@server:~$ mysql -e "SHOW REPLICA STATUS\G" | egrep "Replica_IO_Running|Replica_SQL_Running|Seconds_Behind_Source|Last_SQL_Error|Retrieved_Gtid_Set|Executed_Gtid_Set"
Replica_IO_Running: Yes
Replica_SQL_Running: Yes
Seconds_Behind_Source: 0
Last_SQL_Error:
Retrieved_Gtid_Set: 2f9c3b3a-...:1-98233411
Executed_Gtid_Set: 2f9c3b3a-...:1-98233411

Signification : Un replica qui ne peut pas répliquer aujourd’hui ne va pas magiquement répliquer après que vous l’avez mis à niveau.

Décision : Corrigez la dérive de réplication d’abord. Ne mettez pas à niveau au-dessus d’une réplication cassée ; vous perdrez votre chemin de rollback le plus facile.

Task 6: Check for long transactions (upgrade and failover killers)

cr0x@server:~$ mysql -e "SELECT trx_id, trx_started, TIME_TO_SEC(TIMEDIFF(NOW(), trx_started)) AS age_s, trx_mysql_thread_id, trx_query FROM information_schema.innodb_trx ORDER BY age_s DESC LIMIT 5\G"
*************************** 1. row ***************************
trx_id: 145922993
trx_started: 2025-12-29 01:47:12
age_s: 2081
trx_mysql_thread_id: 17322
trx_query: UPDATE orders SET status='PAID' WHERE id=...

Signification : Les longues transactions bloquent le purge, peuvent bloquer le DDL, et peuvent rendre les basculements interminables (et faire laguer les réplicas).

Décision : Avant le cutover, imposez une fenêtre « pas de longues transactions » : pausez les jobs batch, corrigez les writers bloqués, et envisagez de définir un temps d’exécution max là où c’est pertinent.

Task 7: Find top waits (locks vs IO vs CPU) using Performance Schema

cr0x@server:~$ mysql -e "SELECT event_name, COUNT_STAR, ROUND(SUM_TIMER_WAIT/1000000000000,2) AS total_s FROM performance_schema.events_waits_summary_global_by_event_name WHERE SUM_TIMER_WAIT > 0 ORDER BY SUM_TIMER_WAIT DESC LIMIT 8"
+------------------------------------------+------------+---------+
| event_name                               | COUNT_STAR | total_s |
+------------------------------------------+------------+---------+
| wait/io/table/sql/handler                | 192233331  | 8421.33 |
| wait/synch/mutex/innodb/buf_pool_mutex   |  98223311  | 3120.10 |
| wait/io/file/innodb/innodb_data_file     |  12233411  | 1777.54 |
| wait/lock/table/sql/handler              |   2233111  |  601.22 |
+------------------------------------------+------------+---------+

Signification : Vous regardez ce sur quoi le serveur passe son temps à attendre. Pas des impressions. Pas Slack.

Décision : Si les waits IO dominent, planifiez une validation du stockage et des réglages de flush. Si les waits de verrou dominent, auditez les requêtes et le périmètre des transactions avant d’upgrader.

Task 8: Compare query plans pre-upgrade with EXPLAIN ANALYZE

cr0x@server:~$ mysql -e "EXPLAIN ANALYZE SELECT * FROM sessions WHERE user_id=123 AND created_at > NOW() - INTERVAL 7 DAY ORDER BY created_at DESC LIMIT 50\G"
*************************** 1. row ***************************
EXPLAIN: -> Limit: 50 row(s)  (actual time=0.321..0.338 rows=50 loops=1)
    -> Index range scan on sessions using idx_user_created (user_id=123)  (actual time=0.320..0.333 rows=50 loops=1)

Signification : C’est votre sérum de vérité. Si le « actual time » ou la méthode d’accès change après l’upgrade, vous avez trouvé votre régression.

Décision : Capturez un jeu de requêtes critiques de référence et leurs plans. Après avoir mis à niveau une réplique, comparez. Si les plans divergent, corrigez avec des index, des hints (avec parcimonie), ou des réécritures de requêtes avant le cutover.

Task 9: Validate character set/collation drift that breaks indexes and comparisons

cr0x@server:~$ mysql -e "SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, CHARACTER_SET_NAME, COLLATION_NAME FROM information_schema.COLUMNS WHERE TABLE_SCHEMA='app' AND DATA_TYPE IN ('varchar','text','char') AND COLLATION_NAME NOT LIKE 'utf8mb4%' LIMIT 10"
+--------------+------------+-------------+--------------------+-------------------+
| TABLE_SCHEMA | TABLE_NAME | COLUMN_NAME | CHARACTER_SET_NAME | COLLATION_NAME    |
+--------------+------------+-------------+--------------------+-------------------+
| app          | users      | email       | latin1             | latin1_swedish_ci |
+--------------+------------+-------------+--------------------+-------------------+

Signification : Les collations mixtes causent un ordre étrange, des attentes d’unicité brisées et des comparaisons lentes.

Décision : Ne changez pas les collations pendant un upgrade de moteur sauf si vous êtes prêt pour un projet de migration de schéma. Si nécessaire, faites-le comme un changement séparé avec son propre rayon d’impact et plan de rollback.

Task 10: Check for deprecated/removed features used by your workload

cr0x@server:~$ mysql -e "SHOW VARIABLES LIKE 'query_cache%';"
Empty set (0.00 sec)

Signification : Sur MySQL 8.0, les variables du query cache ont disparu. Sur MariaDB elles peuvent exister selon la version.

Décision : Si votre appli profitait accidentellement du query cache, vous verrez une charge supplémentaire après l’upgrade. Préparez la capacité et corrigez le cache au niveau applicatif où il doit se situer.

Task 11: Verify client compatibility with authentication plugins

cr0x@server:~$ mysql -e "SELECT user, host, plugin FROM mysql.user WHERE user IN ('app','replicator')\G"
*************************** 1. row ***************************
user: app
host: %
plugin: caching_sha2_password
*************************** 2. row ***************************
user: replicator
host: 10.0.0.%
plugin: mysql_native_password

Signification : Si votre bibliothèque cliente ne supporte pas caching_sha2_password, elle ne pourra pas se connecter, ou elle se reconnectera en boucle et vous fera un DoS poli.

Décision : Mettez à niveau les clients/connecteurs/proxies avant l’upgrade serveur, ou définissez explicitement le plugin utilisateur. Préférez mettre à jour les clients ; rétrograder l’authentification est un compromis de sécurité à faire en connaissance de cause.

Task 12: Confirm binary logging and binlog retention won’t explode during migration

cr0x@server:~$ mysql -e "SHOW BINARY LOGS;"
+------------------+-----------+
| Log_name         | File_size |
+------------------+-----------+
| binlog.000331    |  10485776 |
| binlog.000332    | 1073741824|
| binlog.000333    | 1073741824|
+------------------+-----------+

Signification : Vos binlogs sont volumineux et fréquents. Pendant les répétitions de cutover et la reconstruction de réplicas, le churn des binlogs peut remplir les disques.

Décision : Assurez-vous d’un headroom disque et fixez une expiration de binlog raisonnable. Pour MySQL 8.0, préférez binlog_expire_logs_seconds. Évitez de la régler « faible » pendant les upgrades sauf si vous aimez reconstruire des réplicas.

Task 13: Take a logical backup you can actually restore (spot-check restore)

cr0x@server:~$ mysqldump --single-transaction --routines --events --triggers --hex-blob --set-gtid-purged=OFF --databases app > /backups/app.sql
cr0x@server:~$ mysql -e "CREATE DATABASE restore_test; USE restore_test; SOURCE /backups/app.sql;"
Query OK, 1 row affected (0.01 sec)

Signification : La sauvegarde existe et est utilisable. C’est plus rare que cela devrait l’être.

Décision : Si la restauration échoue, arrêtez. Réparez le backup/restore avant de toucher aux binaires de production. Les sauvegardes ne sont pas une case à cocher ; elles sont votre rollback quand tout le reste brûle.

Task 14: Dry-run application compatibility with a shadow instance

cr0x@server:~$ mysql -e "SET GLOBAL log_output='TABLE'; SET GLOBAL slow_query_log=ON; SET GLOBAL long_query_time=0.2;"
Query OK, 0 rows affected (0.00 sec)

Signification : Vous pouvez capturer les slow queries sur la réplique mise à niveau tout en exécutant du trafic en lecture miroir (ou des tests synthétiques).

Décision : Si la réplique mise à niveau montre de nouvelles slow queries pour la même charge, ne coupez pas tant que vous n’avez pas compris pourquoi. « Ça doit aller » est la façon d’entrer dans une semaine d’incident.

Trois mini-histoires d’entreprise depuis les tranchées

Incident causé par une fausse hypothèse : « la réplication, c’est la réplication »

Ils exploitaient une plateforme SaaS chargée avec une réplication asynchrone propre : un primaire, deux réplicas, des sauvegardes nocturnes. Le plan était de « moderniser » en passant de MariaDB à MySQL parce qu’un outil vendor « préférait MySQL 8 ». Le doc de migration faisait deux pages. Ça aurait dû être un indice.

L’hypothèse erronée était subtile : ils pensaient que leur approche de réplication basée GTID MariaDB se mapperait proprement au GTID MySQL. L’équipe a construit un nouveau replica MySQL, tenté de l’attacher au primaire MariaDB, et découvert que « GTID » n’est pas une norme unique entre forks. Ils ont pivoté vers la réplication fichier/position, ce qui a plus ou moins fonctionné jusqu’à ce qu’un test de basculement introduise un décalage de position binlog et que le replica commence à rejeter des événements.

Lors de la fenêtre de cutover, l’application a basculé les écritures sur le nouveau primaire MySQL, et l’ancien cluster MariaDB devait rester comme rollback. Mais le rollback nécessitait une réplication inverse pour le maintenir proche. Ce chemin inverse n’était pas stable. En une heure, ils avaient deux réalités divergentes et aucune ligne propre de retour.

La solution n’était pas glamour. Ils ont reconstruit la migration comme une pipeline CDC avec checks explicites de consistance, et ils ont arrêté d’essayer de faire signifier GTID la même chose entre moteurs. Ils ont aussi mis en place une règle stricte : le rollback doit être testé avec du vrai trafic en écriture dans une répétition, pas « ça semble plausible ». La plateforme a survécu. Le document de deux pages non.

Une optimisation qui s’est retournée contre eux : « optimisons le flushing pendant l’upgrade »

Une autre entreprise avait une flotte MySQL 5.7 sur NVMe rapides. Pendant une mise à niveau planifiée vers 8.0, quelqu’un a proposé un tweak de performance : réduire temporairement les réglages de durabilité pour accélérer le rattrapage et réduire le temps de failover. Plus précisément, ils ont réduit innodb_flush_log_at_trx_commit et sync_binlog sur les réplicas mis à niveau.

En staging, c’était excellent. La réplication appliquait plus vite ; les requêtes étaient réactives. En production, cependant, il y avait un ingrédient supplémentaire : des événements d’alimentation occasionnels dans un rack et un bug firmware du contrôleur de stockage qui avait été sans conséquence sous flush strict. Deux jours après le déploiement, un replica mis à niveau a crashé. La récupération après crash s’est terminée, mais elle a rejoué les logs différemment que prévu et a remonté une inconsistance dans une table edge-case qui utilisait un mélange de patterns autocommit et un trigger legacy.

Aucune donnée n’était « perdue » au sens existentiel — la plupart des tables allaient bien — mais l’inconsistance a suffi à bloquer la promotion de ce replica et a forcé une reconstruction durant le rollout. La mise à niveau globale a fortement ralenti. L’« optimisation » a ajouté plus de risque d’indisponibilité qu’elle n’en a retiré.

La leçon : n’introduisez pas de changements de tuning pendant une mise à niveau à moins d’être prêt à les déboguer comme s’il s’agissait de fonctionnalités production. Si vous voulez changer les knobs de durabilité, faites-le comme un changement séparé, avec sa propre fenêtre de test, et acceptez explicitement le compromis de risque.

Une idée paraphrasée souvent attribuée à Werner Vogels : « Tout échoue ; concevez et opérez en supposant que ce sera le cas. » Cela inclut vos réglages astucieux pendant l’upgrade.

Une pratique ennuyeuse mais correcte qui a sauvé la mise : répétition de cutover avec vrai rollback

Une équipe d’entreprise avait l’habitude que tout le monde taquinait : ils réalisaient des répétitions complètes pour les mises à niveau de base de données, y compris le rollback. Pas « on a des sauvegardes », mais « on a pratiqué le retour en arrière sous charge ». Ça paraissait excessif — jusqu’à ce que ce ne le soit pas.

Ils mettaient à niveau MariaDB sur une configuration trois nœuds utilisée par un système financier interne. Lors de la répétition n°1, ils ont découvert que leur outil de migration de schéma ouvrait des transactions longues, causant des verrous de métadonnées pendant le basculement. Cela aurait transformé un cutover de 2 minutes en une panne de 45 minutes. Ils ont corrigé la configuration de l’outil et séparé les changements de schéma de la mise à niveau du moteur.

Lors de la répétition n°2, ils ont découvert que leur agent de monitoring utilisait une variable d’état dépréciée et avait cessé de rapporter le lag de réplication sur les nœuds mis à niveau. Ils ont mis à jour les tableaux de bord et les alertes avant la production.

La nuit réelle, la mise à niveau s’est bien passée — jusqu’à ce qu’un changement ACL réseau ailleurs provoque une perte de paquets intermittente vers un replica. L’équipe a vu immédiatement le jitter de réplication (parce que le monitoring fonctionnait toujours), a mis la promotion en pause et a basculé vers l’autre replica mis à niveau à la place. Aucun incident. Le travail d’habitude ennuyeux a fait tout le travail dramatique pour eux.

Listes de contrôle / plan pas-à-pas

Il y a deux projets d’upgrade cachés en un : le changement de moteur de base de données et le changement opérationnel. Traitez les deux sérieusement.

Phase 0 : Décidez de ce que vous faites réellement

  • Même moteur, version majeure (MySQL 5.7→8.0, MariaDB 10.x→11.x) : utilisez le rolling upgrade quand possible.
  • Changement de moteur (MySQL ↔ MariaDB) : planifiez une migration avec validation. Supposez des incompatibilités tant que le contraire n’est pas prouvé.
  • Galera : vérifiez les chemins d’upgrade rolling supportés. Si non supporté, planifiez une reconstruction de cluster et un cutover.

Phase 1 : Checklist pré-vol (à faire une semaine avant)

  • Inventaire : versions, configs, plugins, usage des moteurs de stockage.
  • Baseline : latence p95/p99, requêtes principales, lag de réplication, taux de hit du buffer pool, pression redo/undo.
  • Backups : test de restauration d’une sauvegarde logique complète ; test de restauration physique si utilisé.
  • Compatibilité client : connecteurs, proxies, TLS, plugins d’authentification.
  • Headroom disque et IOPS : les upgrades provoquent des rafales.
  • Définir le succès : taux d’erreur acceptable, latence et durée de cutover.

Phase 2 : Construisez un environnement de répétition (ne pas sauter)

Utilisez des données proches de la production. Si vous ne pouvez pas, utilisez au moins le schéma de production et une relecture de charge.

  • Restaurer la sauvegarde de la nuit précédente en staging.
  • Attacher une relecture de charge en lecture seule ou un benchmark synthétique qui approxime le mix de requêtes.
  • Exécuter les tests d’intégration applicatifs contre la nouvelle version.
  • Comparer les plans de requêtes pour les requêtes critiques.

Phase 3 : Plan de rolling upgrade (basé sur la réplication)

Étape 1 : Mettre à niveau un replica

  • Arrêter la réplication (ou la laisser tourner selon la méthode), mettre à niveau le binaire, exécuter les étapes post-upgrade requises.
  • Laisser rattraper, vérifier la vitesse d’application, vérifier la latence des requêtes.

Étape 2 : Valider en profondeur avant de toucher le primaire

  • Envoyer du trafic en lecture sur le replica mis à niveau (ou miroir de lectures) et comparer erreurs/latence.
  • Confirmer que la réplication est stable sur des heures, pas des minutes.
  • Confirmer que les sauvegardes fonctionnent sur la nouvelle version (les outils changent parfois de comportement).

Étape 3 : Mettre à niveau les réplicas restants

N’upgradez jamais tous les réplicas en même temps si vous pouvez l’éviter. Gardez au moins un replica connu bon sur l’ancienne version jusqu’à ce que le cutover soit réussi et stable.

Étape 4 : Cutover

  • Quiescer brièvement les écritures si votre processus de basculement l’exige.
  • Promouvoir un replica mis à niveau en primaire.
  • Repointer l’application, vérifier rapidement, puis réactiver progressivement les jobs de fond.

Étape 5 : Plan de rollback (vous le pratiquez)

  • Gardez l’ancien primaire intact pendant une fenêtre de rollback définie.
  • Décidez des conditions déclenchant le rollback (latence, taux d’erreur, instabilité de réplication).
  • Ayez une procédure scriptée et testée pour repointer le trafic en arrière.

Phase 4 : Plan de migration (MySQL ↔ MariaDB)

Si vous changez de moteur, préférez construire un nouveau cluster et un cutover contrôlé :

  • Provisionnez le nouveau cluster avec le moteur/version cible.
  • Chargez les données de base via une restauration physique (si compatible) ou logique.
  • Streamer les changements en utilisant réplication/CDC (dépendant du moteur).
  • Validez avec checksums et lectures ombrées par l’application.
  • Basculer les écritures ; garder l’ancien cluster en lecture seule pendant la fenêtre de rollback.

Erreurs courantes : symptôme → cause racine → correction

1) Symptom: upgrade succeeds, but app can’t connect

Cause racine : mismatch du plugin d’authentification (ex. caching_sha2_password MySQL 8) ou incompatibilité TLS/chiffres dans les proxies/clients.

Correction : mettez à jour les connecteurs/proxies d’abord ; vérifiez avec un hôte canary. En urgence, définissez le plugin utilisateur sur mysql_native_password pour cet utilisateur et planifiez la correction adéquate.

2) Symptom: sudden query latency spike, same CPU usage

Cause racine : régression de plan de l’optimiseur ou changement du comportement des statistiques.

Correction : capturez EXPLAIN ANALYZE sur ancien vs nouveau ; ajoutez/ajustez les index ; mettez à jour les histogrammes si utilisés ; considérez des techniques de stabilité de plan (mais traitez les hints comme une dette).

3) Symptom: replicas lag heavily only after upgrade

Cause racine : le replica mis à niveau applique les événements ligne plus lentement à cause d’un comportement fsync différent, de réglages de réplication parallèle différents ou de limites IO.

Correction : validez la latence IO, augmentez le parallélisme des replicas quand supporté, assurez-vous que binlog_format et row image sont cohérents, et corrigez la saturation du stockage avant le cutover.

4) Symptom: failover takes forever, connections pile up

Cause racine : longues transactions / verrous de métadonnées / récupération après crash lente sur le candidat primaire.

Correction : identifiez et stoppez les longues transactions avant le cutover ; imposez les règles de fenêtre de maintenance pour les jobs batch ; vérifiez le temps de récupération par des redémarrages contrôlés en répétition.

5) Symptom: data looks “different” (ordering, uniqueness, comparisons)

Cause racine : différences de collation/jeu de caractères, ou changements de comportement des fonctions de comparaison JSON/text entre moteurs.

Correction : épinglez character_set_server/collation_server explicitement ; auditez les colonnes avec collations mixtes ; ajoutez COLLATE explicite dans les requêtes critiques si nécessaire ; évitez de changer les collations durant l’upgrade.

6) Symptom: monitoring dashboards broke on upgrade night

Cause racine : variables d’état modifiées, réglages Performance Schema ou changements de privilèges pour l’utilisateur de monitoring.

Correction : testez l’agent de monitoring contre une réplique mise à niveau durant la répétition ; mettez à jour les requêtes et grants ; conservez un contrôle minimal « DB heartbeat » indépendant des tableaux de bord sophistiqués.

7) Symptom: disk fills up mid-upgrade

Cause racine : croissance des binlogs, débordement de tables temporaires, expansion des redo logs pendant le rattrapage, ou artefacts d’upgrade restants.

Correction : assurez le headroom ; surveillez /var/lib/mysql et tmpdir ; placez tmpdir sur un volume dimensionné pour les tris pires cas ; ne raccourcissez pas agressivement la rétention des binlogs pendant la migration.

8) Symptom: queries that used to “work” now error

Cause racine : SQL mode plus strict, mots réservés, ou changements de valeurs par défaut.

Correction : inventairez sql_mode et épinglez-le ; exécutez la suite de tests applicative ; cherchez dans les logs les patterns « deprecated » et « error near » ; corrigez les requêtes plutôt que d’assouplir la correction globalement sauf si nécessaire.

FAQ

1) Should I upgrade MySQL 5.7 to 8.0 in-place?

Seulement si un temps d’arrêt est acceptable et que vous avez un chemin de restauration testé. Pour tout ce qui est important, mettez d’abord à niveau les réplicas, puis basculez.

2) Can I switch from MariaDB to MySQL by just replacing the binaries?

Non. Traitez cela comme une migration. Les dictionnaires de données, les tables système, les GTID et le comportement des fonctionnalités divergent. Construisez un nouveau cluster et migrez les données avec validation.

3) What’s the safest rollback strategy?

Gardez l’ancien primaire intact et potentiellement réécrivable seulement si vous faites un failback contrôlé avec une direction de réplication connue. Sinon gardez-le en lecture seule et comptez sur la restauration/CDC. Le rollback doit être répété.

4) How do I detect optimizer regressions before cutover?

Sélectionnez 20–50 requêtes critiques, capturez EXPLAIN ANALYZE et les statistiques runtime sur l’ancienne version, puis comparez sur une réplique mise à niveau avec des données et une charge proches de la production.

5) My app uses an old connector. What breaks first on MySQL 8?

L’authentification et TLS sont des points de rupture fréquents : caching_sha2_password, attentes de chiffrement plus strictes, et support des proxies. Mettez à jour clients/proxies avant le serveur si possible.

6) Do I need to run mysql_upgrade?

Ça dépend de la version et de la distribution. Les versions modernes de MySQL intègrent une partie de la logique d’upgrade, mais vous devez toujours suivre les étapes post-upgrade recommandées par l’éditeur. Règle opérationnelle : vérifiez les tables système et exécutez la procédure d’upgrade recommandée sur une réplique d’abord.

7) What about Galera clusters—can I do rolling upgrades?

Parfois. Cela dépend des versions exactes MariaDB/Galera. Validez le chemin supporté et testez en répétition. Si le rolling n’est pas supporté, construisez un nouveau cluster et basculez.

8) Should I change config defaults during the upgrade?

Pas à moins d’avoir une raison spécifique et un test qui le prouve. Changer la version du moteur est déjà une grande variable. Séparez les changements de tuning en un autre projet sauf si vous aimez des causes racines ambiguës.

9) How long should I keep the old cluster around after cutover?

Assez longtemps pour couvrir votre fenêtre réaliste de découverte de bugs — souvent des jours, pas des heures — équilibré avec le coût et le risque opérationnel. Gardez-le dans un état qui supporte votre plan de rollback (lecture seule ou prêt au failback).

10) What’s the fastest way to gain confidence right after cutover?

Surveillez trois choses : le taux d’erreur de l’application, la latence p95/p99 sur quelques endpoints clés, et la santé de la réplication (si vous avez des réplicas en aval). Si l’un d’eux dérive, mettez en pause et décidez vite.

Conclusion : prochaines étapes pratiques

Si vous ne retenez rien d’autre : les mises à niveau ne sont pas un passe-temps de week-end. Ce sont des changements contrôlés du système le plus état-plein que vous exploitez.

  1. Choisissez la méthode : rolling upgrade via réplication pour les upgrades même moteur ; migration pour les switches MySQL ↔ MariaDB.
  2. Répétez : restaurez des données réelles en staging, exécutez la charge, comparez les plans, testez le monitoring.
  3. Rendez le rollback réel : définissez des déclencheurs, gardez un ancien primaire/cluster prêt pour rollback, et entraînez la procédure.
  4. Mettez à niveau les réplicas d’abord : validez pendant des heures sous charge, pas des minutes.
  5. Coupez avec discipline : quiescez les jobs risqués, surveillez erreurs/latence/réplication, et soyez prêt à annuler.

L’objectif n’est pas une commande d’upgrade qui s’exécute. L’objectif est un système de production qui se comporte de manière prévisible le lendemain matin.

← Précédent
Conteneurs Docker multi-réseaux : empêcher l’exposition accidentelle au mauvais réseau
Suivant →
Performances de Ceph sur Proxmox lentes : 10 vérifications qui trouvent réellement le goulot d’étranglement

Laisser un commentaire