Vous avez acheté du NVMe. Les graphiques de benchmark ressemblaient à une expérience religieuse. Puis la production est arrivée : pics de latence p99, CPU « idle »
mais requêtes bloquées, et un graphe de stockage qui ressemble à un cardiogramme. Quelqu’un dit « ce n’est que le flush », un autre propose « augmentez io_capacity »,
et une troisième personne suggère « désactivez la durabilité pour les performances » comme si c’était une phrase adulte.
Ceci est le guide pratique pour exécuter MySQL ou MariaDB sur NVMe sans deviner. Nous allons régler les journaux redo, le comportement des flushs et la capacité d’E/S
avec des commandes que vous pouvez lancer aujourd’hui — plus les modes de défaillance que vous rencontrerez si vous ne le faites pas.
Faits et contexte historique (ce qui explique les étrangetés)
- InnoDB n’est pas né sur du flash. Les hypothèses initiales d’InnoDB convenaient aux disques rotatifs : les écritures séquentielles étaient prioritaires, le hasard coûtait cher. NVMe renverse le profil de douleur.
- Le tampon doublewrite existe à cause des pages déchirées. Une coupure d’alimentation pendant l’écriture d’une page de 16 KB peut laisser des données moitié anciennes, moitié nouvelles. Le doublewrite est la « ceinture de sécurité ».
- MySQL 8.0 a changé le format des journaux redo. Le « nouveau redo » (et le travail en cours sur les écritures atomiques) a modifié certains comportements de performance et de récupération par rapport aux versions plus anciennes.
- MariaDB a divergé sur les fonctionnalités et les valeurs par défaut. Elle a conservé InnoDB (ou XtraDB auparavant), mais le comportement, les variables et les détails d’implémentation diffèrent suffisamment pour impacter le tuning.
- Le group commit est la raison pour laquelle vous pouvez avoir durabilité et débit. Plusieurs transactions partagent un seul fsync quand le moteur les regroupe. Votre charge décide si vous obtenez ce gain.
- NVMe n’est pas « un disque plus rapide ». C’est une interface différente avec des files profondes et du parallélisme. Une charge mono-thread fsync-intensive peut rester lente.
- Le checkpointing est la taxe cachée. Les journaux redo permettent à InnoDB de différer le flush, mais la facture arrive sous forme de pression sur les checkpoints et de travail du nettoyeur de pages.
- La couche bloc de Linux a évolué pour le flash. Les anciens réglages supposaient SATA/SAS ; le NVMe moderne veut souvent le scheduler « none » et des paramètres de writeback étudiés plutôt que des astuces d’ancienne école.
- Le NVMe dans le cloud est parfois « en forme de NVMe ». Beaucoup d’instances exposent des périphériques NVMe reposant sur un stockage réseau. La variance de latence peut être réelle même si le débit est élevé.
Une citation à garder sur un post‑it quand vous êtes tenté de « juste tuner jusqu’à ce que ça file » :
L’espoir n’est pas une stratégie.
— Général Gordon R. Sullivan
Le modèle mental NVMe + InnoDB : ce qui se passe réellement
La manière la plus rapide de perdre une semaine est de traiter InnoDB comme « il écrit des choses sur le disque » et NVMe comme « il écrit vite ».
En production, ce qui compte est quelles écritures, quand elles ont lieu, et à quel point elles sont en rafales.
Trois flux d’E/S que vous devez séparer dans votre tête
- Écritures du journal redo : append séquentiel-ish vers les fichiers ib_logfile/journaux redo. Ils sont régulés par la politique de flush et le coût des fsync.
- Flush des pages de données : pages sales de 16 KB écrites sur les tablespaces en tâche de fond (page cleaners) ou sous contrainte.
- Écritures doublewrite : écritures supplémentaires pour rendre les flushs de pages résilients aux crashs (sauf si vous utilisez des écritures atomiques / des réglages qui changent cela).
NVMe aide tous ces flux, mais pas à égalité. Les écritures redo sont typiquement petites et sensibles à la latence (fsync). Les flushs de pages demandent du débit.
Le doublewrite peut devenir une « mort par mille petites écritures » si mal configuré, ou rester largement invisible si aligné avec les forces du périphérique.
Pourquoi votre latence p99 explose même sur un NVMe rapide
InnoDB est un comptable. Il vous laisse volontiers « dépenser » du crédit d’E/S en mettant des pages sales en mémoire. Quand il faut payer la dette — parce que le redo est presque plein,
ou que le pourcentage de pages sales est trop élevé, ou qu’un checkpoint doit avancer — il peut forcer des threads au premier plan à aider au flush.
C’est là que vous obtenez des blocages : transactions en attente sur un flush de journal, un flush de page, ou les deux.
Blague n°1 : NVMe accélère une mauvaise politique de flush, comme donner un espresso à un tout‑petit — les choses vont plus vite, mais pas mieux.
MySQL vs MariaDB : ce qui diffère pour redo/flush/IO capacity
Les deux moteurs parlent InnoDB, mais ils n’embarquent pas toujours le même InnoDB, et ne se comportent pas identiquement. Si vous copiez un guide de tuning
d’un moteur à l’autre, vous pouvez atterrir dans la vallée de l’étrange : « ça démarre, mais c’est hanté. »
Différences de configuration des journaux redo
- MySQL 8.0 : utilise
innodb_redo_log_capacity(le réglage moderne) et gère les fichiers redo en interne. Beaucoup d’anciens guides parlent encore deinnodb_log_file_size; c’est le monde d’avant. - MariaDB : utilise couramment
innodb_log_file_sizeetinnodb_log_files_in_group(selon la version). Elle n’expose pas toujours la même abstraction de capacité redo.
Capacité IO et threads d’arrière-plan
Les sémantiques de innodb_io_capacity et innodb_io_capacity_max sont similaires en esprit mais ne se comportent pas forcément de la même façon sous pression.
Le tuning du page cleaner et les paramètres de flush voisin peuvent aussi différer.
Écritures atomiques et comportement du doublewrite
C’est là que le « tuning NVMe » devient concret. Certaines déploiements comptent sur les garanties du système de fichiers + périphérique (écritures atomiques 16 KB, comportement DAX, ou fonctionnalités du périphérique)
pour réduire le besoin du doublewrite. Beaucoup ne le font pas. Et le NVMe cloud ne se comporte souvent pas comme votre SSD de labo.
Traitez le doublewrite comme requis sauf si vous pouvez prouver que toute la pile survit aux pages déchirées.
Conclusion opérationnelle : choisissez un serveur (MySQL ou MariaDB), puis tankez en utilisant les variables et compteurs d’état de ce serveur.
« Même InnoDB » suffit pour des diagrammes d’architecture, pas pour éviter un outage.
Journaux redo sur NVMe : taille, disposition et dynamique des checkpoints
Les journaux redo sont votre amortisseur. Une plus grande capacité redo permet à InnoDB de tamponner plus de travail sale et de flusher plus en douceur.
Mais « plus grand = mieux » devient « la récupération prend une éternité » si vous exagérez, et cela peut masquer un problème de flush jusqu’à ce que ce soit une surprise à 3 h du matin.
Ce que les journaux redo vous apportent réellement
- Ils découplent (en grande partie) le commit du flush des pages de données. Le commit écrit le redo ; les pages peuvent être flushées plus tard.
- Ils lissent les schémas d’écriture aléatoire en append séquentiel-ish.
- Ils définissent le budget du checkpoint. Si le checkpoint ne peut pas avancer, vous allez vous bloquer.
Réalisme spécifique NVMe : le redo concerne la variance de latence, pas le débit
Beaucoup de périphériques NVMe peuvent atteindre un débit remarquable, mais la variance de latence des fsync sous pression est ce qui vous tue.
Le chemin redo est sensible à :
- au comportement du cache d’écriture du périphérique et aux sémantiques FUA/flush,
- au mode de journalisation du système de fichiers,
- à la congestion du writeback du noyau,
- aux rafales d’écriture en arrière-plan qui se disputent les mêmes files NVMe.
Dimensionnement des redo : conseils opinionnés
Sur les systèmes modernes, des redo sous-dimensionnés sont une blessure auto-infligée. Si votre capacité redo est petite, vous checkpointez constamment,
et les page cleaners vont thrash. Cela transforme « NVMe rapide » en « pourquoi le commit prend parfois 40 ms ».
Faites plutôt ceci :
- Dimensionnez les redo pour que les rafales d’écriture en régime ne heurtent pas la pression « log full ».
- Validez les attentes de temps de récupération. Un redo très large signifie plus de redo à relire lors d’une récupération après crash.
- Surveillez l’âge du checkpoint et le pourcentage de pages sales ; ajustez pour éviter les motifs en dents de scie.
Quand le redo est trop grand
Si vous opérez sur un nœud dont le délai de redémarrage est strictement limité — pensez autoscaling ou SLA de basculement — un redo trop volumineux peut rendre la récupération lente.
Ce n’est pas théorique. C’est la différence entre un basculement qui ressemble à un « hoquet » et un qui nécessite un « pont d’incident ».
Politique de flush : leviers de durabilité et leur coût
Il y a deux sortes de personnes en bases de données : celles qui ont perdu des données, et celles qui ne l’ont pas encore fait. La politique de flush décide dans quel camp vous êtes.
innodb_flush_log_at_trx_commit : le grand levier
Ce paramètre décide quand InnoDB flush le redo vers le stockage durable. Valeurs communes :
- 1 : écrire et fsync le redo à chaque commit. Durabilité maximale ; sensibilité maximale à la latence des fsync.
- 2 : écrire au commit, fsync une fois par seconde. Peut perdre jusqu’à ~1 seconde de transactions en cas de crash/coupure.
- 0 : flush une fois par seconde ; fenêtre de perte potentiellement plus large.
Mon avis : utilisez 1 pour la plupart des systèmes de production importants, et investissez pour rendre les fsync prévisibles.
N’utilisez 2 que si l’entreprise accepte explicitement la fenêtre de perte et que c’est documenté dans le runbook.
sync_binlog : n’oubliez pas la durabilité de la réplication
Si vous utilisez les binlogs (réplication, récupération point-in-time, CDC), sync_binlog interagit aussi avec la durabilité.
Une erreur fréquente est d’avoir le redo durable mais le binlog non durable, puis de se demander pourquoi un crash provoque des incohérences de réplication ou des trous PITR.
Le système de fichiers et les options de montage comptent plus que la plupart des « tunings BD »
Sous Linux, ext4 et XFS se comportent différemment sous fsync. Le mode de journalisation et les barrières ont de l’importance.
Si vous utilisez des volumes cloud, le périphérique bloc peut mentir sur les sémantiques de cache d’une manière qui est « acceptable » jusqu’au jour où ce n’est plus le cas.
C’est pourquoi les SRE apprennent à se méfier d’un graphe qui semble trop propre.
Blague n°2 : Désactiver les fsync pour les performances, c’est comme enlever votre détecteur de fumée parce qu’il fait du bruit.
Capacité d’E/S bien gérée : io_capacity, E/S en arrière-plan et pages sales
innodb_io_capacity n’est pas « la vitesse de votre disque ». C’est un indice pour InnoDB sur l’agressivité à appliquer pour flush en arrière-plan.
Si vous le réglez trop bas, les pages sales s’accumulent et le flushing devient en rafales. Trop haut, et vous pouvez créer une pression d’écriture constante
qui entre en concurrence avec les lectures et augmente la latence.
L’objectif : flushs réguliers, pas flushs héroïques
Le meilleur cas est ennuyeux : les page cleaners flushent en continu à un rythme qui maintient les pages sales dans une bande stable,
l’âge des checkpoints sain, et la consommation redo lisse. Le pire cas est « calme, calme, panique flush », qui ressemble à des falaises périodiques de latence.
Comment choisir des valeurs de départ
- Commencez par une base réaliste. Le NVMe peut faire des dizaines de milliers d’IOPS, mais les schémas de flush d’InnoDB ne sont pas de simples 4k random writes.
- Mesurez les IOPS d’écriture soutenues et la latence sous charge réelle de base de données, pas avec un benchmark sur une machine vide.
- Utilisez
innodb_io_capacity_maxcomme plafond de rafale, pas comme plan quotidien.
La gestion des pages sales est l’indicateur principal
Si les pages sales augmentent régulièrement pendant le trafic normal puis s’effondrent soudainement pendant les stalls, votre flushing en arrière-plan est insuffisant.
Si les pages sales restent basses mais que vous voyez un IO d’écriture constant et une latence de lecture élevée, vous pourriez sur-flusher.
Tuning Linux + NVMe qui compte vraiment (et ce qui est du snake oil)
Il existe tout un genre de « tuning NVMe » qui est essentiellement du culte de la cargaison. La machine est rapide ; le goulot est souvent les sémantiques de flush,
la contention de files, la planification CPU, ou le comportement du système de fichiers. Cela dit, quelques vérifications Linux valent toujours la peine.
Ordonnanceur d’E/S : généralement none pour NVMe
Pour NVMe, la couche bloc multiqueue du noyau fait que les ordonnanceurs traditionnels n’aident souvent pas. Beaucoup de distributions utilisent déjà none.
Confirmez, n’assumez pas.
Writeback et ratios dirty : éviter les tempêtes synchronisées
Le writeback des pages sales du noyau peut se synchroniser avec le flushing d’InnoDB et produire une congestion périodique.
On ne « répare » pas ça par des changements sysctl aléatoires ; mesurez si les pics de writeback coïncident avec des stalls BD,
puis ajustez prudemment.
Fréquence CPU et interruptions : la saboteuse silencieuse
NVMe + forte QD peut être gourmand en CPU. Si votre CPU se met fortement en basse fréquence, ou si les interruptions sont mal réparties,
votre « stockage rapide » devient un radiateur coûteux. Les chemins fsync sensibles à la latence détestent particulièrement le jitter.
TRIM/discard : traitez avec respect
Le discard en ligne peut introduire des pics de latence sur certains périphériques ou stacks. Beaucoup d’opérateurs préfèrent des fstrim périodiques
pendant les fenêtres de maintenance. La qualité du firmware NVMe varie, et vous n’allez pas surpasser une journée de firmware défectueux.
Tâches pratiques (commandes + sorties + la décision)
Ce sont des tâches réelles que vous pouvez exécuter sur un hôte Linux avec MySQL ou MariaDB. Chaque tâche inclut ce que la sortie signifie
et la décision que vous en tirez. Ne les exécutez pas toutes en production en même temps. Choisissez celles qui correspondent à vos symptômes.
Tâche 1 : Confirmer si vous êtes sur MySQL ou MariaDB (et la version)
cr0x@server:~$ mysql --version
mysql Ver 8.0.36 for Linux on x86_64 (MySQL Community Server - GPL)
Ce que ça signifie : Vous êtes sur MySQL 8.0, donc le dimensionnement des redo utilise probablement innodb_redo_log_capacity plutôt que les anciens réglages.
Décision : Utilisez les noms de variables et les compteurs d’état de MySQL 8.0 ; n’appliquez pas des réglages spécifiques à MariaDB.
Tâche 2 : Capturer les paramètres clés de durabilité InnoDB
cr0x@server:~$ mysql -Nse "SHOW VARIABLES WHERE Variable_name IN ('innodb_flush_log_at_trx_commit','sync_binlog','innodb_doublewrite','innodb_flush_method');"
innodb_doublewrite ON
innodb_flush_log_at_trx_commit 1
innodb_flush_method O_DIRECT
sync_binlog 1
Ce que ça signifie : C’est la posture « durable par défaut » (redo et binlog synchrones). O_DIRECT évite le double caching.
Décision : Conservez-le sauf si l’entreprise accepte les fenêtres de perte ; optimisez la prévisibilité des fsync plutôt que de réduire ces réglages.
Tâche 3 : Vérifier les variables de dimensionnement redo (MySQL 8.0)
cr0x@server:~$ mysql -Nse "SHOW VARIABLES LIKE 'innodb_redo_log_capacity';"
innodb_redo_log_capacity 2147483648
Ce que ça signifie : La capacité redo est de 2 GiB. Pour des charges d’écriture intenses, cela peut être faible et augmenter la pression des checkpoints.
Décision : Si vous observez des stalls de checkpoint ou des « log waits », prévoyez une fenêtre de changement pour augmenter la capacité redo et validez l’impact sur le temps de récupération.
Tâche 4 : Vérifier les variables de dimensionnement redo (style MariaDB)
cr0x@server:~$ mysql -Nse "SHOW VARIABLES WHERE Variable_name IN ('innodb_log_file_size','innodb_log_files_in_group');"
innodb_log_file_size 268435456
innodb_log_files_in_group 2
Ce que ça signifie : Le redo total est d’environ 512 MiB. C’est souvent sous-dimensionné sur des OLTP NVMe où les rafales d’écriture sont courantes.
Décision : Envisagez d’augmenter le redo total, mais tenez compte de la procédure opérationnelle (recréation des fichiers au redémarrage dans de nombreuses configurations).
Tâche 5 : Vérifier le pourcentage de pages sales et la pression du buffer pool
cr0x@server:~$ mysql -Nse "SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_pages_%';"
Innodb_buffer_pool_pages_data 1048576
Innodb_buffer_pool_pages_dirty 196608
Innodb_buffer_pool_pages_free 1024
Innodb_buffer_pool_pages_total 1050624
Ce que ça signifie : Les pages sales sont ~18,7% (196608/1050624). Les pages libres sont quasi nulles, donc le buffer pool est pleinement utilisé.
Décision : Si les pages sales augmentent sous charge stable, augmentez le flushing en arrière-plan (innodb_io_capacity) ou corrigez la contention IO. Si elles oscillent violemment, diminuez la rafale (dimensionnement redo, tuning du flush).
Tâche 6 : Vérifier le comportement des checkpoints via le statut moteur InnoDB
cr0x@server:~$ mysql -Nse "SHOW ENGINE INNODB STATUS\G" | sed -n '1,120p'
*************************** 1. row ***************************
Type: InnoDB
Name:
Status:
=====================================
2025-12-31 12:14:51 0x7f3c2c0c4700 INNODB MONITOR OUTPUT
=====================================
Log sequence number 879112345678
Log flushed up to 879112300000
Pages flushed up to 879110900000
Last checkpoint at 879110900000
Ce que ça signifie : LSN avance (écritures), les flushs prennent du retard, et le checkpoint correspond à « Pages flushed up to ». Si Log sequence number est très en avance sur Last checkpoint, vous accumulez de l’âge de checkpoint.
Décision : Si l’âge du checkpoint croît jusqu’à provoquer des stalls, augmentez la capacité redo et/ou relevez la capacité IO ; vérifiez aussi les pics de latence des fsync et le coût du doublewrite.
Tâche 7 : Mesurer les attentes liées aux fsync via Performance Schema (MySQL)
cr0x@server:~$ mysql -Nse "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 event_name LIKE 'wait/io/file/innodb/innodb_log_file%' ORDER BY sum_timer_wait DESC LIMIT 5;"
wait/io/file/innodb/innodb_log_file 1289345 842.11
Ce que ça signifie : Beaucoup de temps est passé à attendre l’I/O des fichiers de redo. C’est souvent votre latence de commit.
Décision : Corrélez avec la latence p99 des commits. Si c’est élevé, concentrez-vous sur le chemin de flush : variance de latence du périphérique, journalisation du système de fichiers, congestion du noyau.
Tâche 8 : Vérifier les compteurs d’attente de log et la pression d’écriture InnoDB
cr0x@server:~$ mysql -Nse "SHOW GLOBAL STATUS WHERE Variable_name IN ('Innodb_log_waits','Innodb_log_write_requests','Innodb_os_log_fsyncs','Innodb_os_log_written');"
Innodb_log_waits 413
Innodb_log_write_requests 98234567
Innodb_os_log_fsyncs 4512390
Innodb_os_log_written 9876543210
Ce que ça signifie : Innodb_log_waits non nul suggère que des transactions ont dû attendre parce que le buffer de log / l’espace redo était contraint.
Décision : Si les log waits augmentent pendant la charge normale, augmentez la capacité redo et/ou corrigez le checkpointing et le débit de flush.
Tâche 9 : Confirmer le modèle de l’appareil NVMe, le firmware et le lien PCIe (sanité hardware)
cr0x@server:~$ sudo nvme list
Node SN Model Namespace Usage Format FW Rev
/dev/nvme0n1 S6X... SAMSUNG MZVL21T0HCLR-00B00 1 900.19 GB / 1.00 TB 512 B + 0 B GXA7401Q
Ce que ça signifie : Vous savez sur quel périphérique vous tournez, y compris le firmware. Le firmware compte pour les pics de latence.
Décision : Si vous observez des stalls périodiques, vérifiez si l’appareil a des problèmes de firmware connus dans votre parc ; envisagez des mises à jour contrôlées du firmware.
Tâche 10 : Vérifier l’ordonnanceur IO pour NVMe
cr0x@server:~$ cat /sys/block/nvme0n1/queue/scheduler
[none] mq-deadline kyber bfq
Ce que ça signifie : L’ordonnanceur est none, ce qui est souvent correct pour NVMe.
Décision : Laissez-le sauf si vous avez des preuves solides qu’un autre ordonnanceur améliore la latence tail sous concurrence lecture/écriture mixte.
Tâche 11 : Vérifier le système de fichiers et les options de montage (les sémantiques de flush vivent ici)
cr0x@server:~$ findmnt -no SOURCE,FSTYPE,OPTIONS /var/lib/mysql
/dev/nvme0n1p2 ext4 rw,relatime,errors=remount-ro,data=ordered
Ce que ça signifie : ext4 avec data=ordered. Cela affecte le comportement du journal et peut influencer le coût des fsync.
Décision : Si la latence des fsync est volatile, testez des systèmes de fichiers ou des options de montage alternatives en staging ; ne changez pas les options de montage en production à la légère.
Tâche 12 : Vérifier les paramètres writeback du noyau
cr0x@server:~$ sysctl vm.dirty_background_ratio vm.dirty_ratio vm.dirty_expire_centisecs vm.dirty_writeback_centisecs
vm.dirty_background_ratio = 10
vm.dirty_ratio = 20
vm.dirty_expire_centisecs = 3000
vm.dirty_writeback_centisecs = 500
Ce que ça signifie : Le noyau commencera le writeback de fond autour de 10% dirty, et throttlera autour de 20% dirty. Ces valeurs par défaut peuvent convenir, ou se synchroniser mal avec le flushing InnoDB.
Décision : Si vous observez une congestion IO globale périodique, envisagez d’abaisser ces ratios pour encourager le writeback plus tôt, mais validez l’impact avec la charge réelle.
Tâche 13 : Observer la latence IO et le chaînage en temps réel
cr0x@server:~$ iostat -x 1 5
Linux 6.1.0 (server) 12/31/2025 _x86_64_ (32 CPU)
avg-cpu: %user %nice %system %iowait %steal %idle
6.12 0.00 2.11 3.95 0.00 87.82
Device r/s rkB/s rrqm/s %rrqm r_await rareq-sz w/s wkB/s wrqm/s %wrqm w_await wareq-sz aqu-sz %util
nvme0n1 820.0 52500.0 0.0 0.0 1.20 64.0 1600.0 78000.0 0.0 0.0 8.70 48.8 15.2 92.0
Ce que ça signifie : Les écritures ont un await ~8.7 ms avec une forte util. Ce n’est pas « NVMe rapide » au sens où vos commits l’exigent.
Décision : Si la latence de commit se corrèle avec w_await, réduisez les rafales d’écriture (dimensionnement redo, tuning IO capacity) et investiguez la saturation de l’appareil ou des voisins bruyants.
Tâche 14 : Identifier les principaux consommateurs IO MySQL côté OS
cr0x@server:~$ sudo pidstat -d 1 3 -p $(pidof mysqld)
Linux 6.1.0 (server) 12/31/2025 _x86_64_ (32 CPU)
12:16:03 UID PID kB_rd/s kB_wr/s kB_ccwr/s iodelay Command
12:16:04 999 24187 12000.00 54000.00 0.00 52 mysqld
12:16:05 999 24187 11500.00 61000.00 0.00 61 mysqld
Ce que ça signifie : MySQL est le générateur d’écritures. Bien. Si un autre processus écrit beaucoup, il peut voler l’I/O et détruire la latence tail.
Décision : Si conflit IO, isolez MySQL (volume dédié, cgroups/io.max, appareil séparé, ou tuez le voisin bruyant).
Tâche 15 : Vérifier les réglages InnoDB IO capacity en direct
cr0x@server:~$ mysql -Nse "SHOW VARIABLES WHERE Variable_name IN ('innodb_io_capacity','innodb_io_capacity_max','innodb_page_cleaners');"
innodb_io_capacity 200
innodb_io_capacity_max 2000
innodb_page_cleaners 4
Ce que ça signifie : innodb_io_capacity=200 est une valeur de l’ère des disques rotatifs. Sur NVMe, c’est souvent trop bas, menant à l’accumulation de pages sales et à des stalls éventuels.
Décision : Augmentez progressivement (par exemple 1000–5000 selon la charge et le périphérique), surveillez les pages sales, l’âge des checkpoints et la latence de lecture. Ne passez pas directement à 20000 parce que vous l’avez vu sur un forum.
Tâche 16 : Confirmer doublewrite et réglages liés aux écritures atomiques (gestion du risque)
cr0x@server:~$ mysql -Nse "SHOW VARIABLES LIKE 'innodb_doublewrite';"
innodb_doublewrite ON
Ce que ça signifie : Le doublewrite est activé, ce qui protège contre les pages déchirées.
Décision : Laissez-le activé sauf si vous avez une pile atomique validée et un plan de crash/récupération testé qui prouve qu’il est sûr de le changer.
Mode d’emploi de diagnostic rapide (premiers/seconds/troisièmes contrôles)
Quand la latence saute et que tout le monde fixe le même tableau de bord, vous avez besoin d’un chemin court vers « qu’est‑ce que c’est, vraiment ? »
Voici la séquence qui trouve rapidement le goulot dans la plupart des incidents MySQL/MariaDB sur NVMe.
Premier : est-ce la latence fsync du redo ou la pression de flush des données ?
- Vérifiez la latence de commit et les log waits :
Innodb_log_waits, attentes redo dans Performance Schema, et le timing de commit côté application. - Vérifiez
iostat -xpour des pics dew_awaitet une forte %util.
Si la latence fsync redo est lente : concentrez-vous sur la variance de latence du périphérique, la journalisation du système de fichiers, le cache d’écriture, et la contention IO. Le dimensionnement redo peut aider indirectement en lissant les checkpoints, mais il ne réparera pas un chemin fsync mauvais.
Second : les page cleaners prennent-ils du retard ?
- Surveillez l’évolution du pourcentage de pages sales.
- Vérifiez le statut InnoDB pour la croissance de l’âge du checkpoint et l’activité de flushing.
- Confirmez que
innodb_io_capacityn’est pas réglé comme en 2009.
Si les cleaners sont à la traîne : augmentez l’IO capacity progressivement, envisagez d’augmenter la capacité redo, et cherchez une amplification d’écriture due au doublewrite + système de fichiers.
Troisième : la latence de lecture est-elle causée par la contention d’écriture ?
- Si les lectures ralentissent pendant les tempêtes d’écriture, vous avez une contention de file IO.
- Vérifiez si le writeback en arrière-plan du noyau coïncide avec les stalls BD (paramètres dirty writeback du noyau).
Si la latence de lecture suit les rafales d’écriture : réduisez la rafale du flush (redo / IO capacity), isolez l’I/O, et vérifiez l’ordonnanceur et le comportement CPU.
Quatrième (quand c’est « bizarre ») : prouvez si le NVMe est vraiment du NVMe
- Sur le cloud, confirmez le comportement réel de la classe de stockage via la variance de latence observée, pas le nom de périphérique.
- Vérifiez la présence d’autres écrivains et d’éventuelles restrictions côté hyperviseur ou couche volume.
Trois mini-récits d’entreprise venus des tranchées
1) L’incident causé par une mauvaise hypothèse : « NVMe signifie que fsync est bon marché »
Une société un peu fintech a migré un cluster MySQL central de SSD SATA vers du NVMe local sur de nouveaux hôtes. L’équipe s’attendait au classique :
latence plus basse, débit plus élevé, moins d’incidents liés à l’I/O. Ils ont eu les deux premiers en médiane, et le dernier s’est empiré.
Le symptôme était classique : p95 semblait correct, p99.9 plongait périodiquement. Les threads applicatifs s’empilaient en attente de commits.
Le on‑call regarda le CPU et vit beaucoup d’idle. Le débit stockage n’atteignait pas les limites du périphérique. « Donc ça ne peut pas être le disque », dit quelqu’un,
et la salle acquiesça parce que les graphiques sont persuasifs.
La mauvaise hypothèse était que NVMe rend automatiquement les fsync prévisibles. En réalité, leur système de fichiers + options de montage
et le housekeeping interne du firmware du périphérique ont créé des stalls fsync périodiques sous pression d’écriture soutenue.
Les commits étaient bloqués par ces stalls parce qu’ils utilisaient innodb_flush_log_at_trx_commit=1 (correct) et n’avaient pas de marge lorsque le jitter fsync apparaissait.
La solution n’a pas été « diminuer la durabilité ». Ils ont mesuré les événements d’attente redo, les ont corrélés aux pics iostat,
ont testé une configuration de système de fichiers différente en staging, et ont ajouté une marge en augmentant la capacité redo pour réduire les rafales de checkpoint.
La latence tail s’est stabilisée. La surprise fut que le stockage « le plus rapide » produisait la latence la plus volatile tant que la pile n’était pas réglée pour la prévisibilité.
2) L’optimisation qui s’est retournée contre eux : pousser innodb_io_capacity à la lune
Une autre entreprise avait un cluster MariaDB servant un service à forte écriture. Ils voyaient les pages sales grimper pendant les pics et ont décidé que le flushing était le goulot.
Quelqu’un a mis innodb_io_capacity à une valeur qui semblait raisonnable comparée à un benchmark NVMe synthétique, et ils l’ont poussée en fenêtre de maintenance.
Pendant environ une heure, les choses se sont améliorées. Les pages sales restaient basses et les tableaux sont devenus verts. Puis la latence de lecture a commencé à monter,
pas seulement pour les requêtes lourdes mais pour de simples recherches ponctuelles. Le hit rate du buffer pool a légèrement chuté. L’application a commencé à retenter à cause de timeouts.
Tout le monde a blâmé « le réseau » parce que c’est ce que font les gens quand le stockage est censé être résolu.
La réalité : ils ont forcé les page cleaners à flusher agressivement tout le temps, créant une pression d’écriture constante et saturant les files du périphérique.
Les lectures devaient maintenant concurrencer un flot d’écritures d’arrière-plan. Le débit NVMe était élevé, mais la latence tail pour les lectures s’est dégradée. C’était un problème d’ordonnancement IO qu’ils avaient créé.
Ils ont fait un rollback vers une capacité IO inférieure, puis retuné progressivement en surveillant la latence de lecture et les pages sales conjointement.
Le bon schéma a été un taux de flush modéré de base avec un max sensible, plus un gonflement de capacité redo pour éviter la panique du checkpoint.
La leçon : « plus de flushing » n’est pas synonyme de « meilleur flushing ».
3) La pratique ennuyeuse mais correcte qui a sauvé la mise : valider le temps de récupération après crash
Un fournisseur SaaS utilisait MySQL avec une capacité redo relativement large parce que sa charge avait des écritures en rafales. Cela rendait le système lisse sous charge.
Mais ils ont fait quelque chose d’impopulaire : ils testaient régulièrement le temps de récupération après crash en staging avec un volume de données et une utilisation redo proches de la production.
Lors d’un événement datacenter, un primaire a crashé brutalement et redémarré. Le plan de basculement supposait un certain budget de temps de redémarrage.
Parce qu’ils l’avaient testé, ils savaient exactement à quoi s’attendre et avaient déjà ajusté la capacité redo pour rester dans cette fenêtre de récupération.
La réplication a rattrapé sans accroc parce que les réglages de durabilité du binlog correspondaient à la posture de durabilité.
Pendant que d’autres équipes discutaient sur le chat pour savoir s’il fallait reconstruire depuis la sauvegarde ou promouvoir un réplica, cette équipe a suivi le runbook :
confirmer la progression de la récupération redo, surveiller les étapes de la récupération après crash, et garder le trafic drainé jusqu’à ce que le moteur signale un état cohérent.
Le résultat n’était pas glamour. C’était un incident contenu sans perte de données et sans restauration de plusieurs heures.
La « pratique ennuyeuse » était de mesurer la récupération à l’avance et de refuser de tuner la taille du redo sans considérer le temps de redémarrage opérationnel.
Erreurs courantes : symptôme → cause racine → correctif
1) Symptom : pics de latence de commit p99, le débit semble correct
Cause racine : variance de latence des fsync du redo (firmware du périphérique, journalisation du système de fichiers, congestion du writeback, ou contention IO).
Correctif : mesurez les événements d’attente redo ; vérifiez iostat -x pour des pics de write await ; isolez l’I/O ; validez les options du système de fichiers ; évitez des écrivains concurrents sur le même volume.
2) Symptom : « tempêtes de stall » périodiques toutes les quelques minutes
Cause racine : pression de checkpoint et flushing en rafales due à un redo sous-dimensionné et/ou un innodb_io_capacity trop faible.
Correctif : augmentez la capacité redo ; relevez innodb_io_capacity progressivement ; surveillez les pages sales et l’âge des checkpoints pour la stabilité plutôt que des motifs en dents de scie.
3) Symptom : les lectures ralentissent quand les écritures augmentent, même si le NVMe n’est pas saturé en débit
Cause racine : contention de file IO due au flushing en arrière-plan ou à l’amplification doublewrite ; les lectures restent bloquées derrière les écritures.
Correctif : ajustez innodb_io_capacity et innodb_io_capacity_max ; assurez-vous que l’ordonnanceur est approprié ; envisagez de séparer redo/binlog sur d’autres périphériques seulement si vous pouvez gérer la complexité opérationnelle.
4) Symptom : « log waits » en hausse pendant la charge normale
Cause racine : espace redo contraint ; le checkpoint n’avance pas assez vite ; pression du buffer de log ; parfois capacité redo trop faible.
Correctif : augmentez la capacité redo ; assurez-vous que les page cleaners peuvent flusher régulièrement ; vérifiez que le doublewrite et le système de fichiers n’amplifient pas inutilement les écritures.
5) Symptom : après « l’avoir accéléré » en changeant la durabilité, la réplication/PITR devient instable
Cause racine : posture de durabilité incohérente entre redo et binlog (par ex. innodb_flush_log_at_trx_commit=2 mais sync_binlog=0), ou hypothèses erronées sur la cohérence après crash.
Correctif : alignez la durabilité redo et binlog sur la tolérance de perte de l’entreprise ; documentez ; testez les scénarios de crash.
6) Symptom : NVMe affiche une forte %util, mais MySQL n’exécute pas tant de requêtes
Cause racine : flushing en arrière-plan, doublewrite, ou writeback du système de fichiers ; ou un autre processus qui écrit beaucoup.
Correctif : utilisez pidstat -d pour trouver les écrivains ; vérifiez les pages sales InnoDB ; revoyez les paramètres dirty writeback du noyau ; envisagez de déplacer les charges non BD hors du volume.
7) Symptom : le tuning fonctionne en staging mais échoue en production
Cause racine : modèle/firmware NVMe différent, comportement de stockage cloud différent, concurrence différente, ou tâches d’arrière-plan différentes (sauvegardes, compaction, ETL).
Correctif : standardisez le hardware ; testez sous concurrence réaliste ; planifiez les jobs d’arrière-plan hors des pics ; mesurez la latence tail, pas la moyenne.
Listes de contrôle / plan pas à pas
Pas à pas : établir une base avant tout tuning
- Enregistrez la version MySQL/MariaDB et les variables clés : politique de flush, dimensionnement redo, IO capacity, doublewrite, durabilité du binlog.
- Capturez 10 minutes de
iostat -xpendant une charge représentative. - Capturez un snapshot du statut InnoDB au début et à la fin de cette fenêtre (mouvement du checkpoint et des LSN).
- Capturez les tendances du pourcentage de pages sales.
- Confirmez le système de fichiers et les options de montage du datadir.
- Confirmez qu’il n’y a pas d’écrivains lourds concurrents sur le même périphérique.
Pas à pas : stabiliser redo et checkpoints
- Si la capacité redo est petite, augmentez-la pour réduire le churn des checkpoints (maintenance planifiée si nécessaire).
- Après le changement, surveillez le temps de récupération lors d’un redémarrage contrôlé en staging ; ne déployez pas une augmentation des redo sans connaître le coût de redémarrage.
- Surveillez
Innodb_log_waitset l’âge des checkpoints ; ils doivent diminuer.
Pas à pas : tuner la IO capacity sans casser les lectures
- Augmentez
innodb_io_capacitypar petites étapes. - Après chaque étape, surveillez : pages sales, latence de lecture, write await, et CPU.
- Réglez
innodb_io_capacity_maxpour permettre des rafales, mais gardez un plafond. - Arrêtez-vous lorsque les pages sales se stabilisent et que le p99 des lectures ne se dégrade pas.
Pas à pas : décider des leviers de durabilité comme un adulte
- Notez la fenêtre de perte acceptable (0 seconde ? 1 seconde ? plus ?). Faites signer par la personne qui assume les conséquences.
- Alignez
innodb_flush_log_at_trx_commitetsync_binlogselon cette décision. - Testez le comportement de crash et la récupération en staging : arrêt par perte d’alimentation, puis récupération, puis vérifications de cohérence.
FAQ
1) Dois‑je toujours définir innodb_flush_log_at_trx_commit=2 sur NVMe ?
Non. NVMe peut rendre les fsync rapides, mais « rapide » n’est pas synonyme de « prévisible ». N’utilisez 2 que si l’entreprise accepte de perdre jusqu’à ~1 seconde de commits en cas de crash.
2) Quelle taille de redo est recommandée sur NVMe ?
Assez grande pour éviter une pression constante sur les checkpoints, assez petite pour garder la récupération dans votre budget opérationnel. Commencez par mesurer l’âge des checkpoints et le temps de récupération ; ne choisissez pas un chiffre d’un blog.
3) Augmenter la capacité redo améliore-t‑elle toujours les performances ?
Souvent cela réduit les stalls en lissant les checkpoints, mais cela peut allonger le temps de récupération après crash. De plus, ça ne réparera pas un chemin fsync fondamentalement mauvais.
4) Puis‑je désactiver le buffer doublewrite sur NVMe ?
Seulement si vous pouvez prouver que toute votre pile empêche les pages déchirées (périphérique + système de fichiers + configuration) et que vous avez testé la récupération après crash. Sinon, laissez‑le activé et adaptez le tuning autour.
5) Pourquoi innodb_io_capacity vaut encore 200 dans tant de configurations ?
Parce que les configurations survivent aux générations de matériel. 200 avait du sens pour les disques rotatifs. Sur NVMe, ça peut générer des stalls.
6) Mon NVMe affiche 90% util mais faible débit. Est‑ce normal ?
Oui, si vous êtes dominé par de petites écritures synchrones (fsync) ou de l’I/O lié à la latence. Une forte util peut refléter l’attente en file, pas seulement l’utilisation de bande passante.
7) Séparer les journaux redo sur un autre NVMe en vaut‑il la peine ?
Parfois, surtout si les écritures de flush des données privent les fsync redo. Mais cela ajoute de la complexité opérationnelle et peut échouer de nouvelles façons (planification de capacité, domaines de panne). Mesurez d’abord.
8) MySQL vs MariaDB : lequel est « meilleur » sur NVMe ?
Aucun ne gagne par défaut. Choisissez selon les fonctionnalités, l’outillage opérationnel et la compétence de votre équipe. Puis tunez avec les variables de ce moteur et mesurez les résultats.
9) Pourquoi le tuning en staging ne correspond‑il pas à la production ?
Concurrence, jobs d’arrière-plan, voisins bruyants, et firmware de périphérique différents. La latence tail est un sport réservé à la production, sauf si votre staging est vraiment proche de la production.
Conclusion : prochaines étapes réalisables cette semaine
Si vous voulez que le NVMe donne l’impression de magie en production, ne poursuivez pas les IOPS maximum. Poursuivez la prévisibilité de la latence des fsync et un flushing régulier.
La taille du redo est votre amortisseur. La politique de flush est votre contrat de risque. La capacité d’E/S est la manière d’empêcher le moteur de payer ses dettes en panique.
- Exécutez les tâches de base : capturez les réglages de durabilité, le dimensionnement redo, les pages sales, iostat, options du système de fichiers.
- Faites la séquence de diagnostic rapide pendant un vrai pic et étiquetez-le : fsync redo, pression de checkpoint, ou contention IO.
- Si le redo est petit et le checkpointing en rafales, planifiez une augmentation de la capacité redo avec un test de temps de récupération.
- Augmentez
innodb_io_capacityprogressivement jusqu’à ce que les pages sales se stabilisent sans détériorer le p99 des lectures. - Consignez les décisions de durabilité (
innodb_flush_log_at_trx_commit,sync_binlog) et arrêtez de les traiter comme des « knobs de performance ».
Faites cela, et votre NVMe ne sera pas seulement rapide. Il sera ennuyeux. Et l’ennui est ce que vous voulez à 2 h du matin.