Vous avez fait la mise à jour en staging. Les tests ont passé. Les tableaux de bord étaient au vert. Puis la production vous réveille à 2h du matin avec un mystère : le lag de réplication augmente, la latence p95 a doublé,
et le slow query log ressemble à un roman policier écrit par un ORM nerveux.
Le mensonge « ça marche en staging » n’est presque jamais malveillant. C’est en général de la physique : forme des données différente, concurrence différente, comportement du stockage différent, SQL d’edge-case différent, plugins différents, TLS différent, et la chose que personne n’assume—l’urgence humaine différente.
MariaDB vs Percona Server : ce qui change réellement lors d’une mise à niveau
« MariaDB vs Percona Server » est souvent présenté comme un débat philosophique. En production, c’est plutôt comme déménager : vos meubles tiennent généralement, mais les encadrements de porte sont différents, la pression d’eau change, et le propriétaire a des opinions sur ce qui compte comme « standard ».
Percona Server est un fork de MySQL avec une instrumentation supplémentaire, des fonctions de performance et des réglages opérationnels. MariaDB est son propre fork avec sa propre trajectoire d’optimiseur,
des choix de réplication, des moteurs de stockage et une gestion des versions différentes. Les deux peuvent exécuter InnoDB (InnoDB de MariaDB s’appelait XtraDB dans les époques plus anciennes et a divergé au fil du temps),
les deux parlent « majoritairement MySQL », et les deux accepteront volontiers le SQL de votre application jusqu’au jour où ils ne le feront plus.
La compatibilité n’est pas une case à cocher ; c’est une matrice
Quand on dit « compatible », on entend souvent « les clients se connectent et le CRUD basique marche ». Ce n’est pas le niveau requis. Votre exigence est :
même exactitude, même enveloppe de performance, même comportement en cas de panne sous charge réelle et avec des données réelles.
Les points sensibles lors d’une mise à niveau entre MariaDB et Percona Server (ou à l’intérieur de chaque famille) se regroupent en cinq domaines :
- Sémantique SQL et plans d’optimiseur : même requête, plan différent ; même plan, estimations de lignes différentes ; valeurs par défaut SQL mode différentes.
- Comportement de réplication : saveur GTID, valeurs par défaut du binlog, sécurité en cas de crash, réglages de réplication parallèle.
- Comportement InnoDB/redo/undo/journal : dimensionnement des redo logs, valeurs par défaut de flush, changements de doublewrite, valeurs par défaut de checksum.
- Authentification/TLS/plugins : plugins d’authentification, versions/ciphers TLS par défaut, disponibilité et nommage des plugins.
- Hypothèses sur les outils opérationnels : attentes de Percona Toolkit, tables système, différences performance_schema/information_schema.
Si vous migrez entre familles (MariaDB ↔ Percona Server), la plus grosse erreur est de penser que l’histoire de la mise à niveau est la même qu’un « bump de version mineure MySQL ». Ce n’est pas le cas. Vous traversez une frontière de divergence, et la divergence est l’endroit où staging ment le plus fort.
Une idée paraphrasée de Gene Kim (auteur sur la fiabilité/opérations) : Améliorer les résultats vient d’améliorer le système, pas de demander aux gens d’en faire plus sous pression.
Traitez votre mise à niveau comme du design système, pas comme du travail de héros.
Contexte historique utile pour un postmortem
L’histoire ne répare pas les incidents, mais elle explique pourquoi votre « mise à jour simple » a des arêtes vives. Voici des faits à garder en tête lors de la planification :
- MariaDB a commencé comme un fork après l’acquisition de MySQL par Oracle (ère 2009–2010), ce qui a façonné sa position d’« gouvernance ouverte » et sa vitesse de divergence.
- Percona Server a grandi à partir d’une culture de tuning en production : le fork a priorisé l’instrumentation de performance et les contrôles opérationnels pour les systèmes sollicités.
- GTID n’est pas une chose universelle : l’implémentation GTID de MariaDB diffère de celle d’Oracle MySQL/Percona, ce qui compte pour la réplication cross-family et l’outillage de basculement.
- Performance Schema a mûri significativement de MySQL 5.6 → 5.7 → 8.0, et Percona suit généralement le modèle MySQL ; l’instrumentation de MariaDB et l’histoire du sys schema diffèrent.
- MariaDB a introduit des fonctionnalités sans équivalent MySQL (par ex., certaines stratégies d’optimiseur, tables versionnées système), ce qui peut changer le comportement d’exécution.
- Percona Toolkit est devenu un standard opérationnel de facto dans de nombreuses entreprises ; il fonctionne mieux quand le serveur se comporte selon les attentes MySQL/Percona.
- Les valeurs par défaut d’InnoDB ont évolué au fil des générations (taille des redo logs, heuristiques de flush, gestion des métadonnées). Les mises à niveau changent souvent des valeurs par défaut même si votre fichier de config ne change pas.
- Les plugins d’authentification ont évolué (caching_sha2_password dans l’univers MySQL 8.0 ; valeurs par défaut variées ailleurs), rendant le « le client marche en staging » dépendant de la version exacte du connecteur.
- UTF8 est devenu politique : la différence entre utf8 (3 octets) et utf8mb4 (4 octets) et la façon dont les collations par défaut sont choisies a causé d’innombrables surprises « les tests ont passé ».
Pourquoi staging ment : principaux modes de défaillance
1) La distribution de vos données est fausse
Staging a généralement moins de lignes, moins de clés chaudes, moins d’outliers pathologiques et moins de fragmentation. InnoDB se comporte différemment quand le working set tient en
mémoire versus quand il n’y tient pas. L’optimiseur se comporte différemment quand les histogrammes et les statistiques d’index reflètent un vrai skew.
Un mensonge classique : staging a une distribution uniforme de user_id ; la production a un « tennant entreprise » qui possède la moitié des lignes. Votre requête « sûre » devient un
hotspot mutex sous charge.
2) Votre concurrence est imaginaire
La plupart des tests de charge en staging se font à un niveau de concurrence poli et avec un cache chaud. La production est une bousculade. La contention de verrous, le purge lag, la pression redo et
le coût des fsync sont non linéaires. Vous ne pouvez pas extrapoler de « ça marche à 50 QPS » à « ça marche à 5 000 QPS » simplement parce que la requête est la même.
3) Les valeurs par défaut ont changé, mais vous ne l’avez pas remarqué
Une mise à niveau peut changer les valeurs par défaut pour binlog_format, sql_mode, heuristiques de flush InnoDB, gestion des tables temporaires, versions TLS, et plus encore. Si staging utilise un
my.cnf réglé à la main mais que la production a une dérive de configuration (ou vice versa), vous n’avez pas testé une mise à niveau — vous comparez deux univers différents.
4) La pile de stockage n’est pas la même
« Même type d’instance » n’est pas « même E/S ». Votre volume de staging peut être calme, votre volume de production peut être fortement sollicité. Votre staging peut être NVMe local,
la production peut être attachée en réseau. Vos options de montage de système de fichiers peuvent différer. Et oui, la politique de cache du contrôleur RAID compte.
Blague n°1 : le stockage est l’endroit où « oui mais c’est un SSD » va mourir, généralement pendant la fenêtre de maintenance que vous ne pouvez pas prolonger.
5) L’observabilité diffère, donc vous remarquez les problèmes plus tard
Staging a souvent plus de logs de debug, moins de trafic et moins de contraintes de conformité. La production a souvent une rétention des logs plus stricte, moins de métriques et plus
de choses qui crient au CPU. Quand vous mettez à niveau, vous changez aussi ce qui est mesurable et ce qui est coûteux à mesurer.
6) Le comportement de votre application change sous les feature flags de production
Staging exécute rarement exactement les mêmes feature flags, timeouts, logique de retry, circuit breakers, profondeurs de file d’attente et tailles de lots. Une mise à niveau de base de données peut révéler
un petit changement de latence de requête, ce qui déclenche une tempête de retries, qui devient une amplification d’écriture, qui devient votre weekend.
Playbook de diagnostic rapide (premier/deuxième/troisième)
Quand la mise à niveau tourne mal, vous n’avez pas le temps d’admirer les graphes. Il faut trouver le goulot d’étranglement rapidement et choisir une mitigation sûre. Voici un playbook rapide qui marche pour MariaDB et Percona Server.
Premier : la base de données est-elle liée CPU, I/O ou verrous ?
- Liée CPU : mysqld consomme beaucoup de CPU, temps « executing » élevé, beaucoup de handler reads, plans changés, index manquants ou choix d’optimiseur différents.
- Liée I/O : iowait élevé, faible hit rate du buffer pool, pression redo/fsync, tables temporaires sur disque, problèmes d’âge de checkpoint.
- Liée verrous : threads en attente de row locks, metadata locks ou latches internes ; thread SQL de réplication bloqué ; DDL bloquant.
Second : la douleur est-elle concentrée sur un motif de requête ?
Cherchez une ou deux empreintes dominantes : une requête qui a changé de plan, un thread background qui sature les I/O, ou un goulet d’application de la réplication. Si le principal coupable explique plus d’environ 30–40 % du temps,
traitez-le comme la cause de l’incident jusqu’à preuve du contraire.
Troisième : s’agit-il d’exactitude, de performance ou de stabilité ?
- Exactitude : discordances de données, troncature silencieuse, changements de collation, différences de sql_mode, dérive de timezone.
- Performance : régression de latence, chute de débit, lag de réplication, tempêtes de connexions.
- Stabilité : crashs, OOM kills, disque plein, journaux corrompus, échecs de plugins, échecs de négociation TLS.
Votre réponse diffère. Les problèmes d’exactitude signifient souvent rollback ou mode lecture seule jusqu’à vérification. Les problèmes de performance signifient habituellement atténuer la charge et revenir sur des configs.
Les problèmes de stabilité signifient arrêter l’hémorragie : assurer la sécurité des données, puis diagnostiquer.
Tâches de vérification pratiques (commandes, sorties, décisions)
Ce sont les tâches qui séparent « nous avons testé » de « nous avons prouvé ». Chaque tâche a : une commande, une sortie d’exemple, ce que ça signifie et quelle décision prendre.
Exécutez-les en staging et en production avant et après la mise à niveau. Différenciez les résultats. Si vous ne faites pas de diff, vous vous fiez aux impressions.
Tâche 1 : Confirmer l’identité du serveur et la lignée de version
cr0x@server:~$ mysql -NBe "SELECT VERSION(), @@version_comment, @@version_compile_machine;"
8.0.36-28 Percona Server (GPL), Release 28, Revision 1234abcd x86_64
Signification : VERSION() et version_comment vous disent si vous êtes sur MariaDB, Percona ou autre chose déguisée.
Décision : Si ce n’est pas la distribution/version voulue exactement, stoppez. Votre « mise à jour » pourrait être un problème de dépôts mélangés.
Tâche 2 : Dumper et diff des variables runtime qui changent le comportement
cr0x@server:~$ mysql -NBe "SHOW VARIABLES WHERE Variable_name IN ('sql_mode','binlog_format','transaction_isolation','innodb_flush_log_at_trx_commit','sync_binlog','character_set_server','collation_server','log_bin','gtid_mode','enforce_gtid_consistency');"
sql_mode ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
binlog_format ROW
transaction_isolation REPEATABLE-READ
innodb_flush_log_at_trx_commit 1
sync_binlog 1
character_set_server utf8mb4
collation_server utf8mb4_0900_ai_ci
log_bin ON
gtid_mode ON
enforce_gtid_consistency ON
Signification : Ces variables façonnent l’exactitude, la sécurité de réplication et la performance. Elles changent aussi entre versions majeures et entre forks.
Décision : Si staging et production ne correspondent pas, corrigez la dérive avant de faire confiance aux tests. Si une mise à niveau change des valeurs par défaut, figez-les explicitement.
Tâche 3 : Vérifier la taille du buffer pool InnoDB et la pression
cr0x@server:~$ mysql -NBe "SHOW VARIABLES LIKE 'innodb_buffer_pool_size'; SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_reads'; SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read_requests';"
innodb_buffer_pool_size 34359738368
Innodb_buffer_pool_reads 918273
Innodb_buffer_pool_read_requests 5544332211
Signification : Reads vs read_requests approximativement correspond aux misses du cache. Une montée de buffer_pool_reads après la mise à niveau indique souvent un nouveau working set,
un changement de plan ou du churn en backgound.
Décision : Si le taux de misses s’aggrave significativement, vous êtes probablement I/O-bound. Augmentez le buffer pool (si sûr), corrigez les plans de requête ou réduisez le churn.
Tâche 4 : Inspecter la pression redo et checkpoint (InnoDB log waits)
cr0x@server:~$ mysql -NBe "SHOW GLOBAL STATUS LIKE 'Innodb_log_waits'; SHOW VARIABLES LIKE 'innodb_redo_log_capacity';"
Innodb_log_waits 742
innodb_redo_log_capacity 4294967296
Signification : Innodb_log_waits > 0 signifie que des transactions de premier plan ont attendu le redo. C’est le classique « vos redo sont trop petits ou le flush est trop lent ».
Décision : Si les waits augmentent pendant la charge, augmentez la capacité redo (là où supporté), revoyez les réglages de flush et vérifiez la latence fsync du stockage.
Tâche 5 : Vérifier la latence disque et la saturation au niveau OS
cr0x@server:~$ iostat -x 1 3
Linux 6.5.0 (db-prod-01) 12/30/2025 _x86_64_ (16 CPU)
avg-cpu: %user %nice %system %iowait %steal %idle
22.11 0.00 6.08 18.47 0.00 53.34
Device r/s w/s rkB/s wkB/s aqu-sz await svctm %util
nvme0n1 320.0 980.0 20480.0 65536.0 9.40 8.90 0.55 72.00
Signification : Await élevé et util élevé indiquent une pression sur le stockage. iowait > ~10–20 % pendant l’incident est un indice fort.
Décision : Si await bondit avec la mise à niveau, suspectez redo/fsync, débordement de tables temporaires ou changement du flushing en background. Mitigez en réduisant le taux d’écriture,
en augmentant la mémoire ou en passant à des volumes plus rapides.
Tâche 6 : Valider l’état et les coordonnées de la réplication
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: 84
Last_SQL_Error:
Retrieved_Gtid_Set: 3b2c1d10-aaaa-bbbb-cccc-111111111111:1-982733
Executed_Gtid_Set: 3b2c1d10-aaaa-bbbb-cccc-111111111111:1-982649
Signification : Seconds_Behind_Source qui augmente + un écart GTID qui s’élargit indique que l’application n’arrive pas à suivre.
Décision : Si le lag apparaît seulement après la mise à niveau, vérifiez les réglages de réplication parallèle, le binlog_format et la pression fsync sur la réplique.
Tâche 7 : Capturer les régressions de plan avec EXPLAIN et optimizer trace (ciblé)
cr0x@server:~$ mysql -e "EXPLAIN SELECT o.id FROM orders o JOIN customers c ON c.id=o.customer_id WHERE c.email='x@example.com' AND o.status='OPEN'\G"
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: c
type: ref
possible_keys: idx_email
key: idx_email
rows: 1
filtered: 100.00
Extra: Using index
*************************** 2. row ***************************
table: o
type: ref
possible_keys: idx_customer_status
key: idx_customer_status
rows: 12
filtered: 10.00
Extra: Using where
Signification : Vous cherchez des changements dans l’ordre des jointures, le type d’accès et les estimations de lignes. « type: ALL » là où vous aviez « ref » est un drapeau rouge.
Décision : Si le plan a changé, envisagez de mettre à jour les statistiques, d’ajouter des indexes ou de forcer avec des hints (dernier recours). Vérifiez aussi que les collations et types de données correspondent.
Tâche 8 : Comparer la fraîcheur des statistiques des tables
cr0x@server:~$ mysql -NBe "SELECT table_schema, table_name, update_time FROM information_schema.tables WHERE table_schema='app' ORDER BY update_time DESC LIMIT 5;"
app orders 2025-12-30 01:12:44
app customers 2025-12-30 01:10:02
app order_items 2025-12-29 23:45:11
app invoices 2025-12-29 21:03:19
app payments 2025-12-29 19:58:07
Signification : update_time peut être trompeur selon le moteur et les réglages, mais c’est un indice rapide pour savoir si la maintenance a tourné et si les tables sont « fraîches ».
Décision : Si les tables semblent obsolètes après migration/import, lancez ANALYZE TABLE pour les tables critiques et re-vérifiez EXPLAIN pour les requêtes clés.
Tâche 9 : Vérifier les débordements de tables temporaires et la pression de tri
cr0x@server:~$ mysql -NBe "SHOW GLOBAL STATUS LIKE 'Created_tmp%'; SHOW VARIABLES LIKE 'tmp_table_size'; SHOW VARIABLES LIKE 'max_heap_table_size';"
Created_tmp_tables 182773
Created_tmp_disk_tables 44291
Created_tmp_files 9312
tmp_table_size 134217728
max_heap_table_size 134217728
Signification : Un ratio élevé de Created_tmp_disk_tables signifie que des requêtes déversent sur disque. Les mises à niveau peuvent changer le comportement interne des tables temporaires et les valeurs par défaut.
Décision : Si les débordements disque ont augmenté après la mise à niveau, augmentez tmp_table_size/max_heap_table_size prudemment, et corrigez les requêtes et index problématiques.
Tâche 10 : Identifier les motifs de requêtes principaux par digest (performance schema)
cr0x@server:~$ mysql -NBe "SELECT DIGEST_TEXT, COUNT_STAR, ROUND(SUM_TIMER_WAIT/1e12,2) AS total_s, ROUND(AVG_TIMER_WAIT/1e9,2) AS avg_ms FROM performance_schema.events_statements_summary_by_digest ORDER BY SUM_TIMER_WAIT DESC LIMIT 3;"
SELECT * FROM orders WHERE customer_id = ? AND status = ? ORDER BY created_at DESC LIMIT ? 882771 912.22 1.03
UPDATE inventory SET qty = qty - ? WHERE sku = ? AND qty >= ? 221009 644.11 2.91
SELECT id FROM customers WHERE email = ? 775002 312.88 0.40
Signification : Si un digest domine le temps total, vous avez une cible chirurgicale. Si tout est devenu plus lent uniformément, suspectez I/O ou contention globale.
Décision : Corrigez d’abord les digests principaux. Si le digest principal est nouveau après la mise à niveau, suspectez un changement de plan ou un SQL modifié par le connecteur.
Tâche 11 : Vérifier les accumulations de metadata locks (le tueur silencieux)
cr0x@server:~$ mysql -e "SELECT OBJECT_SCHEMA, OBJECT_NAME, LOCK_TYPE, LOCK_STATUS, COUNT(*) cnt FROM performance_schema.metadata_locks GROUP BY 1,2,3,4 ORDER BY cnt DESC LIMIT 5;"
+--------------+-------------+-----------+-------------+-----+
| OBJECT_SCHEMA | OBJECT_NAME | LOCK_TYPE | LOCK_STATUS | cnt |
+--------------+-------------+-----------+-------------+-----+
| app | orders | SHARED_READ | GRANTED | 132 |
| app | orders | EXCLUSIVE | PENDING | 1 |
+--------------+-------------+-----------+-------------+-----+
Signification : Un verrou EXCLUSIVE en attente plus une pile de SHARED granted est le classique « quelqu’un a lancé un DDL et maintenant tout attend ».
Décision : Si vous voyez ça pendant la mise à niveau, arrêtez le DDL ou programmez-le correctement avec des outils de changement de schéma en ligne et des fenêtres hors-pointe.
Tâche 12 : Valider le journal d’erreurs pour régressions plugin/auth/TLS
cr0x@server:~$ sudo tail -n 30 /var/log/mysql/error.log
2025-12-30T01:20:11.123456Z 0 [Warning] [MY-010068] [Server] CA certificate ca.pem is self signed.
2025-12-30T01:20:15.765432Z 12 [ERROR] [MY-010054] [Server] Slave SQL: Error 'Unknown collation: 'utf8mb4_0900_ai_ci'' on query. Default database: 'app'. Query: 'CREATE TABLE ...', Error_code: 1273
Signification : Mismatch de collation et warnings TLS sont des pièges courants cross-version/cross-fork. « Unknown collation » est un problème de frontière de migration.
Décision : Si la collation n’est pas supportée sur la cible, vous devez convertir le schéma/colonnes ou choisir une collation compatible avant de répliquer/importer.
Tâche 13 : Valider la dérive de schéma entre staging et production (DDL diff via mysqldump)
cr0x@server:~$ mysqldump --no-data --routines --triggers --databases app | sha256sum
c0a5f4c2f6d7f6bb38a39b9c1d9e2c11caa8d9b7a0f1b2c3d4e5f6a7b8c9d0e1 -
Signification : Hasher le dump de schéma est grossier, mais efficace. Si les hashes diffèrent, vos environnements ne sont pas équivalents.
Décision : Si le schéma diffère, cessez d’affirmer que staging a validé la production. Reconstruisez staging à partir du schéma de production (et de préférence avec des données masquées).
Tâche 14 : Vérifier la validité de backup/restore avant de toucher la production
cr0x@server:~$ xtrabackup --prepare --target-dir=/backups/xb_2025-12-30_0100
xtrabackup: This target seems to be prepared.
Signification : Un backup que vous n’avez pas préparé/restauré est une théorie, pas un backup. Prepare valide la capacité du backup à devenir cohérent.
Décision : Si prepare échoue ou prend beaucoup plus de temps après la mise à niveau, considérez-le comme un bloqueur de release : votre chemin de récupération est compromis.
Tâche 15 : Exécuter un micro-benchmark à la forme de production (pas un benchmark de vanité)
cr0x@server:~$ sysbench oltp_read_write --mysql-host=127.0.0.1 --mysql-user=sb --mysql-password=sb --mysql-db=sbtest --tables=16 --table-size=500000 --threads=64 --time=120 --report-interval=10 run
SQL statistics:
queries performed:
read: 1456720
write: 416206
other: 208103
transactions: 104051 (867.03 per sec.)
Latency (ms):
min: 2.31
avg: 73.81
max: 512.24
Signification : Il ne s’agit pas du TPS absolu ; il s’agit de comparer avant/après sur le même matériel et la même config. Surveillez la latence et les pics max.
Décision : Si la latence max explose après la mise à niveau, vous avez probablement des soucis de fsync/redo, de contention mutex ou de débordements temps-temp. Ne mettez pas en production.
Tâche 16 : Valider les erreurs de connexion et le comportement de handshake
cr0x@server:~$ mysqladmin -uroot -p extended-status | egrep 'Aborted_connects|Connection_errors_internal|Connection_errors_max_connections|Threads_connected'
Aborted_connects 17
Connection_errors_internal 0
Connection_errors_max_connections 4
Threads_connected 412
Signification : Les mises à niveau peuvent changer les plugins d’auth, les exigences TLS et les timeouts. Des pics d’aborted_connects peuvent venir d’incompatibilité client ou de charge.
Décision : Si les connections avortées augmentent uniquement après la mise à niveau, validez les versions des connecteurs, les réglages TLS et max_connections/thread_cache.
Blague n°2 : la seule chose plus confiante qu’un ORM est un ORM après que vous ayez mis à niveau la base de données sous lui.
Trois micro-récits d’entreprise (douloureux, réels, utiles)
Micro-récit 1 : L’incident causé par une mauvaise hypothèse (GTID « c’est GTID »)
Une entreprise SaaS de taille moyenne a planifié une migration « simple » : remplacer un cluster MariaDB vieillissant par Percona Server parce que la nouvelle pile d’observabilité parlait MySQL/Percona plus couramment. Staging s’est bien passée. La réplication fonctionnait. Les exercices de failover semblaient assez propres pour signer.
La mauvaise hypothèse était subtile : ils ont présumé que le comportement GTID était portable comme « le port 3306 est portable ». En staging, ils utilisaient des dumps logiques et des réplicas short-lived. En production, ils dépendaient d’une automatisation basée sur GTID dans leur outillage de basculement et s’attendaient à ce que cela continue à fonctionner après la bascule.
Pendant le cutover en production, la réplique promue est montée, mais l’automatisation a refusé de rattacher un nœud en retard. Puis une deuxième réplique a appliqué des transactions dans un ordre qui ne correspondait pas à leurs attentes. Personne n’a perdu de données, mais ils ont perdu du temps, et le temps est un bug d’accessibilité.
Le postmortem a trouvé le véritable coupable : le plan de migration traitait le GTID de MariaDB et le GTID MySQL/Percona comme suffisamment interchangeables pour « juste marcher », sans valider les hypothèses de l’outillage de basculement. Staging n’incluait pas la couche d’automatisation ni la topologie de réplication long-lived.
La correction n’était pas héroïque. Ils ont reconstruit le plan : traiter la saveur GTID comme une frontière dure, valider les procédures de basculement de bout en bout, et inclure l’outil d’automatisation dans les répétitions de staging avec des réplicas long-lived et une injection de lag réaliste. Le cutover suivant fut… ennuyeux, ce qui est le plus grand compliment en opérations.
Micro-récit 2 : L’optimisation qui s’est retournée contre eux (plus gros redo logs, pire latence tail)
Une équipe fintech a mis à niveau Percona Server au sein de la même famille majeure et a observé des stalls d’écriture intermittents en production. Quelqu’un a repéré Innodb_log_waits qui augmentait
et a pris une décision raisonnable : augmenter la capacité redo. Un redo plus grand signifie moins de checkpoints, moins de stalls. C’est le folklore.
Ils ont déployé le changement, et la latence moyenne s’est améliorée. Puis la latence tail est devenue étrange. La latence p99 d’écriture a flambé lors de pointes de trafic, et les réplicas ont pris plus de retard.
Les graphes ressemblaient à une mer calme avec des monstres marins occasionnels.
Le vrai problème n’était pas la taille du redo ; c’était le comportement I/O sous charge bursty combiné aux caractéristiques du stockage. Un redo plus grand a changé le timing du flush et des checkpoints.
Au lieu de petits travaux d’arrière-plan fréquents, ils ont créé de plus larges phases d’écriture moins prévisibles qui se sont alignées mal avec la variabilité du débit de leur volume de stockage.
La correction a été de cesser de traiter le redo comme un simple bouton. Ils ont instrumenté la latence fsync, ajusté le flushing, vérifié le doublewrite, et assuré que les réplicas avaient suffisamment de capacité I/O. La taille du redo est restée plus grande qu’avant—mais pas « aussi grande que possible », et pas sans vérifier le comportement des queues tail.
La leçon : les réglages de performance ne sont pas des bonbons gratuits. Tout bouton qui déplace le moment où le travail se produit peut générer de la latence tail. Si vous ne regardez que les moyennes, vous livrerez des régressions avec le sourire.
Micro-récit 3 : La pratique ennuyeuse mais correcte qui a sauvé la mise (hash de schéma + gel de configuration)
Une équipe d’entreprise a dû passer de MariaDB à Percona Server à cause de contraintes fournisseur dans leur plateforme. Ils n’étaient pas enthousiastes. Ils étaient aussi disciplinés, ce qui est la version adulte d’être enthousiaste.
Ils ont commencé par deux non-négociables : un gel de configuration et une porte de hachage de schéma. Pendant quatre semaines avant le cutover, les changements de my.cnf en production étaient interdits sauf liés à un incident.
Tout changement nécessitait une revue de diff et une rejoue en staging. Pendant ce temps, staging était reconstruit chaque nuit depuis le schéma de production, et le dump de schéma était haché et comparé dans le CI.
Lors de la répétition générale, la porte de hachage de schéma a attrapé une petite différence : une collation sur une table en staging ne correspondait pas à la production. Ce n’était pas intentionnel.
C’était le résidu d’une migration à moitié terminée des mois plus tôt qui n’avait jamais atteint la production.
Ils ont corrigé staging pour qu’il corresponde à la production, relancé la répétition de mise à niveau, et ont trouvé une régression de plan de requête qui n’apparaissait qu’avec la collation de production et la cardinalité d’index.
C’est la partie où les gens disent « on a eu de la chance ». Ce n’était pas de la chance. C’était un processus ennuyeux faisant son travail.
La nuit du cutover a été sans histoire. Les meilleures histoires de mise à niveau n’entrent pas dans le folklore de l’entreprise parce que personne n’aime se remémorer des choses qui se sont bien passées.
Erreurs courantes : symptôme → cause racine → correction
1) Symptôme : la réplication casse avec « Unknown collation » ou « Unknown character set »
Cause racine : Le schéma source utilise une collation/charset non supporté sur la cible (commun aux frontières MariaDB ↔ MySQL/Percona, et aux sauts MySQL 5.7 → 8.0).
Correction : Convertir le schéma avant la migration. Standardiser sur utf8mb4 et choisir des collations supportées des deux côtés. Vérifier en restaurant un dump schema-only sur la cible.
2) Symptôme : les requêtes ralentissent, CPU inchangé et I/O plus élevé
Cause racine : les misses du buffer pool ont augmenté à cause de changements de plan ou du comportement des tables temporaires ; les données de staging tenaient en RAM, pas la production.
Correction : Comparer les plans EXPLAIN, mettre à jour les statistiques (ANALYZE), ajouter/ajuster des indexes, et vérifier tmp_table_size et les différences d’engines internes pour les temps-temp.
3) Symptôme : p99 write latency s’envole après la mise à niveau
Cause racine : pression redo/fsync, heuristiques de flushing changées, ou variabilité de la pile de stockage. Parfois causé par des changements « aidants » de config pendant la mise à niveau.
Correction : Mesurer la latence fsync (OS + base), revoir innodb_flush_log_at_trx_commit et sync_binlog, tuner la capacité redo de façon responsable, et assurer de la marge I/O sur le volume.
4) Symptôme : « trop de connexions » ou tempêtes de connexions après le cutover
Cause racine : incompatibilité handshake/plugin auth, échecs de négociation TLS, ou retries applicatifs amplifiant une petite régression de latence.
Correction : Figer le plugin d’auth et les réglages TLS, valider les versions des connecteurs en tests production-like, ajuster les timeouts, et déployer des limites saines de connection pooling.
5) Symptôme : DDL provoque des stalls système pendant la fenêtre de mise à niveau
Cause racine : metadata locks ; les hypothèses d’online DDL ne sont pas valides pour cette opération/version ; transactions longues tenant des verrous.
Correction : Utiliser des outils de changement de schéma en ligne (ou DDL natif en ligne quand sûr), tuer/éviter les transactions longues, définir des lock wait timeouts, et planifier le DDL plus tôt.
6) Symptôme : les réplicas prennent du retard seulement après la mise à niveau
Cause racine : réplication parallèle non réglée ou valeurs par défaut changées ; binlog format changé ; différences d’ordre de commit ; I/O du réplica plus faible que la primaire.
Correction : Vérifier binlog_format=ROW, tuner le parallélisme du réplica, augmenter la capacité I/O du réplica, et assurer que la configuration du réplica correspond à la charge post-upgrade.
7) Symptôme : “Deadlocks en hausse” et erreurs applicatives
Cause racine : changements de plans d’exécution ou d’usage d’index entraînant un ordre d’acquisition de verrous différent ; augmentation de la concurrence ; valeurs par défaut d’isolation/sql_mode différentes.
Correction : Comparer les plans, ajouter des indexes de support, réduire la portée des transactions, et s’assurer que transaction_isolation et sql_mode sont figés et revus.
8) Symptôme : le disque se remplit de façon inattendue pendant ou après la migration
Cause racine : binlogs plus gros, débordements de tables temporaires, slow log en mode verbeux, artefacts de backup non pivotés, ou croissance redo/undo.
Correction : Pré-allouer un budget d’espace, imposer la rotation des logs, limiter la rétention, monitorer /var/lib/mysql et tmpdir, et valider que les scripts de migration nettoient derrière eux.
Checklists / plan pas à pas (ennuyeux volontairement)
Phase 0 : Décider quel type de « mise à niveau » vous faites réellement
- Mise à niveau intra-famille (Percona → Percona, MariaDB → MariaDB) : toujours risqué, mais l’outillage et la sémantique sont plus proches.
- Migration cross-family (MariaDB ↔ Percona) : traitez-la comme une migration, pas comme une mise à jour. Différents GTID, collations, optimiseur, tables système différentes.
- Saut de version majeur : supposez des changements de comportement sauf preuve du contraire.
Phase 1 : Faire en sorte que staging arrête de mentir
- Reconstruire staging schema chaque nuit depuis la production. Hacher le dump de schéma. Bloquer si mismatch.
- Charger staging avec une forme de données production-like : snapshot de production masqué ou jeu de données généré avec skew et cardinalité similaires.
- Faire correspondre la config : même my.cnf, mêmes kernel/sysctl, mêmes options de montage du FS, même classe de stockage.
- Rejouer les patterns de trafic de production (y compris les pointes). Ne vous contentez pas de lancer des tests unitaires.
- Inclure la couche d’automatisation : outillage de failover, backups, monitoring, système de migration de schéma.
Phase 2 : Garde-fous de compatibilité avant-vol
- Vérification compatibilité collation/charset sur l’ensemble du schéma.
- SQL mode et strictness : garantir le comportement attendu pour les inserts, group by et conversions implicites.
- Plugins d’auth et TLS : s’assurer que chaque librairie cliente peut se connecter avec les nouveaux réglages par défaut.
- Plan de réplication : décider d’une stratégie GTID ; éviter « on verra pendant le cutover ».
- Répétition backup/restore : prouver que vous pouvez restaurer et promouvoir dans votre RTO.
Phase 3 : Garde-fous de performance (prouver que vous n’avez pas déplacé le goulot dans l’ombre)
- Benchmarkez avec la concurrence et les patterns de burst de production ; enregistrez p95/p99 et latences max.
- Comparez les top digests avant/après ; signalez les nouveaux principaux coupables et les régressions de plans.
- Validez la marge I/O et le comportement redo sous pointes d’écriture.
- Vérifiez les débordements de tables temporaires et la pression de tri.
Phase 4 : Plan de cutover qui respecte la réalité du rollback
- Définir des triggers de rollback : erreur d’exactitude, instabilité de réplication, régression soutenue de latence, augmentation du taux d’erreur.
- Geler les changements de schéma pendant la fenêtre de cutover.
- Réduire le blast radius : canary sur un sous-ensemble de trafic ou de tenants, si votre architecture le permet.
- Exécuter les mêmes tâches de vérification pendant le cutover que lors de la répétition. Mêmes commandes, mêmes diffs, mêmes portes.
- Ne pas improviser de « quick optimizations » pendant un incident à moins de pouvoir les mesurer et les annuler en toute sécurité.
FAQ
1) Percona Server est-il « juste MySQL » et MariaDB « juste MySQL » ?
Pas dans le sens où votre plan de mise à niveau s’en soucie. Percona suit MySQL de près mais ajoute des fonctionnalités et des builds. MariaDB a plus divergé avec le temps. Traitez les déplacements cross-family
comme des migrations avec validation de compatibilité, pas comme de simples mises à jour.
2) Puis-je répliquer de MariaDB vers Percona Server (ou l’inverse) pendant la migration ?
Parfois, oui, mais ce sont les edge cases qui font saigner : mismatch de saveur GTID, différences de collation, et hypothèses statement/row format. Si vous le faites, prouvez-le avec une répétition de réplication long-lived,
pas un test staging de 30 minutes.
3) Quelle est la cause unique la plus fréquente du « ça marche en staging » ?
La forme des données. Les datasets de staging reproduisent rarement le skew, la fragmentation et les clés chaudes de la production. Les optimisateurs et caches se comportent autrement quand la réalité est désordonnée.
4) Dois-je figer toutes les variables de configuration avant la mise à niveau ?
Figez celles qui changent la sémantique ou la durabilité : sql_mode, binlog_format, transaction isolation, réglages de flush et sync_binlog, charset/collation, et politique d’auth/TLS. Ne figez pas des knobs au hasard que vous ne comprenez pas ; vous fossiliseriez d’anciennes erreurs.
5) Comment détecter tôt les régressions de plan de requête ?
Capturez les digests de requêtes principales, exécutez EXPLAIN sur des requêtes représentatives, comparez avant/après, et validez avec des statistiques proches de la production (ANALYZE). Surveillez aussi les débordements temp et les patterns de handler reads, pas seulement le temps de requête.
6) Pourquoi le lag de réplication a-t-il augmenté après la mise à niveau alors que les écritures n’ont pas augmenté ?
L’application sur le réplica peut être limitée par l’apply single-threaded, des contraintes d’ordre de commit, la pression fsync, ou des valeurs par défaut de réplication parallèle différentes. La primaire peut gérer, mais la réplique n’a pas le budget I/O pour appliquer assez vite.
7) Est-il sûr de changer les réglages redo/flush pendant un incident ?
Seulement si vous pouvez mesurer le résultat rapidement et si vous comprenez le compromis de durabilité. Certains réglages réduisent le coût des fsync au prix de la sécurité en cas de crash.
Si votre incident touche l’exactitude, ne sacrifiez pas l’intégrité pour la vitesse sans accord de la direction.
8) Comment savoir si staging est « suffisamment proche de la production » ?
Quand vos tâches de vérification diffèrent proprement (schéma/config), votre benchmark reproduit les goulots de production, et vos drills (restore backup, rebuild réplica, failover) se comportent de la même façon.
Si vous ne pouvez pas reproduire la douleur de production en staging, staging n’est pas réaliste.
9) Devrions-nous utiliser des dumps logiques ou des backups physiques pour la migration ?
Les dumps logiques sont portables mais lents et peuvent changer des choses subtiles (definers, collations, jeux de caractères). Les backups physiques sont rapides mais exigent une compatibilité engine/version.
Pour les mouvements cross-family, beaucoup d’équipes combinent les méthodes : physique dans la famille, logique entre familles, toujours répété.
10) Quel est un critère de succès raisonnable pour une mise à niveau ?
Exactitude validée, réplication stable, et performance dans un budget de régression défini (par exemple, pas de régression soutenue du p95 et pas d’explosion du p99).
Plus : backup/restore fonctionne toujours et le monitoring dit la vérité.
Prochaines étapes réalisables cette semaine
Si vous planifiez un mouvement MariaDB ↔ Percona Server (ou même une mise à niveau intra-famille « facile »), ne commencez pas par débattre des fonctionnalités. Commencez par éliminer les mensonges de staging.
- Construisez un rapport de diff : exécutez les Tâches 1–3 et 13 sur les deux environnements et corrigez la dérive jusqu’à ce qu’ils correspondent.
- Choisissez 10 requêtes critiques : exécutez EXPLAIN avant/après, et enregistrez les plans comme artefacts.
- Faites une seule relecture de trafic réaliste : sysbench convient comme base, mais rejouez aussi un segment de trafic de production si vous le pouvez.
- Répétez la récupération : préparez et restaurez des backups, prouvez la promotion, et vérifiez que vous pouvez reconstruire une réplique dans votre RTO.
- Rédigez les triggers de rollback : rendez-les mesurables (lag, taux d’erreur, p99), et obtenez un accord avant la fenêtre de maintenance.
Les mises à niveau n’échouent pas parce que les ingénieurs sont négligents. Elles échouent parce que la réalité est différente à grande échelle et que le plan supposait le contraire. Faites de la réalité une partie de votre harness de test, et « ça marche en staging » deviendra moins un mensonge et plus une prédiction utile.