Vous avez lancé fio sur Debian 13, obtenu des chiffres héroïques et décrété la victoire. Puis la production a planté pendant le premier reindex, la première sauvegarde ou la première compaction, et les disques « rapides » se sont soudain comportés comme un partage réseau de 2008.
C’est normal. Pas parce que le stockage Linux est mauvais — mais parce que les benchmarks sont faciles à biaiser par accident. fio ne ment pas ; nous nous mentons à nous-mêmes par des hypothèses incorrectes, des paramètres par défaut pratiques et des tests qui ne correspondent pas à la charge réelle.
Ce que fio mesure réellement (et ce qu’il ne mesure pas)
fio est un générateur de charge. Il soumet des E/S avec des patrons spécifiques (aléatoire vs séquentiel, lecture vs écriture, mixte, tailles de blocs, profondeur de file, modes de synchronisation) et rapporte ce qui s’est passé. C’est tout. Il ne sait pas si vous avez testé le cache de page plutôt que le disque. Il ne sait pas que votre SSD est sur le point de réduire ses performances pour cause de température. Il ne sait pas que votre contrôleur RAID acknowledge des écritures qui sont encore dans une mémoire volatile. Il ne sait pas que votre base de données fait des lectures aléatoires 4k alors que votre benchmark a effectué des écritures séquentielles 1M.
Pour benchmarker le stockage sans se leurrer, vous devez répondre à trois questions en amont :
- Que cherchez-vous à prédire ? Débit de pointe ? Latence P99 sous concurrence ? Temps de récupération ? Comportement en queue lors de rafales d’écritures ?
- Quel est l’unité de vérité ? Les IOPS ne sont pas la vérité si vos utilisateurs se soucient de la latence. Le débit n’est pas la vérité si votre goulot d’étranglement est constitué d’écritures synchrones en petits blocs.
- Quel système testez-vous ? Le disque seul, la pile OS, le système de fichiers, le gestionnaire de volumes, le chiffrement, le stockage réseau, le cache du contrôleur et la planification CPU comptent tous. Décidez ce qui relève du périmètre.
Les benchmarks ne doivent pas « être équitables ». Ils doivent être prédictifs. Si votre test ne correspond pas à la forme d’E/S en production, c’est un générateur de confiance, pas un benchmark.
Faits et contexte intéressants (ce qui rend prévisibles les erreurs d’aujourd’hui)
- Le cache de page Linux est un « amplificateur de benchmark » depuis les années 1990. L’E/S tamponnée peut mesurer la vitesse de la RAM et ressembler à la vitesse du disque si vous ne forcez pas l’I/O directe ou n’excédez pas la mémoire.
- Les performances SSD dépendent de l’état interne. Une NAND sortie d’usine se comporte différemment qu’en état d’équilibre après de nombreux écrits ; le préconditionnement est devenu un concept formel parce que les premières revues SSD étaient essentiellement de la fantaisie.
- La profondeur de file s’est popularisée avec les SAN d’entreprise. Beaucoup de folklore autour de « QD32 » vient des réglages à l’époque des contrôleurs ; ce n’est pas automatiquement pertinent pour NVMe basse latence et les applications modernes avec une concurrence limitée.
- NVMe a changé le côté CPU du stockage. Les chemins de soumission/complétion d’E/S sont devenus moins coûteux et plus parallèles, si bien que le pinning CPU et la distribution des IRQ peuvent devenir le « goulot disque » même quand le média est ok.
- Le cache d’écriture a toujours été controversé. Les contrôleurs qui acquittent des écritures avant qu’elles ne soient durables ont produit d’excellents benchmarks et quelques pertes mémorables ; la mémoire cache sur batterie fut le compromis.
- Les systèmes de fichiers ont historiquement optimisé pour différents modes de panne. Les réglages par défaut d’ext4 visent une sécurité large ; XFS brille souvent en débit parallèle ; les systèmes copy-on-write échangent certaines latences contre snapshots et sommes de contrôle.
- Les problèmes d’alignement sont plus anciens que les SSD. Des partitions mal alignées pénalisent les unités de bande RAID et font toujours mal aujourd’hui quand vos blocs logiques 4k ne correspondent pas à la géométrie sous-jacente.
- Les “percentiles de latence” sont passés de l’académique à l’opérationnel. L’industrie a appris que les moyennes cachent la douleur ; P99 et P99.9 sont devenus significatifs à mesure que les systèmes web-scale ont grandi.
Une citation à garder en tête pendant les benchmarks : Espérer n’est pas une stratégie.
(Vince Lombardi)
Comment les benchmarks trichent accidentellement : les suspects habituels
1) Vous avez benchmarké la RAM (cache de page), pas le stockage
Si vous exécutez des jobs fio basés sur des fichiers sans --direct=1, Linux peut satisfaire des lectures depuis le cache et absorber des écritures en mémoire, puis les vider après coup. Vos résultats ont l’air incroyables. Votre base de données n’obtient pas ces chiffres quand elle doit attendre une durabilité réelle.
Et non, « mais mon fichier de test était gros » n’est pas toujours suffisant. Les caches existent à plusieurs couches : cache de page, cache du disque, cache du contrôleur, voire l’hyperviseur si vous êtes virtualisé.
2) Votre taille de requête et la sémantique de sync ne correspondent pas à la réalité
Beaucoup de systèmes en production effectuent des écritures petites et synchrones (commits de journal, WAL fsync, mises à jour de métadonnées). Un benchmark qui réalise des écritures séquentielles 1M avec une forte profondeur de file mesure un univers différent.
3) La profondeur de file devient un costume de performance
Une forte iodepth peut masquer la latence en maintenant l’appareil occupé, gonflant le débit et les IOPS. Cela peut être légitime — si votre application émet réellement autant de requêtes en attente. Si ce n’est pas le cas, vous testez un système que vous n’utilisez pas.
4) Le dispositif est dans un état « propre » que la production ne voit jamais
Le comportement du FTL des SSD dépend des blocs libres et de la pression du garbage collection. Les tests courts sur des disques vides peuvent être irréalistes. Les tests longs peuvent devenir lents. Les deux sont vrais ; un seul est prédictif.
5) Vous avez mesuré un chemin différent de celui utilisé par votre charge
Le test du bloc peut contourner la surcharge du système de fichiers, le journal, les options de montage et la contention de métadonnées. Les tests basés sur les fichiers incluent ces éléments — mais aussi la disposition des répertoires et le comportement d’allocation. Choisissez en connaissance de cause.
6) CPU, IRQ et NUMA deviennent discrètement « performance stockage »
Sur NVMe, il est courant d’être limité par le traitement des interruptions, la contention sur une file unique ou une mauvaise affinité. Votre « benchmark disque » devient un benchmark d’ordonnancement CPU. Ce n’est pas faux — sauf si vous ne le remarquez pas.
7) La gestion d’énergie, la réduction de fréquence et les politiques firmware changent les règles en cours de test
Les SSD et les NVMe peuvent réduire leur cadence pour cause thermique. Les CPU peuvent rétrograder. Les portables font des choses de portable. Les serveurs font des choses d’« économie d’énergie ». Votre benchmark devient un test de dissipateur thermique, pas d’IOPS.
Blague courte #1 : Un benchmark de stockage sans percentiles de latence, c’est comme une critique de restaurant qui donne seulement le temps d’attente moyen — félicitations pour votre faim statistiquement satisfaisante.
Tâches pratiques : 12+ contrôles avec commandes, signification des sorties et décisions
Ce ne sont pas des « bons à avoir ». C’est la façon d’empêcher fio de devenir du théâtre de performance sur Debian 13.
Task 1: Verify what device you’re testing (and whether it’s virtual)
cr0x@server:~$ lsblk -o NAME,MODEL,SIZE,ROTA,TRAN,TYPE,MOUNTPOINTS
NAME MODEL SIZE ROTA TRAN TYPE MOUNTPOINTS
sda INTEL SSDSC2KB48 447G 0 sata disk
├─sda1 1G 0 part /boot
└─sda2 446G 0 part
└─vg0-lv0 400G 0 lvm /
nvme0n1 SAMSUNG MZVL21T0 1.8T 0 nvme disk
└─nvme0n1p1 1.8T 0 part /data
Ce que cela signifie : Vous ne pouvez pas interpréter les résultats si vous ne savez pas si vous êtes sur un SSD SATA, un NVMe, un disque virtuel ou une couche LVM. ROTA=0 indique non-rotationnel, mais cela inclut toujours les SSD SATA et NVMe.
Décision : Choisissez des tests sur bloc pour comparer les périphériques bruts ; des tests sur fichier pour le système de fichiers + options de montage ; ne les mélangez pas puis ne discutez pas de « le disque ».
Task 2: Identify the active I/O scheduler (and avoid accidental queueing behavior)
cr0x@server:~$ cat /sys/block/nvme0n1/queue/scheduler
[mq-deadline] none
Ce que cela signifie : Le scheduler entre crochets est actif. NVMe fonctionne souvent bien avec none ou mq-deadline, mais le « bon » dépend de votre charge et des valeurs par défaut du noyau.
Décision : Restez cohérent entre les tests. Si vous benchmarkez pour la production, alignez le scheduler sur la production. Si vous diagnostiquez une latence étrange, testez none et mq-deadline.
Task 3: Confirm logical/physical block sizes and alignment risk
cr0x@server:~$ sudo blockdev --getss /dev/nvme0n1
512
cr0x@server:~$ sudo blockdev --getpbsz /dev/nvme0n1
4096
Ce que cela signifie : Secteurs logiques 512 bytes, physiques 4k. Un désalignement peut entraîner des pénalités de lecture-modification-écriture, surtout pour les petites écritures.
Décision : Assurez-vous que les partitions commencent à des frontières 1MiB (les outils modernes le font généralement). Pour les bases de données, alignez la taille de page et la taille de bloc du système de fichiers quand c’est possible.
Task 4: Check filesystem and mount options (journaling and barriers matter)
cr0x@server:~$ findmnt -no SOURCE,FSTYPE,OPTIONS /data
/dev/nvme0n1p1 ext4 rw,relatime,errors=remount-ro
Ce que cela signifie : Des options comme noatime, barrier (ou nobarrier historiquement) et le mode de journalisation influencent le comportement d’écriture.
Décision : Si vous mesurez des performances sensibles à la durabilité, ne changez pas d’options « pour la vitesse » sans prouver la sécurité en cas de perte d’alimentation et la correction applicative.
Task 5: Detect write cache settings (and whether you’re benchmarking honesty)
cr0x@server:~$ sudo hdparm -W /dev/sda | head
/dev/sda:
write-caching = 1 (on)
Ce que cela signifie : Le cache d’écriture « activé » peut être acceptable pour des SSD avec protection contre la perte d’alimentation ; dangereux pour des disques grand public ou des contrôleurs sans protection si vous désactivez les barrières ou simulez des flushs inexistants.
Décision : Pour des bases de données en production, exigez une protection contre la perte d’alimentation ou des réglages conservateurs. Pour les benchmarks, notez explicitement la configuration du cache dans votre rapport.
Task 6: Observe current device health and thermal state (before blaming fio)
cr0x@server:~$ sudo smartctl -a /dev/nvme0n1 | sed -n '1,40p'
smartctl 7.4 2023-08-01 r5530 [x86_64-linux-6.12.0-amd64] (local build)
=== START OF INFORMATION SECTION ===
Model Number: SAMSUNG MZVL21T0
Serial Number: S6XXXXXXXXXXXX
Firmware Version: 3B2QGXA7
Total NVM Capacity: 1,900,000,000,000 [1.90 TB]
...
Temperature: 68 Celsius
Available Spare: 100%
Percentage Used: 2%
Ce que cela signifie : Si la température est élevée, la limitation thermique peut intervenir en cours de test. « Percentage Used » aide à détecter des disques en fin de vie qui se comportent de façon étrangement variable.
Décision : Si les thermiques sont proches des limites constructeur, améliorez le refroidissement avant de « tuner fio ». Si le disque est usé, attendez-vous à des écritures soutenues moins bonnes.
Task 7: Confirm you can bypass page cache (direct I/O sanity check)
cr0x@server:~$ fio --name=direct-check --filename=/data/fio.test --size=2G --rw=read --bs=128k --direct=1 --ioengine=libaio --iodepth=16 --numjobs=1 --runtime=10 --time_based --group_reporting
direct-check: (g=0): rw=read, bs=(R) 128KiB-128KiB, (W) 128KiB-128KiB, (T) 128KiB-128KiB, ioengine=libaio, iodepth=16
fio-3.38
Starting 1 process
direct-check: Laying out IO file (1 file / 2048MiB)
Jobs: 1 (f=1): [R(1)][100.0%][r=912MiB/s][r=7296 IOPS][eta 00m:00s]
direct-check: (groupid=0, jobs=1): err= 0: pid=21199: Mon Dec 29 11:09:10 2025
read: IOPS=7200, BW=900MiB/s (944MB/s)(9000MiB/10001msec)
Ce que cela signifie : --direct=1 demande l’I/O direct (contournant le cache de page). Si vous l’omettez, les lectures peuvent frapper la RAM après la première passe.
Décision : Utilisez --direct=1 pour caractériser le périphérique. Utilisez l’I/O tamponné uniquement si vous modélisez explicitement une application qui dépend du cache (et alors mesurez aussi la taille et le comportement du cache).
Task 8: Prove to yourself that buffered reads can cheat
cr0x@server:~$ fio --name=buffered-cheat --filename=/data/fio.test --size=2G --rw=read --bs=128k --direct=0 --ioengine=psync --iodepth=1 --numjobs=1 --runtime=10 --time_based --group_reporting
buffered-cheat: (g=0): rw=read, bs=(R) 128KiB-128KiB, (W) 128KiB-128KiB, (T) 128KiB-128KiB, ioengine=psync, iodepth=1
fio-3.38
Starting 1 process
Jobs: 1 (f=1): [R(1)][100.0%][r=5240MiB/s][r=41920 IOPS][eta 00m:00s]
buffered-cheat: (groupid=0, jobs=1): err= 0: pid=21305: Mon Dec 29 11:11:33 2025
read: IOPS=41000, BW=5120MiB/s (5370MB/s)(51200MiB/10001msec)
Ce que cela signifie : Ce débit est suspectement proche du comportement de la bande passante mémoire sur de nombreux systèmes. Félicitations, vous avez benchmarké le cache.
Décision : Ne publiez jamais des chiffres de lectures tamponnées comme « vitesse du disque » sans avertissements explicites et une méthodologie de cache froid.
Task 9: Measure latency percentiles (because averages are liars)
cr0x@server:~$ fio --name=latency-4k --filename=/dev/nvme0n1 --rw=randread --bs=4k --direct=1 --ioengine=io_uring --iodepth=32 --numjobs=4 --runtime=30 --time_based --group_reporting --lat_percentiles=1
latency-4k: (g=0): rw=randread, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=io_uring, iodepth=32
fio-3.38
Starting 4 processes
Jobs: 4 (f=4): [r(4)][100.0%][r=1680MiB/s][r=430k IOPS][eta 00m:00s]
latency-4k: (groupid=0, jobs=4): err= 0: pid=21601: Mon Dec 29 11:14:22 2025
read: IOPS=430k, BW=1680MiB/s (1762MB/s)(50400MiB/30001msec)
clat percentiles (usec):
| 1.00th=[ 66], 5.00th=[ 72], 10.00th=[ 76], 50.00th=[ 90]
| 90.00th=[ 120], 95.00th=[ 140], 99.00th=[ 210], 99.90th=[ 420]
| 99.99th=[ 920]
Ce que cela signifie : La médiane a l’air bonne, le P99 est correct, et le P99.99 est l’endroit où vivent les « pics mystérieux ». La latence tail compte pour les bases de données, les brokers de messages et tout ce qui a un commit synchrone.
Décision : Si P99.9/P99.99 est mauvais, ne validez pas le stockage sur la base du débit moyen. Corrigez la contention, réduisez la profondeur de file ou changez de média/contrôleur.
Task 10: Confirm you’re not CPU-bound during “storage” tests
cr0x@server:~$ mpstat -P ALL 1 5
Linux 6.12.0-amd64 (server) 12/29/2025 _x86_64_ (32 CPU)
11:16:01 AM CPU %usr %nice %sys %iowait %irq %soft %steal %idle
11:16:02 AM all 18.2 0.0 31.5 0.3 0.0 9.2 0.0 40.8
11:16:02 AM 7 11.0 0.0 72.0 0.0 0.0 12.0 0.0 5.0
Ce que cela signifie : Si un ou deux CPU sont bloqués à un fort %sys/%soft alors que d’autres sont au repos, vous pourriez être limité par une file IRQ unique ou le traitement des complétions.
Décision : Inspectez les affinités IRQ et la distribution multi-queue avant d’acheter « des disques plus rapides » pour résoudre un problème d’ordonnancement CPU.
Task 11: Watch real-time disk utilization and latency while fio runs
cr0x@server:~$ iostat -x 1 5
Linux 6.12.0-amd64 (server) 12/29/2025 _x86_64_ (32 CPU)
Device r/s rkB/s rrqm/s %rrqm r_await rareq-sz w/s wkB/s w_await wareq-sz aqu-sz %util
nvme0n1 98000.0 392000.0 0.0 0.0 0.11 4.00 0.0 0.0 0.00 0.00 12.1 98.5
Ce que cela signifie : r_await est une mesure grossière de latence. %util proche de 100 % indique une saturation du périphérique (mais sur NVMe c’est délicat ; c’est quand même utile).
Décision : Si fio annonce de hautes IOPS mais iostat montre une faible utilisation, quelque chose cloche (cache, mauvaise cible, ou fio ne fait pas ce que vous pensez).
Task 12: Inspect NVMe error log and firmware details (silent problems happen)
cr0x@server:~$ sudo nvme id-ctrl /dev/nvme0n1 | grep -E 'mn|fr|oacs|oncs'
mn : SAMSUNG MZVL21T0
fr : 3B2QGXA7
oacs : 0x17
oncs : 0x5f
cr0x@server:~$ sudo nvme error-log /dev/nvme0n1 | head
Error Log Entries for device:nvme0n1 entries:64
Entry[ 0]
.................
Ce que cela signifie : Les versions de firmware et les logs d’erreurs aident à corréler les « creux étranges du benchmark » avec des problèmes au niveau du périphérique. Certains disques ont des bizarreries connues sous des mixes de commandes spécifiques.
Décision : Si vous voyez des erreurs ou des timeouts, arrêtez. Les benchmarks ne réparent pas le matériel. Remplacez/mettre à jour le firmware, puis retestez.
Task 13: Precondition / steady-state test for SSDs (so you don’t test the showroom model)
cr0x@server:~$ fio --name=precondition --filename=/dev/nvme0n1 --rw=write --bs=1M --direct=1 --ioengine=io_uring --iodepth=32 --numjobs=1 --runtime=600 --time_based --group_reporting
precondition: (g=0): rw=write, bs=(R) 1024KiB-1024KiB, (W) 1024KiB-1024KiB, (T) 1024KiB-1024KiB, ioengine=io_uring, iodepth=32
fio-3.38
Starting 1 process
Jobs: 1 (f=1): [W(1)][100.0%][w=1450MiB/s][w=1450 IOPS][eta 00m:00s]
Ce que cela signifie : Les écritures soutenues poussent le disque vers un état plus réaliste : cache SLC épuisé, GC actif, caractéristiques thermiques visibles.
Décision : Si l’état stable vous importe, vous devez préconditionner, puis lancer votre test réel. Si vous ne vous intéressez qu’à la vitesse en rafale, étiquetez-le explicitement comme tel.
Task 14: Verify TRIM/discard behavior (especially for virtual disks and thin provisioning)
cr0x@server:~$ lsblk -D -o NAME,DISC-GRAN,DISC-MAX,DISC-ZERO
NAME DISC-GRAN DISC-MAX DISC-ZERO
sda 2M 2G 0
nvme0n1 4K 2G 0
Ce que cela signifie : Une granularité/max de discard non nulle indique que le périphérique supporte le discard. Qu’il soit activé dépend des options de montage et de votre environnement.
Décision : Si vous comptez sur la provision fine ou les performances SSD à long terme, assurez-vous que la stratégie discard/TRIM est intentionnelle (p. ex. fstrim périodique vs discard en ligne).
Concevoir des jobs fio qui ressemblent à votre charge
La manière la plus rapide de vous tromper est d’utiliser n’importe quelle commande fio trouvée dans un article de blog qui ne mentionnait pas fsync, les percentiles, ou la taille du fichier de test par rapport à la RAM.
Choisir le périmètre : bloc vs système de fichiers
- Tests sur bloc (
--filename=/dev/nvme0n1) pour caractériser le média/contrôleur et le surcoût du chemin I/O du noyau. Ils contournent les métadonnées et la fragmentation du système de fichiers. Idéal pour comparer des périphériques ; moins prédictif pour « app sur ext4 ». - Tests sur fichier (
--filename=/data/fio.test) incluent le comportement du système de fichiers. Bons pour les options de montage, l’effet du journal, et le comportement d’allocation. Mais ils sont plus faciles à mettre en cache par erreur.
Contrôler le caching explicitement
Pour la plupart des caractérisations de stockage :
- Utilisez
--direct=1. - Choisissez une taille de test qui n’est pas « trop petite pour rester chaude ». Si vous devez faire des tests sur fichier, prenez des tailles supérieures à la RAM quand vous testez les lectures.
- Notez si le système était idle ou partagé. « Le backup de quelqu’un d’autre » est une variable de performance.
Choisir un ioengine avec intention
Sur Debian 13, vous utiliserez couramment :
io_uring: moderne, efficace, bon pour les IOPS élevés. Peut exposer plus vite les problèmes CPU/IRQ.libaio: interface async classique pour O_DIRECT, encore largement utilisée et stable.psync: utile pour un comportement sync mono-thread ; ne l’utilisez pas par défaut « parce que ça marche ».
Adapter la concurrence et la profondeur de file à votre application
Ne choisissez pas iodepth=256 parce que ça a l’air sérieux. Mesurez le nombre d’E/S en attente que votre charge réelle génère, et émulez-le. Les bases de données ont souvent un nombre limité de lectures concurrentes, et la latence compte plus que le débit de remplissage de file maximal.
Utiliser des charges mixtes quand la réalité est mixte
Beaucoup de systèmes font 70/30 lectures/écritures, avec des tailles de blocs et des sémantiques de sync différentes. fio peut le faire ; c’est à vous de le spécifier.
Mesurer la queue, pas seulement la moyenne
Les percentiles ne sont pas un gadget. C’est la partie de la distribution où vos SLO meurent.
Blague courte #2 : Si votre rapport de benchmark ne contient que « MB/s », vous n’avez pas lancé un test — vous avez fait un contrôle d’ambiance.
Feuille de route de diagnostic rapide : trouver le goulet sans une semaine de débats
Voici la séquence que j’utilise quand un système « a un stockage lent » et que tout le monde est déjà émotionnellement attaché à une théorie.
Premier : prouvez quel chemin vous testez
- Confirmez le périphérique et les couches :
lsblk,findmnt, vérifiez LVM, mdraid, dm-crypt. - Confirmez que vous touchez la bonne cible : le filename fio pointe vers le bon device ou point de montage.
- Confirmez que le choix direct vs buffered correspond à la question.
Deuxième : déterminez si c’est saturation ou stalls
- Lancez fio avec percentiles et une concurrence modérée ; surveillez
iostat -x. - Si
%utilest élevé et que la latence augmente avec l’iodepth, vous saturez le périphérique ou son chemin de queue. - Si l’utilisation est faible mais la latence élevée, suspectez une contention ailleurs : CPU, IRQ, locks, système de fichiers, chiffrement ou scheduler mal configuré.
Troisième : séparez les goulets CPU/IRQ des goulets média
- Vérifiez la distribution CPU :
mpstat. - Vérifiez l’affinité IRQ et les queues NVMe si vous êtes approfondi (ce n’est pas un guide complet ici, mais le symptôme est généralement « un cœur en feu »).
- Essayez un autre ioengine ou réduisez l’iodepth pour voir si la latence tail s’améliore.
Quatrième : validez le comportement du périphérique sous charge soutenue
- Préconditionnez si SSD.
- Lancez des tests plus longs ; surveillez température et débit au fil du temps.
- Vérifiez SMART/NVMe logs pour les erreurs.
Cinquième : confirmez les sémantiques de système de fichiers et de durabilité
- Testez avec des patrons sync pertinents pour l’app (
fsync,fdatasyncou patrons spécifiques à la base de données). - Validez les options de montage ; évitez les pièges de type « nobarrier » sauf si vous avez une protection contre la perte d’alimentation et une bonne raison.
Erreurs courantes : symptômes → cause racine → correction
1) « Les lectures font 5 GB/s sur un SSD SATA »
- Symptôme : Lectures tamponnées montrent plusieurs GB/s ; lectures directes beaucoup plus basses.
- Cause racine : Le cache de page a satisfait les lectures après le warmup ; vous avez benchmarké la mémoire.
- Correction : Utilisez
--direct=1, une méthodologie de cache froid et/ou dépassez la RAM.
2) « Les écritures sont rapides, mais fsync de la base est lent »
- Symptôme : Le débit d’écriture séquentiel semble excellent ; latences explosives sous écritures synchrones.
- Cause racine : Le benchmark utilisait des écritures tamponnées asynchrones ; l’app attend des flush/FUA/commits de journal.
- Correction : Lancez fio avec des sémantiques sync : incluez
--fsync=1ou utilisez--rw=randwriteavec lesbsetiodepthpertinents, et mesurez les percentiles.
3) « Les IOPS d’écriture aléatoire s’effondrent après une minute »
- Symptôme : Bons chiffres initiaux ; performance soutenue chute fortement.
- Cause racine : Épuisement du cache SLC du SSD et garbage collection ; périphérique non préconditionné.
- Correction : Préconditionnez (écritures soutenues), puis testez l’état stable ; envisagez surprovisionnement ou SSD entreprise pour charges d’écriture lourdes.
4) « Les chiffres changent entre des runs identiques »
- Symptôme : Même commande fio, résultats différents d’un jour à l’autre.
- Cause racine : Charge de fond, throttling thermique, scaling CPU, disposition d’espace libre différente ou état du cache.
- Correction : Contrôlez l’environnement : isolez l’hôte, consignez les thermiques, fixez la fréquence CPU si pertinent, préconditionnez et exécutez plusieurs itérations en reportant la variance.
5) « Beaucoup d’IOPS mais latence tail terrible »
- Symptôme : Moyenne excellente ; P99.9 pénible.
- Cause racine : Profondeur de file excessive, contention dans la couche bloc, pics de GC firmware ou périphérique partagé.
- Correction : Réduisez
iodepth, alignez la concurrence sur l’app, testez en isolation, et concentrez-vous sur les percentiles plutôt que le pic.
6) « C’est rapide sur le device raw, lent sur filesystem »
- Symptôme : Tests /dev/nvme excellents ; tests fichier /data lents.
- Cause racine : Journalisation du système de fichiers, contention sur les métadonnées, petites tables d’inodes, options de montage ou fragmentation.
- Correction : Benchmarquez intentionnellement les deux couches ; tunez le système de fichiers pour la charge ; validez l’alignement et l’espace libre ; considérez les différences XFS/ext4 pour le parallélisme.
7) « Ajouter le chiffrement n’a presque rien changé au débit, mais la latence est bizarre »
- Symptôme : Le débit semble similaire ; la latence tail se dégrade.
- Cause racine : Chemin crypto lié au CPU lors des rafales ; mauvaise affinité CPU/NUMA ; overhead sur petites E/S.
- Correction : Mesurez l’usage CPU pendant fio ; considérez
--numjobset le pinning CPU ; assurez-vous d’AES-NI et d’un mode de chiffrement adapté ; gardez une iodepth réaliste.
Trois mini-récits d’entreprise depuis les tranchées du stockage
Mini-récit 1 : L’incident causé par une mauvaise hypothèse
Une entreprise SaaS de taille moyenne a mis à jour une flotte de nœuds de base de données. Nouveaux NVMe, noyau neuf, installations Debian fraîches. La feuille de benchmark interne était fantastique : lectures aléatoires à des centaines de milliers d’IOPS. Le plan de migration a été signé et planifié pour un weekend calme.
Après la bascule, la base était « correcte » pendant quelques heures. Puis la latence a grimpé pendant des opérations de maintenance routinières : rebuild d’index, autovacuum et arrière-plan d’écritures lourdes. L’app n’a pas planté. Elle est juste devenue difficilement utilisable. Les tickets de support sont arrivés en vagues, comme quand un système est vivant mais souffre.
L’hypothèse erronée était subtile : le benchmark avait utilisé des tests basés sur fichiers sur un système de fichiers vide avec I/O tamponnée, et la taille du test était suffisamment petite pour que le cache de page fasse le héros. En production, le jeu de données dépassait la mémoire et forçait des lectures réelles. Pire, la charge exigeait fréquemment un comportement de type fsync ; le benchmark ne l’avait pas modélisé.
La correction n’a pas été « acheter des NVMe plus rapides ». Ils ont relancé des tests avec --direct=1, une concurrence réaliste et des percentiles de latence ; puis ajusté les limites de concurrence I/O de la base pour coller au comportement du périphérique. Une fois qu’ils ont cessé de courir après le chiffre IOPS et visé la latence P99 sous une iodepth réaliste, le système s’est stabilisé. Les disques allaient bien. La narration du benchmark non.
Mini-récit 2 : L’optimisation qui s’est retournée contre eux
Une autre organisation dirigeait un grand cluster d’analytics. Quelqu’un a remarqué que leurs jobs nocturnes étaient plus lents après un rafraîchissement de stockage, malgré du matériel meilleur. Un ingénieur, de bonne volonté, a conclu que le goulot était « l’overhead du système de fichiers » et a basculé plusieurs charges vers des devices bruts, en augmentant la profondeur de file côté application à la manière de fio.
Au début, les tableaux de bord se sont améliorés. Le débit a augmenté, les jobs ont fini plus vite, et l’email hebdo contenait des chiffres qui donnaient confiance. Puis le revers : des pics de latence tail sont apparus aux heures de pointe, affectant des services non liés partageant le même matériel. Les NVMe étaient saturés par des files profondes, et les services sensibles à la latence se retrouvaient bloqués derrière un mur d’E/S de requêtes en attente.
L’« optimisation » a amélioré le débit en augmentant l’ordonnancement, mais a détérioré le système global en augmentant la contention et la latence tail. Elle a aussi réduit l’observabilité : contourner les sémantiques du système de fichiers signifiait perdre certaines protections et outils que l’équipe utilisait pour diagnostiquer.
La reprise a impliqué de plafonner la concurrence I/O par charge, de restaurer l’accès basé sur fichiers quand cela avait du sens opérationnel, et de traiter la profondeur de file comme un budget de ressource plutôt qu’un bouton « accélérer ». Ils ont conservé un bon débit, sans sacrifier le reste de la flotte.
Mini-récit 3 : La pratique ennuyeuse mais correcte qui a sauvé la mise
Une plateforme de services financiers avait une habitude qui paraissait peu sexy dans les revues d’architecture : chaque exécution de benchmark stockage était accompagnée d’un bref « enregistrement d’environnement ». Modèle du périphérique/firmware, version du noyau, scheduler, options de montage, mode de cache, taille du test et isolement du système. C’était de la paperasse, en somme.
Un trimestre, une régression de latence est apparue après une mise à jour de routine. L’équipe avait de bons instincts mais pas de coupable évident. Certains blâmaient le noyau. D’autres le nouveau pipeline batch. Quelques-uns voulaient tout restaurer et en finir.
Parce que les runs de benchmark étaient reproductibles et consignés, ils ont rapidement remarqué un petit mais significatif changement : le choix du scheduler différait entre deux images hôtes, et les nœuds affectés avaient un pattern d’IRQ différent. Le périphérique brut n’était pas « plus lent », mais le chemin CPU était plus bruité, et la latence tail pire sous le même profil fio.
La correction a été ennuyeuse : standardiser le scheduler pour cette classe de périphérique, appliquer une politique d’affinité IRQ cohérente, relancer la même suite fio comme gate, puis avancer. Pas d’héroïsme, pas d’appels nocturnes au constructeur. Le geste qui a sauvé la mise n’était pas un tweak miracle ; c’était une comparaison disciplinée.
Checklists / plan étape par étape
Plan étape par étape : un benchmark que vous pouvez défendre en postmortem
- Écrivez l’intention de la charge. « Nous avons besoin d’une latence P99.9 prévisible pour des lectures aléatoires 4k à concurrence X » est utilisable. « Nous avons besoin de disques rapides » ne l’est pas.
- Consignez l’environnement. Version Debian, noyau, version fio, modèle/firmware du périphérique, système de fichiers, options de montage, scheduler.
- Choisissez le périmètre du test. Device brut vs système de fichiers. Si système de fichiers, notez l’espace libre et le risque de fragmentation.
- Contrôlez le cache. Utilisez
--direct=1pour la caractérisation du périphérique. Si vous utilisez du buffered, justifiez-le et prouvez l’état du cache. - Préconditionnez si SSD et si l’état stable compte. Ne sautez pas cette étape si vous écrivez beaucoup en production.
- Utilisez plusieurs durées. 30 s rapides pour un sanity check, 10–30 minutes pour le comportement soutenu.
- Mesurez les percentiles. Toujours. Si vous ne vous préoccupez pas de la latence tail, vous avez soit de la chance soit un système batch que personne ne surveille.
- Exécutez avec une concurrence réaliste. Commencez par ce que l’app peut générer, puis explorez plus haut pour voir les marges et les points d’inflexion.
- Observez le système pendant l’exécution.
iostat -x,mpstat, thermiques périphériques. - Répétez et reportez la variance. Si les résultats varient énormément, vous n’avez pas un benchmark ; vous avez un mystère.
- Prendre une décision liée à la métrique. Par exemple : « Accepter si P99.9 < 2ms à iodepth=8, numjobs=4. »
Checklist rapide : « Suis-je en train de benchmarker la bonne chose ? »
- Ai-je choisi explicitement I/O direct vs tamponné ?
- La taille du test est-elle significative par rapport à la RAM ?
- Ai-je consigné les réglages de cache et les sémantiques de durabilité ?
- Ai-je collecté les percentiles de latence ?
- Ai-je adapté tailles de bloc et concurrence à la production ?
- Ai-je vérifié les thermiques et la saturation CPU ?
FAQ
1) Dois-je toujours utiliser --direct=1 sur Debian 13 ?
Pour la caractérisation du périphérique et la plupart des affirmations de « performance stockage » : oui. Utilisez l’I/O tamponnée seulement lorsque vous modélisez une application qui bénéficie intentionnellement du cache de page, et mesurez alors explicitement le comportement du cache.
2) io_uring est-il toujours meilleur que libaio ?
Non. io_uring est souvent plus rapide et plus scalable, mais il peut exposer des goulets CPU/IRQ et est sensible aux quirks du noyau/périphérique. Utilisez les deux lors du diagnostic ; standardisez sur l’un pour les tests de régression afin de garder la comparabilité.
3) Pourquoi mes chiffres fio sont excellents mais ma base est lente ?
Raisons courantes : fio a testé de l’I/O séquentielle alors que la base fait de l’aléatoire ; fio n’a pas inclus la sémantique sync/flush ; fio utilisait une profondeur de file différente de celle de la base ; le système de fichiers et la journalisation n’étaient pas représentés ; ou votre base est limitée par des locks CPU et pas du tout par l’I/O.
4) Quelle taille de bloc dois-je tester ?
Testez plusieurs tailles, mais commencez par ce que votre application utilise : 4k random read est un baseline classique ; 16k/32k peuvent compter pour certaines bases ; 128k–1M pour les scans séquentiels et backups. Si vous n’en choisissez qu’une, vous optimiserez par accident pour celle-là.
5) Combien de temps doivent durer les runs fio ?
Assez longtemps pour inclure le comportement qui vous importe. Pour une rafale : 30–60 s peut suffire. Pour le comportement steady-state SSD et la latence tail : minutes à dizaines de minutes, après préconditionnement si vous écrivez fortement.
6) Comment éviter de détruire des données en testant des devices bruts ?
Supposez que fio peut ruiner votre journée. Utilisez un périphérique de test dédié ou un LV LVM jetable. Vérifiez trois fois --filename. Utilisez --rw=read pour des tests non destructifs, et traitez les tests d’écriture comme destructifs à moins que vous ne cibliez un fichier de test sur un système de fichiers monté.
7) Pourquoi « QD32 » est-il un défaut si courant dans les exemples ?
C’est un artefact historique des systèmes de stockage où des files profondes aidaient à saturer contrôleurs et disques. Cela peut encore être utile pour mesurer le débit de pointe, mais cela représente souvent mal les charges sensibles à la latence et les applications mono-thread.
8) Dois-je vider les caches entre les runs ?
Si vous faites des tests tamponnés et tentez de simuler un cache froid, oui — prudemment. Dans des tests de type production, il est généralement préférable d’utiliser l’I/O directe et d’éviter tout le problème de « gestion de cache » à moins que le cache fasse partie du système que vous modélisez.
9) Mes résultats fio diffèrent entre tests fichier et bloc. Lequel est « réel » ?
Les deux sont réels ; ils mesurent des systèmes différents. Les tests bloc mesurent device + couche bloc du noyau ; les tests fichier incluent allocation de fichier, métadonnées et journalisation. Choisissez celui qui correspond à votre question, et ne les mélangez pas en une seule histoire.
10) Quel est le champ de sortie fio le plus utile ?
Les percentiles de latence (clat percentiles) sous une concurrence réaliste. Le débit est négociable ; la latence tail est l’endroit où la douleur visible par l’utilisateur se situe.
Conclusion : étapes suivantes qui réduisent réellement le risque
Si vous ne retenez qu’une leçon opérationnelle : arrêtez de débattre de « stockage rapide » et commencez à définir une « latence acceptable sous une concurrence réaliste ». Puis benchmarkez cela, en contrôlant le cache et en rapportant les percentiles.
Étapes concrètes suivantes :
- Choisissez deux ou trois profils fio qui correspondent aux formes d’E/S de production (lectures aléatoires 4k, mixte 70/30, pattern d’écriture synchrone si applicable).
- Standardisez la capture d’environnement (périphérique, firmware, noyau, scheduler, système de fichiers, options de montage, direct/buffered).
- Établissez une porte simple pass/fail basée sur la latence P99/P99.9, pas sur le MB/s maximal.
- Exécutez préconditionnement + tests d’état stable pour les systèmes SSD orientés écriture.
- Quand les résultats vous surprennent, suivez la feuille de route de diagnostic rapide au lieu de « tuner jusqu’à ce que le graphe ait belle allure ».
fio ne ment pas. Il est juste obéissant. Votre travail est de lui poser les bonnes questions — et de refuser les réponses flatteuses qui ne résistent pas au contact de la production.