ZFS pour MySQL : éviter les effondrements de latence lors des rafales d’écriture

Cet article vous a aidé ?

Tout va bien. Le QPS est stable. La latence est sans histoire. Puis arrive un déploiement, une file qui se vide ou un job par lot « juste pour aujourd’hui » — et votre p99 MySQL devient un scénario cauchemardesque. Les graphiques ne font pas un pic poli ; ils plongent en falaise, puis restent à plat sur « timeout ».

Si vous exécutez MySQL sur ZFS, vous pouvez tout à fait obtenir d’excellentes performances et une grande sécurité opérationnelle. Vous pouvez aussi construire la machine parfaite pour transformer de courtes rafales d’écritures en effondrements de latence prolongés. ZFS est franc : il fera exactement ce que vous avez demandé, pas ce que vous aviez voulu.

Le modèle mental : où MySQL rencontre ZFS et ça chauffe

Commencez par la vérité inconfortable : MySQL se préoccupe plus de la cohérence de la latence que du débit maximal. Votre application ne déclenche pas d’alerte parce que vous avez perdu 10 % de débit. Elle vous alerte parce que le p99 est passé de 5 ms à 800 ms et que des connexions commencent à expirer.

ZFS n’est pas une mince couche au-dessus des disques. C’est un système de fichiers transactionnel copy-on-write avec son propre cache (ARC), un rassemblement d’écritures, des sommes de contrôle et un mécanisme de journal d’intention séparé (ZIL) pour les sémantiques synchrones. Il est brillant pour rendre les écritures sûres et vérifiables. Il est aussi capable de transformer un flux régulier de petites écritures synchrones en un problème d’attente — surtout quand le pool est occupé, fragmenté ou mal dimensionné pour la latence.

D’où viennent les écritures dans MySQL

InnoDB réalise plusieurs types d’écritures. Sous des charges rafaleuses, celles qui font le plus mal sont :

  • Écritures du redo log (séquentielles, souvent synchronisées fréquemment selon innodb_flush_log_at_trx_commit).
  • Écritures du doublewrite buffer (amplification d’écriture par conception, pour la sécurité au crash).
  • Flushs de pages de données (en arrière-plan, mais des rafales surviennent quand la pression de pages sales augmente).
  • Écritures de binlog (peuvent être synchronisées, surtout en réplication/GTID).
  • Tables temporaires / fichiers de tri (si mal configurés, ils peuvent chambouler le stockage lors des pics).

Où ZFS place ces écritures

ZFS écrit dans des groupes de transactions (TXGs). Les données sont accumulées en mémoire, puis engagées périodiquement (généralement toutes les quelques secondes). Ce lissage est excellent… jusqu’à ce qu’il ne le soit plus — parce que lorsqu’un commit arrive, ZFS doit pousser beaucoup de données et de métadonnées sales, et il peut devoir le faire pendant que votre base exige aussi des garanties de durabilité synchrones.

Synchrones est le mot clé. Quand MySQL exécute un fsync() ou ouvre des fichiers avec O_SYNC, il exige que les données soient sur un stockage stable avant de poursuivre. Sur ZFS, les écritures synchrones passent par le ZIL (ZFS Intent Log). Si vous ajoutez un périphérique de log séparé (un SLOG), vous changez l’endroit où ces écritures de log synchrones atterrissent.

Le ZIL n’est pas un « cache d’écriture » pour tout le pool. C’est un mécanisme pour satisfaire en toute sécurité les sémantiques sync. La plupart du temps, les entrées ZIL ne sont rejouées qu’après un crash ; sinon elles sont « consumées » lorsque le prochain TXG commit. Pour la latence MySQL, le chemin ZIL est l’endroit où des secondes redeviennent millisecondes — ou des millisecondes deviennent des secondes.

Conseil franc : si vous exécutez MySQL avec des paramètres de durabilité réels (et vous devriez), vous vous engagez à des écritures synchrones. Traitez ZIL/SLOG et la latence comme des citoyens de première classe, pas comme des idées de second plan.

Idée paraphrasée (attribuée) : Werner Vogels a poussé l’idée que vous devriez concevoir pour la défaillance comme condition normale, pas comme exception.

Faits et contexte historique qui expliquent les modes de défaillance actuels

  • ZFS est né chez Sun comme un système de fichiers axé sur l’intégrité des données de bout en bout : sommes de contrôle partout, auto-réparation avec redondance. Cet ADN « intégrité d’abord » influence encore aujourd’hui les compromis de performances.
  • Le copy-on-write n’est pas optionnel dans ZFS. Les réécritures deviennent allocation de nouveau et mise à jour des métadonnées. Super pour les snapshots ; potentiellement rude pour des bases à écritures aléatoires quand l’espace est limité.
  • Le ZIL existe parce que POSIX exige des sémantiques sync. Ce n’est pas une fonctionnalité spéciale pour les bases de données ; c’est le tuyau pour le comportement correct sous fsync().
  • SLOG est un périphérique, pas un mode. Les gens parlent d’« activer SLOG » ; en réalité vous ajoutez un vdev de log séparé pour stocker les enregistrements ZIL plus rapidement et de manière plus prévisible.
  • ARC (Adaptive Replacement Cache) a été conçu pour surpasser le LRU classique en équilibrant récence et fréquence. Il peut rendre les lectures magiques — jusqu’à ce qu’il vole trop de mémoire au buffer pool InnoDB et au noyau.
  • L2ARC est arrivé plus tard pour étendre l’ARC vers des périphériques rapides. Il aide les lectures, mais il coûte aussi de la mémoire et de la bande passante d’écriture pour se maintenir, ce qui n’est pas gratuit lors des rafales d’écriture.
  • Le doublewrite buffer de MySQL est une réponse aux écritures partielles de pages en cas de crash. Sur des systèmes de fichiers avec garanties d’atomicité au niveau page, il est redondant ; sur la plupart des systèmes, il protège. Sur ZFS, il aide souvent encore la sécurité opérationnelle, mais c’est des E/S en plus.
  • Les propriétés de dataset ont évolué comme garde-fous parce que les administrateurs se tiraient souvent une balle dans le pied. Des réglages comme atime=off, compression et recordsize existent parce que le comportement par défaut du système de fichiers n’est pas « orienté base de données ».
  • Les SSD modernes ont ajouté leurs propres surprises de latence : exhaustion du cache SLC, garbage collection du firmware et amplification d’écriture variable. Votre SLOG « rapide » peut devenir une citrouille sous des écritures synchrones soutenues.

Comment les rafales d’écriture deviennent des effondrements de latence sur ZFS

1) Amplification des écritures synchrones : les tempêtes fsync rencontrent des IOPS limités

Quand MySQL est configuré pour la durabilité — par exemple innodb_flush_log_at_trx_commit=1 et binlog sync activé — chaque commit peut nécessiter un fsync(). Le group commit aide, mais sous un trafic rafaleux vous pouvez toujours voir une horde de requêtes sync.

Si ZFS doit placer ces écritures synchrones sur le pool principal, votre latence devient la latence du pool. Et le pool est aussi occupé à effectuer des commits TXG, des mises à jour de métadonnées et éventuellement un resilver/scrub. C’est ainsi que vous obtenez un système où le débit semble « correct » mais chaque transaction attend pour un stockage durable.

2) Pression des commits TXG : des écritures « lisses » deviennent des douleurs périodiques

ZFS accumule des données sales en mémoire et les vide dans des TXG. Sous des rafales d’écriture, vous pouvez atteindre les limites de données sales, et ZFS commencera à restreindre les écritures entrantes pour éviter une utilisation mémoire incontrôlée. C’est un comportement correct. C’est aussi le moment où vos threads de base de données cessent de faire un travail utile et commencent à attendre le stockage.

Quand un flush TXG est volumineux, il rivalise avec les IO synchrones. Même avec un SLOG, le pool doit toujours faire le vrai travail d’écriture des données et des métadonnées. Le SLOG vous aide à accuser réception rapidement des écritures sync, mais vous pouvez encore fondre plus tard si le pool ne suit pas et que le ZIL commence à se remplir en attendant des commits.

3) Espace et fragmentation : le multiplicateur silencieux de latence

ZFS veut de l’espace libre. Pas « un peu d’espace libre ». De l’espace libre réel. Au fur et à mesure que les pools se remplissent, l’allocation devient plus difficile. Les blocs deviennent plus fragmentés. Les mises à jour de métadonnées deviennent plus dispersées. Chaque écriture ressemble de plus en plus à un problème d’E/S aléatoire.

Les bases de données sont excellentes pour transformer l’espace libre en « pas libre ». Si votre pool oscille autour de 80–90% d’utilisation, vous êtes en train de tester à la volée les algorithmes d’allocation de ZFS pendant les pics. Ne faites pas ça en production sauf si vous aimez entendre votre téléphone sonner à 3 h du matin.

4) recordsize inadapté et trop de travail sur les métadonnées

Les pages InnoDB font couramment 16K. Le recordsize par défaut de ZFS est 128K pour les charges générales. Si vous le laissez à 128K pour un dataset contenant des tablespaces InnoDB, vous pouvez augmenter l’amplification d’écriture : changer 16K peut nécessiter la réécriture de blocs plus grands selon les schémas d’accès et la compression.

Ce n’est pas toujours catastrophique — ZFS a un peu d’intelligence, et les E/S séquentielles peuvent encore bien performer — mais sous des mises à jour aléatoires et des rafales d’écriture, un mauvais recordsize force votre pool à travailler plus pour le même travail de base de données.

5) Le mythe du SLOG : « ajoutez un SSD et toute la latence sync disparaît »

Un SLOG est utile uniquement selon sa latence d’écriture durable. Les SSD grand public peuvent être rapides jusqu’à une certaine limite d’écriture. Ils peuvent aussi mentir sur le comportement du flush. Pour un SLOG, vous voulez une latence prévisible sous écritures soutenues et une protection contre la perte de courant (ou des garanties équivalentes d’entreprise).

Blague #1 : Acheter un SSD bon marché pour SLOG, c’est comme engager un stagiaire pour tenir les fondations du bâtiment — enthousiaste, mais le département de physique aura des questions.

6) ARC vs buffer pool InnoDB : la mémoire est un champ de bataille partagé

ARC est agressif et efficace. Le buffer pool InnoDB est aussi agressif et efficace. Si vous laissez les deux se battre pour la RAM, le noyau finira par choisir un gagnant, et ce ne sera pas votre disponibilité. Vous verrez du swapping, des tempêtes de reclaim et une amplification IO tandis que les pages mises en cache tournent.

Pour MySQL, il est généralement préférable de dimensionner le buffer pool InnoDB volontairement et de limiter ARC pour que ZFS n’avale pas le reste. ZFS peut survivre avec un ARC plus petit ; MySQL souffrant de lectures aléatoires, non.

Playbook de diagnostic rapide (premier/deuxième/troisième)

Ceci est le flux « vous avez cinq minutes avant que la direction ne rejoigne le canal d’incident ». Ce n’est pas élégant. C’est efficace.

Premier : confirmer que le symptôme est la latence du stockage, pas le CPU ou des verrous

  1. Vérifier MySQL pour des attentes de verrou et la pression de flush : si des threads sont bloqués sur des mutex/verrous, l’ajustement du stockage n’aidera pas.
  2. Vérifier la charge OS et l’attente IO : un %wa élevé et une charge en hausse avec un faible usage CPU indiquent un embouteillage IO.
  3. Vérifier la latence IO du pool ZFS et la profondeur de file : identifiez si le pool ou le SLOG est le point d’étranglement.

Deuxième : décider si les écritures synchrones sont le goulot

  1. Recherchez des taux élevés d’écritures synchrones (redo/binlog) et le comportement de fsync().
  2. Vérifiez si vous avez un SLOG et s’il est saturé ou lent.
  3. Confirmez le réglage sync du dataset (devrait généralement être standard pour la durabilité ; ne « réparez » pas un incident en trompant le système).

Troisième : vérifier la santé du pool et les facteurs de « lente combustion »

  1. Remplissage du pool : si vous êtes au-dessus d’environ 80% utilisé, vous avez trouvé un contributeur.
  2. Fragmentation : une fragmentation élevée corrèle avec des douleurs d’écritures aléatoires.
  3. Scrub/resilver : si en cours, cela peut transformer un pic gérable en un effondrement.
  4. Throttling des données sales : ZFS peut vous ralentir volontairement pour rester en vie.

Biais décisionnel : lors d’un incident lié à une rafale d’écriture, les trois coupables principaux sont (1) le chemin d’écriture sync, (2) la saturation/latence du pool, (3) la pression mémoire provoquant le churn des caches. Ne commencez pas par changer le recordsize en pleine panne, sauf si vous aimez jouer aux dés avec vos données.

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

Voici des actions opérationnelles réelles : lancez une commande, interprétez la sortie, décidez quoi faire ensuite. Supposez un hôte Linux avec OpenZFS, pool tank, dataset tank/mysql et MySQL dans /var/lib/mysql.

Tâche 1 : Voir si le pool est manifestement surchargé maintenant

cr0x@server:~$ zpool iostat -v tank 1 5
              capacity     operations     bandwidth
pool        alloc   free   read  write   read  write
----------  -----  -----  -----  -----  -----  -----
tank        6.20T  1.10T    120   3800   18M   420M
  raidz2    6.20T  1.10T    120   3700   18M   410M
    sda         -      -     15    520  2.2M    58M
    sdb         -      -     14    515  2.1M    57M
    sdc         -      -     15    540  2.2M    60M
    sdd         -      -     15    530  2.2M    59M
logs            -      -      -     100     -    12M
  nvme0n1       -      -      -     100     -    12M

Que cela signifie : les écritures sont lourdes (3800 ops/s). La bande passante du pool est de 420 MB/s avec relativement peu de lectures. Si la latence est élevée, il vous faut la commande suivante : la latence IO, pas seulement les ops.

Décision : si les écritures explosent et que le pool est presque plein, prévoyez de réduire la charge d’écriture (limiter les jobs par lot) et investiguez immédiatement le sync et le throttling des données sales.

Tâche 2 : Vérifier la latence par vdev pour trouver le goulot

cr0x@server:~$ zpool iostat -v -l tank 1 5
                               operations         bandwidth            total_wait         disk_wait
pool                         read  write        read  write        read  write        read  write
--------------------------  -----  -----  ---------  ---------  ---------  ---------  ---------  ---------
tank                           90   4200        12M       480M        3ms     120ms        1ms     110ms
  raidz2                       90   4100        12M       470M        3ms     125ms        1ms     115ms
    sda                         9    580       1.2M        68M        2ms     140ms        1ms     130ms
    sdb                         9    570       1.2M        67M        2ms     138ms        1ms     128ms
    sdc                         9    600       1.3M        69M        2ms     142ms        1ms     132ms
    sdd                         9    590       1.3M        69M        2ms     141ms        1ms     131ms
logs                            0    120         0        14M        0ms       2ms        0ms       1ms
  nvme0n1                       0    120         0        14M        0ms       2ms        0ms       1ms

Que cela signifie : le total_wait en écriture du pool est ~120 ms. Le SLOG va bien (2 ms). Le goulot est le travail de flush/commit du pool principal, pas le périphérique de log.

Décision : concentrez-vous sur la capacité d’écriture du pool, la fragmentation, l’espace libre et le throttling des données sales. Un SLOG plus rapide ne corrigera pas ce blocage précis.

Tâche 3 : Vérifier le remplissage du pool et la fragmentation (prédit des ennuis)

cr0x@server:~$ zpool list -o name,size,alloc,free,capacity,frag,health tank
NAME  SIZE  ALLOC  FREE  CAPACITY  FRAG  HEALTH
tank  7.28T  6.20T  1.08T      85%   62%  ONLINE

Que cela signifie : 85% de capacité et 62% de fragmentation. C’est une taxe de latence, surtout pour des écritures aléatoires et des charges lourdes en métadonnées.

Décision : priorisez le soulagement de capacité (ajouter des vdevs ou migrer des données). Considérez l’« espace libre » comme une fonctionnalité de performance, pas un détail administratif.

Tâche 4 : Vérifier si un scrub ou resilver est en cours

cr0x@server:~$ zpool status tank
  pool: tank
 state: ONLINE
  scan: scrub in progress since Mon Dec 23 02:11:44 2025
        3.12T scanned at 1.2G/s, 1.55T issued at 600M/s, 6.20T total
        0B repaired, 25.00% done, 02:30:12 to go
config:

        NAME        STATE     READ WRITE CKSUM
        tank        ONLINE       0     0     0
          raidz2-0  ONLINE       0     0     0
            sda     ONLINE       0     0     0
            sdb     ONLINE       0     0     0
            sdc     ONLINE       0     0     0
            sdd     ONLINE       0     0     0
        logs
          nvme0n1   ONLINE       0     0     0

errors: No known data errors

Que cela signifie : un scrub effectue des IO. Sur un pool déjà aux prises avec la latence d’écriture, un scrub peut vous pousser au meltdown.

Décision : si vous êtes en incident, envisagez de mettre le scrub en pause (selon la politique). Reprenez-le après stabilisation de la latence.

Tâche 5 : Mettre en pause / reprendre un scrub pendant un incident (si la politique le permet)

cr0x@server:~$ sudo zpool scrub -p tank
cr0x@server:~$ zpool status tank
  pool: tank
 state: ONLINE
  scan: scrub paused since Mon Dec 23 03:01:02 2025
        3.12T scanned at 1.2G/s, 1.55T issued at 600M/s, 6.20T total
        0B repaired, 25.00% done, 02:30:12 to go
errors: No known data errors

Que cela signifie : la pression IO devrait diminuer. Vous échangez le délai de détection d’erreurs latentes contre la stabilité en production.

Décision : ne le faites que si votre organisation accepte le risque ; documentez-le et reprenez plus tard.

Tâche 6 : Inspecter les propriétés du dataset qui affectent le comportement MySQL

cr0x@server:~$ zfs get -o name,property,value -s local,default recordsize,compression,atime,sync,logbias,primarycache,secondarycache tank/mysql
NAME       PROPERTY        VALUE
tank/mysql recordsize      128K
tank/mysql compression     off
tank/mysql atime           on
tank/mysql sync            standard
tank/mysql logbias         latency
tank/mysql primarycache    all
tank/mysql secondarycache  all

Que cela signifie : recordsize est par défaut 128K, compression désactivée, atime activé. Pour InnoDB, recordsize devrait souvent être plus petit ; atime devrait généralement être off ; la compression est fréquemment bénéfique sur CPU modernes.

Décision : planifiez les changements délibérément (surtout recordsize). Désactivez atime rapidement ; envisagez d’activer la compression ; évaluez recordsize selon le contexte de la charge.

Tâche 7 : Désactiver atime pour le dataset MySQL

cr0x@server:~$ sudo zfs set atime=off tank/mysql
cr0x@server:~$ zfs get -o name,property,value atime tank/mysql
NAME       PROPERTY  VALUE
tank/mysql atime     off

Que cela signifie : les lectures ne généreront plus d’écritures de métadonnées pour mettre à jour les temps d’accès. Petit gain, mais constant.

Décision : faites-le sauf si vous avez un workload d’audit qui dépend d’atime (rare pour un répertoire de données MySQL).

Tâche 8 : Activer la compression (généralement lz4) pour réduire les E/S lors des rafales

cr0x@server:~$ sudo zfs set compression=lz4 tank/mysql
cr0x@server:~$ zfs get -o name,property,value compression tank/mysql
NAME       PROPERTY     VALUE
tank/mysql compression  lz4

Que cela signifie : ZFS compressera les nouveaux blocs. Pour les bases, cela réduit souvent la bande passante d’écriture et peut améliorer la latence, sauf si le CPU est déjà saturé.

Décision : si le CPU a de la marge, activez-la. Si le CPU est saturé pendant les incidents, testez d’abord.

Tâche 9 : Vérifier la taille ARC et la pression mémoire (éviter les combats de mémoire)

cr0x@server:~$ arcstat 1 3
    time  read  miss  miss%  dmis  dm%  pmis  pm%  mmis  mm%  arcsz     c  avail
12:10:01   980   120     10    40   33    60   50    20   17   58G   60G    3G
12:10:02  1020   140     12    55   39    70   50    15   11   59G   60G    2G
12:10:03  1005   160     14    70   44    75   47    15    9   60G   60G    1G

Que cela signifie : ARC est à sa cible (c), et la mémoire disponible s’effondre. Si l’hôte commence à swapper, MySQL en souffrira fortement.

Décision : limitez ARC (zfs_arc_max) pour que le buffer pool MySQL ait une RAM stable. Ne laissez pas les deux caches croître sans contrôle.

Tâche 10 : Vérifier le swap réel et l’attente IO au niveau OS

cr0x@server:~$ vmstat 1 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
12  3  524288  31200  10240  88400  150  220   120  9800 2100 6400 12  8 62 18  0
10  2  524288  28000  10080  87000  180  260   110 10500 2200 6500 11  9 60 20  0
14  4  524288  25000   9980  86500  210  300    90 12000 2300 6700 10 10 58 22  0
11  3  524288  24000   9950  86000  190  280   100 11000 2250 6600 11  9 59 21  0
13  4  524288  23000   9900  85500  220  310    95 12500 2350 6800 10 10 56 24  0

Que cela signifie : swap in/out (si/so) est actif et wa est élevé. C’est un scénario classique : « la latence a empiré parce que la mémoire a empiré ».

Décision : réduire ARC, réduire la pression mémoire MySQL ou ajouter de la RAM. À court terme, arrêtez la source d’écritures la plus lourde et stabilisez.

Tâche 11 : Confirmer les périphériques ZIL/SLOG et leur santé

cr0x@server:~$ zpool status -v tank
  pool: tank
 state: ONLINE
config:

        NAME        STATE     READ WRITE CKSUM
        tank        ONLINE       0     0     0
          raidz2-0  ONLINE       0     0     0
            sda     ONLINE       0     0     0
            sdb     ONLINE       0     0     0
            sdc     ONLINE       0     0     0
            sdd     ONLINE       0     0     0
        logs
          nvme0n1   ONLINE       0     0     0

errors: No known data errors

Que cela signifie : il y a un SLOG (nvme0n1) et il est sain.

Décision : si vous avez une latence sync mais que le SLOG est sain et faible en latence, le pool principal est probablement le facteur limitant (comme dans la Tâche 2).

Tâche 12 : Vérifier que le dataset est monté là où MySQL stocke réellement les données

cr0x@server:~$ findmnt /var/lib/mysql
TARGET         SOURCE      FSTYPE OPTIONS
/var/lib/mysql tank/mysql  zfs    rw,xattr,noacl

Que cela signifie : MySQL est bien sur le dataset ZFS que vous pensez. Vous seriez surpris de voir à quelle fréquence ce n’est pas le cas.

Décision : si ce n’est pas le cas, arrêtez de « tuner » le mauvais dataset et corrigez d’abord votre montage/organisation.

Tâche 13 : Inspecter les réglages de durabilité MySQL qui provoquent des écritures sync

cr0x@server:~$ mysql -e "SHOW VARIABLES WHERE Variable_name IN ('innodb_flush_log_at_trx_commit','sync_binlog','innodb_doublewrite','innodb_flush_method');"
+--------------------------------+------------+
| Variable_name                  | Value      |
+--------------------------------+------------+
| innodb_doublewrite             | ON         |
| innodb_flush_log_at_trx_commit | 1          |
| innodb_flush_method            | O_DIRECT   |
| sync_binlog                    | 1          |
+--------------------------------+------------+

Que cela signifie : c’est le mode durabilité maximale : redo flushé à chaque commit et binlog synchronisé à chaque commit. Excellent pour la correction. Plus difficile pour le stockage pendant les rafales.

Décision : ne changez pas cela à la légère. Si l’activité accepte le risque (certaines le font pour des caches, pas pour de l’argent), envisagez sync_binlog > 1 ou innodb_flush_log_at_trx_commit=2, mais seulement avec validation explicite.

Tâche 14 : Vérifier les signaux actuels de checkpoint / pression de pages sales MySQL

cr0x@server:~$ mysql -e "SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_pages_dirty'; SHOW GLOBAL STATUS LIKE 'Innodb_os_log_fsyncs'; SHOW GLOBAL STATUS LIKE 'Innodb_log_waits';"
+-------------------------------+--------+
| Variable_name                 | Value  |
+-------------------------------+--------+
| Innodb_buffer_pool_pages_dirty| 245000 |
+-------------------------------+--------+
+---------------------+--------+
| Variable_name       | Value  |
+---------------------+--------+
| Innodb_os_log_fsyncs| 185000 |
+---------------------+--------+
+-----------------+------+
| Variable_name   | Value|
+-----------------+------+
| Innodb_log_waits| 420  |
+-----------------+------+

Que cela signifie : de nombreuses pages sales et des Innodb_log_waits non nuls suggèrent un goulot de flush de log. Lors des rafales d’écriture, cela correspond à une pression sync sur le stockage.

Décision : corrélez avec la latence ZFS. Si l’attente d’écriture ZFS est élevée, corrigez le chemin de stockage ; si ZFS semble correct, examinez la configuration de log MySQL et l’ordonnancement CPU.

Tâche 15 : Repérer des processus bloqués en IO (rapide et sale)

cr0x@server:~$ ps -eo pid,comm,state,wchan:30 | egrep 'mysqld|z_wr_iss|z_wr_int|z_wr|txg|sync' | head
  2141 mysqld           D io_schedule
  2147 mysqld           D io_schedule
  2153 mysqld           D io_schedule
  1103 z_wr_iss         D cv_wait
  1104 z_wr_int         D cv_wait

Que cela signifie : des threads MySQL en état D attendant le scheduler IO indiquent des blocages de stockage. Des threads écrivains ZFS en attente peuvent aussi indiquer du throttling/pression de commit interne.

Décision : validez avec zpool iostat -l et les stats disque OS. Si confirmé, réduisez la charge d’écriture et adressez les limites du pool.

Tâche 16 : Vérifier la latence par périphérique et la saturation depuis Linux (complète la vue ZFS)

cr0x@server:~$ iostat -x 1 3
avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          12.10    0.00    8.20   18.40    0.00   61.30

Device            r/s     w/s   rKB/s   wKB/s  avgrq-sz avgqu-sz   await  r_await  w_await  svctm  %util
sda               9.0   580.0   1200   68000     228.0     45.0   135.0     3.0    137.0   1.6  99.0
sdb               9.0   570.0   1180   67000     228.0     44.0   132.0     3.0    134.0   1.6  98.5
nvme0n1           0.0   120.0      0   14000     233.0      0.3     2.0     0.0      2.0   0.2   2.4

Que cela signifie : les HDDs sont saturés à ~99% d’util avec ~130 ms d’attente en écriture. Le NVMe (SLOG) est correct. Cela confirme l’histoire ZFS : la latence d’écriture du pool est le problème.

Décision : il vous faut plus d’IOPS (plus de vdevs, autre topologie), moins de pression d’écriture aléatoire (tuning, groupement), ou plus de marge (espace libre).

Les réglages ZFS importants pour MySQL (et lesquels sont des pièges)

Recordsize : alignez-le sur la charge, pas sur l’idéologie

Pour InnoDB, la pratique fréquente est recordsize=16K sur le dataset stockant les tablespaces. Raisonnement : InnoDB modifie des pages de 16K, et des enregistrements plus petits peuvent réduire l’amplification d’écriture et améliorer la latence lors d’updates aléatoires.

Mais ne faites pas de cargo-cultisme. Si votre charge est principalement des scans séquentiels larges, des backups ou de l’analytique lisant de larges plages, un recordsize plus grand peut aider. Si vous stockez binlogs ou backups sur le même dataset (ne le faites pas), vous aurez des besoins conflictuels.

Position pratique :

  • Mettez les données MySQL sur leur propre dataset.
  • Pour OLTP lourd InnoDB, commencez à recordsize=16K.
  • Pour une charge mixte, testez 16K vs 32K.

Compression : lz4 est généralement de l’argent gratuit

La compression réduit les octets écrits et lus. Pour les rafales d’écriture, ça compte car vous essayez de pousser moins d’E/S physiques par le même goulot. Sur des CPU modernes, lz4 est typiquement un gain net.

Cas limites existent : si le CPU est déjà saturé pendant les rafales, la compression peut vous déplacer d’un goulot IO vers un goulot CPU. C’est souvent mieux (la saturation CPU est plus facile à monter en charge que la latence stockage), mais mesurez.

sync et logbias : soyez honnête, mais choisissez votre poison

sync=standard est le réglage normal et honnête : respectez les requêtes sync de l’application. sync=disabled ment aux applications : il accuse réception des écritures sync avant qu’elles soient durables. C’est parfois utilisé pour des données éphémères ou caches, mais pour des bases réelles c’est un piège à perte de données.

logbias=latency vs logbias=throughput est souvent mal compris. Pour MySQL, vous vous souciez généralement de la latence. Si vous avez un SLOG, logbias=latency est raisonnable. Si vous n’avez pas de SLOG et que votre pool est lent, changer logbias ne créera pas de hardware. Il peut toutefois modifier la quantité allant dans le ZIL vs le pool principal dans certains cas.

Blague #2 : Mettre sync=disabled pour « corriger la latence » revient à enlever votre détecteur de fumée parce qu’il sonne pendant un incendie.

SLOG : ce qu’il fait réellement et quand il aide

Un SLOG aide quand :

  • Votre charge effectue de nombreuses écritures synchrones.
  • Votre pool principal a une latence pire qu’un bon SSD/NVMe.
  • Le périphérique SLOG a une protection contre la perte d’alimentation et une latence d’écriture faible et constante.

Un SLOG n’aide pas quand :

  • Vous êtes limité par les commits TXG vers le pool principal (le pool ne peut pas vider assez vite).
  • Votre charge est majoritairement asynchrone (pas de pression fsync).
  • Votre « SLOG » est un SSD grand public qui s’effondre sous écritures soutenues ou qui ment sur les flushes.

Conseil opérationnel : mettez le SLOG en miroir si votre profil de risque l’exige. Un périphérique SLOG défaillant peut forcer le pool dans un état dangereux selon la plateforme et la configuration ; même lorsqu’il ne le fait pas, vous venez de créer un déclencheur d’incident. Pour un MySQL critique, un SLOG miroir est ennuyeux mais correct.

vdev spécial : les métadonnées et petits blocs peuvent être votre gain caché

Si votre pool est basé sur des HDD, envisagez un vdev spécial (sur SSD) pour les métadonnées et les petits blocs. Les bases créent du churn de métadonnées : blocs indirects, structures de répertoire, space maps. Les placer sur un média rapide peut réduire la latence et améliorer la cohérence.

Mais : un vdev spécial n’est pas redondant par défaut. S’il meurt et n’est pas redondant, vous pouvez perdre le pool. Traitez-le comme un vdev haut de gamme avec un mirroring approprié.

Primarycache/secondarycache : arrêtez de vous dupliquer en cache jusqu’au swap

MySQL a déjà un buffer pool. ZFS a ARC et potentiellement L2ARC. Si vous mettez tout deux en cache, vous gaspillez de la RAM et augmentez le churn d’éviction lors des rafales.

Beaucoup d’équipes mettent primarycache=metadata sur le dataset MySQL pour éviter qu’ARC ne mette en cache les pages de données que InnoDB garde déjà, tout en gardant les métadonnées chaudes. Cela peut aider à stabiliser l’usage mémoire. Ce n’est pas universel ; mesurez vos patterns de lecture.

Ashift et layout vdev : la règle « vous ne pouvez pas tuner hors de la géométrie »

Si votre pool a été construit avec un mauvais alignement de secteur (ashift trop petit), vous pouvez souffrir d’amplification d’écriture pour toujours. Si votre layout vdev a été conçu pour la capacité plutôt que pour les IOPS (par exemple, RAIDZ large pour une base OLTP sensible à la latence), vous pouvez subir une douleur prévisible.

Règle opinionnée : pour MySQL sensible à la latence, les vdevs mirror sont le choix par défaut à moins d’avoir une raison très claire de ne pas le faire. RAIDZ peut fonctionner, mais il rend les petites écritures aléatoires plus difficiles et le comportement de rebuild plus complexe.

Réglages MySQL/InnoDB qui interagissent avec ZFS

Paramètres de durabilité : rendre le risque explicite

Le levier de latence le plus important dans MySQL est la fréquence à laquelle vous forcez des flushs durables :

  • innodb_flush_log_at_trx_commit=1 : redo durable à chaque commit. Sécurité maximale, pression sync la plus élevée.
  • innodb_flush_log_at_trx_commit=2 : flush vers l’OS à chaque commit, fsync une fois par seconde. Moins de pression sync ; risque de perdre jusqu’à 1s de transactions en cas de crash.
  • sync_binlog=1 : fsync du binlog à chaque commit. Cohérence forte en réplication ; pression sync.

Si vous choisissez d’assouplir ces réglages, faites-le comme une décision métier, pas comme un bricolage de minuit. Pour beaucoup d’entreprises, perdre jusqu’à une seconde de données est acceptable pour certains systèmes (staging analytique) et inacceptable pour d’autres (transactions, inventaire, authentification).

Méthode de flush : éviter le double buffering

innodb_flush_method=O_DIRECT est couramment utilisé pour éviter la page cache de l’OS pour les fichiers de données InnoDB, réduisant le double buffering. Sur ZFS, l’interaction est nuancée parce que ZFS a ARC, pas le même modèle de page cache, mais O_DIRECT est souvent utilisé avec succès.

Ce que vous essayez d’éviter : MySQL écrit les données, l’OS les met en cache, ZFS les met en cache à nouveau, la mémoire s’évapore, puis le noyau commence à swapper. Ce n’est pas une stratégie de performance ; c’est un appel à l’aide.

Réglage des pages sales : les rafales sont amplifiées par l’arriéré

Quand le buffer pool accumule trop de pages sales, MySQL doit flush de manière agressive. Cela peut transformer une rafale modérée en une ruée sur le stockage. Réglez :

  • innodb_max_dirty_pages_pct et innodb_max_dirty_pages_pct_lwm
  • innodb_io_capacity et innodb_io_capacity_max (paramétrez-les en fonction de la capacité réelle du stockage)
  • innodb_flush_neighbors (souvent 0 sur pools SSD ; plus nuancé sur HDD)

Ne mettez pas innodb_io_capacity à « un grand nombre » parce que vous avez acheté des disques rapides. Fixez-le à ce que le pool délivre en conditions mixtes lecture/écriture sous charge, pas à la fiche technique.

Binlogs et fichiers temporaires : ne colocalisez pas votre douleur

Placez les binlogs sur un dataset optimisé pour les écritures séquentielles, potentiellement avec un recordsize plus grand, et évitez qu’ils ne concurrencent les tablespaces si possible. Idem pour tmpdir si vous faites des tris lourds. Colocaliser tout sur un seul dataset est la façon de créer une « latence mystérieuse » pendant les rafales.

Trois mini-récits d’entreprise issus du terrain

Incident causé par une fausse hypothèse : « On a ajouté un SLOG, donc la latence sync est résolue »

L’entreprise avait un primaire MySQL sur un pool ZFS sur HDD. Ils subissaient des pics p99 lors de rafales de trafic — surtout autour d’envois marketing et de jobs de fin de mois. Quelqu’un a fait la bonne chose à moitié : ils ont ajouté un NVMe rapide comme SLOG. La latence s’est améliorée en steady state, donc le changement a été qualifié de « corrigé ».

Des mois plus tard, une rafale plus intense est arrivée. Le p99 a encore grimpé, mais l’on-call était confiant que ce ne pouvait pas être le stockage parce que « nous avons un SLOG ». Ils ont chassé des fantômes dans les plans de requêtes et les pools de connexion pendant que le pool restait fortement utilisé. Les utilisateurs ont continué à réessayer, ce qui a généré plus d’écritures, puis plus de latence — échec auto-alimenté classique.

Lorsqu’ils ont finalement regardé zpool iostat -l, l’histoire était claire : les écritures du SLOG étaient à faible latence, mais l’attente d’écriture du pool principal était énorme. Les commits TXG étaient le goulot. Le ZIL pouvait accuser réception rapidement, mais il ne pouvait pas rendre le pool capable d’écrire les données sales plus vite.

La correction n’était pas un autre périphérique de log. Elle était ennuyeuse : ajouter des vdevs pour augmenter les IOPS, réduire le remplissage du pool et séparer les charges pour que binlogs et tmp ne se battent pas avec les tablespaces. L’erreur était de penser que latence sync = latence SLOG ; en réalité, la durabilité sync dépend aussi du pool qui reste en avance sur le travail sale accumulé.

Une optimisation qui a rebondi : « On augmente recordsize pour le throughput »

Une autre équipe avait un pool majoritairement SSD et voulait améliorer les performances de gros chargements. Ils ont changé le recordsize du dataset MySQL à 1M car recommandé pour de larges charges séquentielles. Les gros chargements se sont accélérés. Victoire proclamée.

Puis la latence OLTP a commencé à osciller. Pas toujours, mais pendant les rafales. De petites mises à jour de lignes chaudes provoquaient des E/S disproportionnées. ZFS réécrivait des enregistrements volumineux plus souvent, et le churn des métadonnées a augmenté. Le pool pouvait gérer la bande passante, mais la distribution de latence est devenue désagréable. Le business ne se souciait pas que les chargements nocturnes se terminent plus vite ; il se souciait que le checkout prenne parfois 900 ms.

Le pire était opérationnel : les snapshots ont grossi et la réplication a mis plus de temps car les changements touchaient des blocs plus larges. Le RTO s’est discrètement dégradé. Personne ne voulait changer le RTO, mais ils l’ont fait.

La solution a été de segmenter : datasets séparés pour tables chargées en bulk avec un recordsize plus grand, garder les tables OLTP à 16K ou 32K, et arrêter de traiter « MySQL » comme un seul pattern IO homogène. Le rebond n’était pas que le grand recordsize soit toujours mauvais ; c’était d’appliquer un seul réglage à des workloads conflictuels.

Une pratique ennuyeuse mais correcte qui a sauvé la mise : « marge et throttling gradué »

Une organisation exécutait MySQL sur ZFS pour un système interne critique. Rien de glamour : workflows métiers, quelques rafales pendant la journée, backfills occasionnels. Ils avaient une règle stricte : le pool ne dépasse jamais un seuil conservateur de remplissage, et chaque job écrit lourd a un bouton de throttle intégré dans l’ordonnanceur.

Quand un bug a entraîné des retries agressifs en amont, le trafic d’écriture a triplé en quelques minutes. C’était le type d’incident qui devient habituellement un meltdown de plusieurs heures. Mais cette fois la base est devenue lente, pas hors-service. La latence a monté, mais n’a pas dégénéré en timeouts.

Pourquoi ? Deux raisons. D’abord, il y avait de l’espace libre et peu de fragmentation, donc l’allocation ne s’est pas dégradée sous pression. Ensuite, l’on-call a pu rapidement réduire le backfill et la file de batch sans toucher aux paramètres de durabilité. Le système avait des voies d’échappement qui n’impliquaient pas de mentir au système de fichiers.

Après coup, le postmortem était presque ennuyeux. C’était le but. La pratique « ennuyeuse » était marge de capacité plus contrôle opérationnel des rafales d’écriture. Pas un magic sysctl. Pas un rebuild héroïque. Juste une planification disciplinée et la capacité à réduire volontairement la charge d’écriture.

Erreurs courantes : symptôme → cause racine → correction

1) Pics p99 pendant des rafales, SLOG semble correct

Symptôme : zpool iostat -l montre une faible latence de log mais une forte attente d’écriture du pool ; MySQL montre des log waits et des timeouts.

Cause racine : le pool principal ne peut pas flush les TXG assez vite ; le pool est saturé, fragmenté ou comporte trop peu de vdevs pour les IOPS.

Correction : ajouter des vdevs ou passer à une topologie mirror, réduire le remplissage du pool, réduire les workloads concurrents, envisager un vdev spécial pour les métadonnées, tuner le comportement des pages sales MySQL.

2) La latence s’aggrave soudainement après que le pool dépasse ~80% d’utilisation

Symptôme : aucun changement de configuration, mais les écritures ralentissent sur des semaines ; la fragmentation augmente.

Cause racine : pénalités d’allocation et de fragmentation sur un système copy-on-write presque plein ; écriture plus dispersée et plus de mises à jour de métadonnées.

Correction : ajouter de la capacité (de préférence en ajoutant des vdevs, pas en remplaçant les disques un par un), rééquilibrer en migrant des datasets, appliquer des SLO d’espace libre.

3) « Corrigé » en mettant sync=disabled, puis un crash cause une perte de données

Symptôme : la latence est bonne jusqu’à un reboot inattendu ; après redémarrage, les tables MySQL sont corrompues ou les commits récents manquent.

Cause racine : les sémantiques synchrones ont été désactivées ; les applications ont reçu une ack avant que les données soient durables.

Correction : remettre sync=standard (ou always si nécessaire), utiliser un vrai SLOG et résoudre le vrai goulot de performance.

4) Le swapping commence pendant les rafales et ne se rétablit jamais complètement

Symptôme : vmstat montre de l’activité de swap ; MySQL reste bloqué même après la fin des rafales ; ARC reste volumineux.

Cause racine : ARC et buffer pool MySQL se concurrencent ; la pression mémoire déclenche reclaim et swap, augmentant les IO et la latence.

Correction : limiter ARC, redimensionner correctement le buffer pool MySQL, éviter L2ARC sauf si vous pouvez assumer le coût mémoire, ajouter de la RAM si nécessaire.

5) Les écritures sont rapides jusqu’à ce qu’un scrub/resilver démarre, puis le p99 explose

Symptôme : corrélé avec zpool status montrant une activité de scan.

Cause racine : les IO de maintenance concurrencent les écritures de production ; le pool n’a pas de marge.

Correction : planifier les scrubs hors pic, assurer une marge IOPS pour le pool, utiliser des contrôles de priorité IO si disponibles, mettre les scrubs en pause pendant les incidents selon la politique.

6) Après « ajout de cache SSD », les performances empirent sous écriture

Symptôme : L2ARC activé ; sous rafales, la latence d’écriture augmente ; l’utilisation mémoire monte.

Cause racine : les coûts de maintenance du L2ARC (métadonnées, écritures vers L2ARC) augmentent la pression ; le surcoût mémoire réduit la marge.

Correction : désactiver L2ARC pour les systèmes OLTP à écriture intensive sauf si vous avez un problème de miss en lecture mesuré et beaucoup de RAM.

7) Stalls aléatoires toutes les quelques secondes comme une horloge

Symptôme : pics de latence périodiques alignés avec les intervalles de commit TXG.

Cause racine : flushs TXG en rafales provoquant de la mise en file ; le pool ne peut pas soutenir le workload de flush en continu.

Correction : augmenter la capacité d’écriture du pool, réduire la production de données sales (tuning MySQL), vérifier les jobs de fond provoquant des rafales, assurer que le SLOG n’est pas la seule « solution » de performance.

Listes de contrôle / plan étape par étape

Étape par étape : construire (ou reconstruire) ZFS spécifiquement pour la résilience aux rafales MySQL

  1. Choisir une topologie vdev priorisant les IOPS : vdevs mirror comme base pour l’OLTP. La capacité vient de plus de mirrors, pas de RAIDZ large.
  2. Garder le pool sous un SLO de capacité : règle interne (par ex. alerter à 70%, agir à 80%). Le chiffre exact dépend de la charge, mais « faire tourner jusqu’à 95% » n’est pas de l’ingénierie sérieuse.
  3. Utiliser un vrai SLOG si vous avez besoin de perf sync : latence faible, protégé contre la perte de puissance, et de préférence mirroring.
  4. Envisager un vdev spécial sur pools HDD : en miroir, dimensionné pour les métadonnées et petits blocs.
  5. Créer des datasets séparés :
    • tank/mysql pour les tablespaces InnoDB
    • tank/mysql-binlog pour les binlogs
    • tank/mysql-tmp pour tmpdir si nécessaire
  6. Définir les propriétés des datasets intentionnellement :
    • atime=off
    • compression=lz4
    • recordsize=16K (point de départ pour les tablespaces OLTP)
    • logbias=latency (typique pour les datasets DB)
  7. Décider d’une politique de cache : souvent primarycache=metadata sur les tablespaces, garder le défaut sur les binlogs si vous les lisez fréquemment pour réplication/sauvegarde.
  8. Limiter ARC selon la RAM totale et le buffer pool MySQL : laissez de la marge pour l’OS, les tables de pages, les connexions et l’absorption des rafales.
  9. Tester en charge les rafales d’écriture : pas seulement la charge moyenne. Simulez un pattern de rafales et surveillez le p99, pas seulement le débit.

Checklist opérationnelle : quand vous déployez un changement lourd en écriture

  • Vérifier la capacité et la fragmentation du pool. Si vous êtes déjà proche du seuil, retarder le job ou ajouter de la capacité d’abord.
  • Confirmer qu’un scrub/resilver n’est pas prévu en chevauchement.
  • S’assurer que les jobs batch ont des throttles et peuvent être mis en pause sans changement de code.
  • Baseliner zpool iostat -l et les compteurs fsync MySQL avant le changement.
  • Alerter sur la latence p99, Innodb_log_waits et le temps d’attente d’écriture ZFS ensemble. Les alertes mono-métriques mentent.

Checklist d’incident : les règles « ne pas empirer »

  • Ne pas mettre sync=disabled sur le dataset de données comme mitigation d’incident à moins d’accepter explicitement la perte de données.
  • Ne pas changer le recordsize en plein incident en espérant un soulagement instantané ; il n’affecte que les nouvelles écritures et la cause racine est généralement ailleurs.
  • Mettre en pause les scrubs/resilvers si la politique le permet et que le pool fond.
  • Réduire la charge d’écriture : throttler les jobs, diminuer les retries, éliminer les écritures non critiques et arrêter les backfills.
  • Capturer des preuves : zpool iostat -l, iostat -x, compteurs MySQL. Votre futur vous voudra des reçus.

FAQ

1) Dois-je exécuter MySQL sur ZFS du tout ?

Oui, si vous voulez de fortes garanties d’intégrité, des snapshots et une administration raisonnable. Mais vous devez concevoir pour la latence : topologie vdev appropriée, marge disponible et plan pour les écritures synchrones.

2) Le SLOG est-il obligatoire pour MySQL ?

Pas toujours. Si votre charge est majoritairement d’écritures asynchrones ou que vous acceptez une durabilité relaxée, vous n’en aurez peut-être pas besoin. Si vous exécutez innodb_flush_log_at_trx_commit=1 et sync_binlog=1 sous un trafic rafaleux, un bon SLOG fait souvent la différence entre « ça va » et « incident ».

3) Puis-je corriger la latence des rafales en mettant sync=disabled ?

Vous pouvez réduire la latence et aussi réduire la vérité. Cela accuse réception des écritures sync avant qu’elles ne soient durables, ce qui peut faire perdre des transactions commises en cas de crash. Utilisez-le uniquement pour des données non critiques et reconstructibles.

4) Quel recordsize utiliser pour InnoDB ?

Point de départ courant : recordsize=16K pour le dataset tablespace. Testez 16K vs 32K si vous avez des patterns mixtes. Séparez les datasets pour les différents patterns IO (binlogs, backups).

5) La compression ZFS aide-t-elle les bases de données ?

Souvent oui. lz4 peut réduire les écritures et lectures physiques, ce qui aide lors des rafales et réduit l’usure. Validez la marge CPU et mesurez le p99 de latence, pas seulement le débit.

6) Dois-je activer L2ARC pour MySQL ?

Habituellement non pour de l’OLTP à écriture intensive. L2ARC a un coût mémoire et des coûts de maintenance en écriture. S’il y a un problème réel de miss en lecture et beaucoup de RAM, il peut aider, mais ce n’est pas un réglage par défaut.

7) Pourquoi la performance se dégrade-t-elle à mesure que le pool se remplit ?

Le copy-on-write devient plus difficile avec moins d’espace libre ; la fragmentation et la surcharge métadonnées augmentent. Votre « simple écriture » devient une E/S plus aléatoire et plus de paperasserie. Gardez de la marge.

8) RAIDZ ou mirrors pour MySQL ?

Pour l’OLTP sensible à la latence, les mirrors sont le défaut sûr car ils fournissent plus d’IOPS et une latence plus prévisible. RAIDZ peut fonctionner, mais il est plus facile d’atteindre des falaises de latence sous petites écritures aléatoires et forte utilisation.

9) Comment savoir si le goulot est les écritures sync ou le flush en arrière-plan ?

Corrélez Innodb_log_waits et les compteurs fsync MySQL avec zpool iostat -l (temps d’attente du log et du pool). Si la latence du log est élevée, regardez le SLOG et le chemin sync. Si l’attente du pool est élevée, regardez la pression de flush TXG, le remplissage du pool et la topologie vdev.

10) Les vdevs spéciaux peuvent-ils aider la latence MySQL ?

Oui, surtout sur des pools HDD, en accélérant les métadonnées et les petits IO. Mettez-les en miroir et traitez-les comme critique ; la perte d’un vdev spécial peut signifier la perte du pool.

Conclusion : prochaines étapes qui réduisent réellement le risque

Si vous exécutez MySQL sur ZFS et que vous craignez les rafales d’écriture, vous n’avez pas besoin de mysticisme. Vous avez besoin de trois choses : un modèle mental correct, des choix de durabilité honnêtes et suffisamment d’IOPS/marge pour que ZFS fasse son travail sans transformer la base en boulet.

Prochaines étapes pratiques :

  1. Exécutez le playbook de diagnostic rapide un jour calme. Capturez une baseline zpool iostat -l, iostat -x et les compteurs MySQL clés.
  2. Auditez le remplissage et la fragmentation du pool. Si vous dépassez votre seuil sûr, traitez la capacité comme une correction de performance urgente.
  3. Confirmez votre chemin d’écriture sync : le sync du dataset est honnête, le SLOG (si présent) est de qualité entreprise et sain, et vous comprenez si le flush du pool est le vrai goulot.
  4. Séparez les datasets par pattern IO et définissez les propriétés intentionnellement (atime off, lz4 on, recordsize approprié).
  5. Limitez ARC pour que la mémoire ne devienne pas le déclencheur caché des incidents de stockage.
  6. Mettez en place des throttles opérationnels pour les jobs lourds en écriture. Votre meilleur correcteur de latence pendant une rafale est souvent « arrêter les écritures en trop », pas « changer le système de fichiers en pleine bataille ».

Faites cela, et la prochaine rafale d’écriture deviendra un ralentissement contrôlé, pas un effondrement à minuit. L’objectif n’est pas une récupération héroïque. C’est des graphiques ennuyeux.

← Précédent
Migration e-mail : déplacer la messagerie vers un nouveau serveur avec un temps d’arrêt minimal (étapes réelles)
Suivant →
Ubuntu 24.04 : Corriger « Too many open files » sur Nginx en augmentant les limites correctement (systemd)

Laisser un commentaire