Benchmarks de compression ZFS : mesurer des gains réels, pas un placebo

Cet article vous a aidé ?

Vous activez compression=lz4, lancez un « benchmark », et les chiffres semblent incroyables. Super — jusqu’à ce que votre hôte VM le plus chargé commence à hoqueter à 10h,
la latence de votre base de données devienne étrangement ponctuelle, et que la seule métrique que quelqu’un cite soit compressratio de zfs get.
La compression n’a pas « échoué ». Le benchmark, si.

La compression ZFS est généralement un plat gratuit, mais les systèmes de production ne servent pas un plat gratuit ; ils exécutent des SLA.
Si vous voulez des gains réels — pas un placebo — vous devez mesurer les bonnes choses, dans le bon ordre, sous la bonne charge, puis décider comme un adulte.

Ce que signifient des « gains réels » pour la compression ZFS

« La compression rend les choses plus petites » est vrai de la même manière que « l’exercice rend plus sain ». Oui, généralement. Aussi : cela dépend de ce que vous faites,
de la façon dont vous mesurez, et de ce sur quoi vous êtes déjà limité.

En production, la compression ZFS est réussie lorsqu’elle atteint un (ou plusieurs) de ces résultats dans vos contraintes réelles :

  • Réduction de la latence p95/p99 (parce que vous poussez moins d’octets à travers un périphérique lent ou un HBA saturé).
  • Augmentation du débit (parce que votre stockage est limité en bande passante et que la compression échange du CPU contre moins d’écritures).
  • Cache plus efficace (ARC/L2ARC contient plus de données logiques par octet physique quand les blocs se compressent bien).
  • Moindre amplification des écritures (moins de blocs physiques écrits pour le même changement logique).
  • Plus de capacité utilisable sans changer le domaine de défaillance ou la mise en pool.

La compression n’est pas réussie lorsque le seul « gain » est un nombre plus joli dans zfs get compressratio.
Si votre CPU devient le goulot d’étranglement, si la queue de latence s’allonge, ou si votre charge n’est déjà pas liée à l’I/O, vous pouvez tout à fait empirer la situation.

Le rôle d’un benchmark de compression n’est pas de déclarer un codec gagnant. Le rôle est de répondre à une question spécifique :
« Avec cette charge, sur ce matériel, sous ces contraintes, que changent la latence, le débit, le CPU et le volume d’écritures quand la compression change ? »

Faits et historique qui importent vraiment

Un peu de contexte vous empêche de faire du tuning en mode cargo-cult. Voici des faits concrets qui reviennent dans l’interprétation réelle des benchmarks :

  1. ZFS a été conçu pour garantir l’intégrité des données de bout en bout (sommations de contrôle partout), pas pour « un maximum d’IOPS à tout prix ». La compression vit dans ce modèle.
  2. LZ4 est devenu la recommandation par défaut dans de nombreux cercles OpenZFS parce qu’il est assez rapide pour que le CPU soit rarement le goulot pour les charges serveur typiques.
  3. OpenZFS moderne a ajouté ZSTD parce que les gens voulaient de meilleurs ratios avec des niveaux configurables ; ce n’est pas « juste un peu plus lent », c’est une gamme.
  4. La compression est par bloc et se produit avant l’écriture sur disque ; les blocs incompressibles sont stockés sans compression (généralement avec un petit surcoût).
  5. Recordsize compte parce que la compression opère sur des blocs de record ; les mêmes données peuvent se compresser différemment en 16K vs 128K.
  6. Le copy-on-write change l’économie : réécrire de petites parties de gros blocs peut causer plus d’attrition ; la compression peut réduire l’attrition physique si les données se compressent.
  7. L’ARC met efficacement en cache les données après compression : si les données se compressent 2:1, l’ARC peut contenir environ deux fois plus de contenu logique, modifiant le comportement de lecture.
  8. La déduplication et la compression ne font pas bon ménage par défaut : dedup augmente la pression sur les métadonnées et la RAM ; ajouter la compression aux pools avec dedup peut vous induire en erreur.
  9. Les vdevs spéciaux ont changé le comportement des métadonnées : les métadonnées et les petits blocs peuvent résider sur des médias plus rapides, donc « la compression a aidé la latence » peut en fait être « le vdev spécial vous a sauvé ».

Une idée paraphrasée à garder au mur : Tout échoue, tout le temps — Werner Vogels (idée paraphrasée). Le tuning de la compression n’échappe pas à la règle :
testez en gardant à l’esprit les modes de défaillance et la saturation, pas seulement des runs héroïques en labo vide.

Principes de benchmark (ou : comment ne pas se mentir)

1) Décidez ce que vous optimisez : queue de latence, débit ou capacité

Si l’entreprise se soucie de la latence p99, cessez d’afficher des graphiques de débit moyen. Si l’entreprise se soucie de la capacité, arrêtez de montrer « IOPS augmentés »
quand votre jeu de données est incompressible. Choisissez la cible, puis choisissez la mesure.

2) Séparez le comportement cache chaud du cache froid

Les benchmarks ZFS sans contrôle du cache sont essentiellement un test de Rorschach. L’ARC peut faire paraître la « performance disque » comme de la « performance RAM » pendant un moment.
Ce n’est pas mauvais — l’ARC est réel — mais c’est un système différent de « à quelle vitesse vont mes vdevs sous charge soutenue ? »

3) Mesurez explicitement le coût CPU

La compression n’est pas gratuite. LZ4 est peu coûteux, ZSTD peut être peu coûteux ou cher selon le niveau et les données. Si vous ne mesurez pas la saturation CPU,
vous attribuerez à tort les ralentissements à « l’overhead ZFS » ou au « réseau ».

4) Ne faites pas de benchmark sur des pools vides et n’annoncez pas la victoire

Le comportement de ZFS change à mesure que les pools se remplissent. Fragmentation, allocation de metaslabs et patterns d’écriture évoluent. Un pool à 20% est une créature différente d’un pool à 80%.
Si vous testez seulement sur du vide, vous testez un meilleur cas que vous ne reverrez jamais.

5) Utilisez des tailles d’I/O et une concurrence réalistes

Si votre charge réelle est des lectures aléatoires 8K à queue depth 32, ne lancez pas des écritures séquentielles 1M à queue depth 1 et appelez ça « semblable à une base de données ».
Les professionnels du stockage aiment les chiffres ; la réalité s’en fiche.

6) Contrôlez ce que ZFS peut légalement changer sous vous

recordsize, volblocksize (pour les zvols), paramètres sync, atime, stockage xattr et politiques de vdev spécial affectent tous le résultat.
Un benchmark de compression qui change quatre autres variables est un rituel superstitieux, pas une expérience.

Première blague (et oui, elle est courte) : un benchmark sans contrôles, c’est comme un exercice d’évacuation où on oublie de tirer l’alarme.
Tout le monde se sent préparé, jusqu’à ce que le bâtiment brûle.

Métriques qui comptent (et lesquelles sont de la vaine gloire)

Métriques de compression : utiles, mais limitées

  • compressratio : ratio au niveau dataset entre l’espace logique et physique. Utile pour la planification de capacité. Mauvais comme prédicteur de performance.
  • logicalused vs used : vous indique combien d’espace la compression économise. Toujours pas de la performance.
  • économies par algorithme (lz4 vs zstd-N) : pertinentes seulement si la forme de vos données est stable.

Métriques de performance : ce qui décide du débat

  • latence p95/p99 des lectures et écritures (réalité côté application).
  • temps de service des périphériques (zpool iostat -v colonnes de latence sont de l’or).
  • utilisation CPU et steal time (la compression brûle des cycles ; la virtualisation ajoute sa propre taxe).
  • octets écrits/lus au niveau vdev (la promesse « moins d’octets » de la compression se mesure ici).
  • taux de hit ARC en steady state (la compression peut augmenter la capacité effective du cache).

Métriques de vaine gloire (ou « métriques nécessitant du contexte »)

  • pic de débit sur un seul run : souvent juste un warmup de cache.
  • latence moyenne : les moyennes cachent la douleur des queues. La douleur des queues vous réveille la nuit.
  • IOPS sans taille de bloc : dénué de sens. 4K IOPS vs 128K IOPS sont des planètes différentes.
  • « capacité utilisée » de zpool list seule : ne révèle pas à quel point vous êtes fragmenté ou sujet à l’amplification d’écriture.

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

Voici les tâches que j’exécute réellement quand quelqu’un dit « La compression a rendu plus lent » ou « ZSTD niveau 19 est un gain gratuit ».
Chaque tâche inclut : la commande, ce que la sortie signifie, et la décision que vous prenez.

Task 1: Confirm dataset compression setting (and inherited surprises)

cr0x@server:~$ zfs get -o name,property,value,source compression tank/vm
NAME     PROPERTY     VALUE     SOURCE
tank/vm  compression  zstd      local

Signification : La compression est réglée sur zstd localement, pas héritée. Si elle était héritée, vous verriez inherited from ....
Décision : Pour un benchmark, assurez-vous que le dataset testé est explicitement réglé sur le codec que vous prétendez utiliser, et enregistrez la source.

Task 2: Check actual achieved ratio, not just “enabled”

cr0x@server:~$ zfs get -o name,property,value -p used,logicalused,compressratio tank/vm
NAME     PROPERTY      VALUE
tank/vm  used          214748364800
tank/vm  logicalused   322122547200
tank/vm  compressratio 1.50x

Signification : Logical est ~300G tandis que l’espace physique utilisé est ~200G, donc la compression fait son travail (~1.5x).
Décision : Si compressratio est ~1.00x, les tests de performance ne montreront pas de gains « moins d’octets » ; concentrez-vous alors sur l’overhead CPU et la latence.

Task 3: Check pool health and errors before blaming compression

cr0x@server:~$ zpool status -xv tank
pool 'tank' is healthy

Signification : Pas d’erreurs connues. Si vous voyez des erreurs de checksum ou des vdevs dégradés, votre « benchmark de compression » mesure la récupération d’erreur.
Décision : Réparez la santé du pool d’abord ; benchmarquer sur un pool dégradé, c’est chronométrer un marathon avec une chaussure en moins.

Task 4: Check pool fullness (your “empty pool win” detector)

cr0x@server:~$ zpool list -o name,size,alloc,free,capacity,fragmentation tank
NAME  SIZE  ALLOC   FREE  CAP  FRAG
tank  40T   28T     12T   70%  34%

Signification : 70% plein avec une fragmentation modérée. La performance à 70% peut différer fortement de celle à 20%.
Décision : Si votre benchmark a été réalisé sur un pool neuf, refaites-le sur un niveau de remplissage similaire ou au moins divulguez la différence.

Task 5: Observe vdev-level bandwidth and latency during load

cr0x@server:~$ zpool iostat -v tank 1
                              capacity     operations     bandwidth    total_wait     disk_wait
pool                        alloc   free   read  write   read  write   read  write   read  write
--------------------------  -----  -----  -----  -----  -----  -----  -----  -----  -----  -----
tank                         28T    12T    2200   1800  320M  290M    12ms  18ms     4ms  10ms
  raidz2-0                   28T    12T    2200   1800  320M  290M    12ms  18ms     4ms  10ms
    sda                          -      -     0    300    0B   48M       -     -     3ms  11ms
    sdb                          -      -     0    300    0B   48M       -     -     4ms  10ms
    sdc                          -      -     0    300    0B   48M       -     -     4ms  10ms
    sdd                          -      -     0    300    0B   48M       -     -     4ms  10ms
--------------------------  -----  -----  -----  -----  -----  -----  -----  -----  -----  -----

Signification : Regardez total_wait et disk_wait. Si disk_wait est élevé, le stockage est le goulot.
Si total_wait est élevé mais disk_wait est faible, vous pouvez faire face à de la mise en file en logiciel (CPU, verrous, sync, etc.).
Décision : Si la compression réduit la bande passante mais que la latence reste élevée, vous n’êtes pas limité par la bande passante ; cherchez le CPU, le sync, ou la fragmentation.

Task 6: Watch CPU saturation while toggling compression

cr0x@server:~$ mpstat -P ALL 1 5
Linux 6.6.0 (server) 	12/26/2025 	_x86_64_	(16 CPU)

12:10:01 AM  CPU   %usr %nice %sys %iowait %irq %soft %steal %idle
12:10:02 AM  all   62.1  0.0  18.4   0.9    0.0  1.1    0.0   17.5
12:10:02 AM    3   96.0  0.0   3.0   0.0    0.0  0.0    0.0    1.0

Signification : Certains cœurs sont presque saturés. La compression concentre souvent le travail ; un seul cœur surchauffé peut brider le débit.
Décision : Si activer un codec plus lourd pousse les cœurs à saturation, attendez-vous à des pics de latence. Baissez le niveau ZSTD, réduisez la concurrence ou ajoutez de la marge CPU.

Task 7: Check ARC behavior (are you benchmarking RAM?)

cr0x@server:~$ arcstat 1 3
    time  read  miss  miss%  dmis  dm%  pmis  pm%  mmis  mm%  arcsz     c
12:10:20   11K   120     1   110   1    10   0     0   0   48G   64G
12:10:21   12K   140     1   130   1    10   0     0   0   48G   64G

Signification : Le taux de miss est minime ; les lectures proviennent principalement de l’ARC. Super pour les applications, terrible si vous comparez la performance des vdevs.
Décision : Si vous avez besoin de chiffres de cache froid, concevez le test pour dépasser l’ARC ou utilisez des données uniques par run.

Task 8: Verify recordsize/volblocksize matches workload

cr0x@server:~$ zfs get -o name,property,value recordsize tank/vm
NAME     PROPERTY    VALUE
tank/vm  recordsize  128K

Signification : Les fichiers auront tendance à utiliser des records de 128K. Pour des images VM avec I/O aléatoire 4K, cela peut être un mauvais ajustement.
Décision : Pour les datasets VM, envisagez recordsize=16K (ou zvol volblocksize=8K/16K) et refaites le benchmark — la compression interagit avec ce paramètre.

Task 9: Confirm sync behavior (compression benchmark vs sync benchmark)

cr0x@server:~$ zfs get -o name,property,value sync tank/vm
NAME     PROPERTY  VALUE
tank/vm  sync      standard

Signification : Les écritures sync sont respectées. Si quelqu’un a mis sync=disabled, il n’a pas accéléré la compression ; il a supprimé la sécurité.
Décision : Si vous voyez sync=disabled dans des tests perf, écartez les résultats sauf si la production fonctionne délibérément de la même façon.

Task 10: Watch TXG and write throttling symptoms via iostat + latency

cr0x@server:~$ iostat -x 1 3
avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          55.40    0.00   17.10    1.20    0.00   26.30

Device            r/s     w/s   rMB/s   wMB/s  avgrq-sz avgqu-sz await  r_await  w_await  svctm  %util
nvme0n1          0.0   1200.0    0.0   280.0    478.0     8.2    6.9    0.0      6.9     0.8   92.0

Signification : Le périphérique est fortement utilisé, mais le temps de service est faible ; le queueing se construit. La compression peut réduire wMB/s, ce qui peut diminuer le queueing.
Décision : Si la compression fait baisser la bande passante et que await baisse aussi, c’est une victoire réelle. Si la bande passante baisse mais que await augmente, vous êtes limité par le CPU ou le sync.

Task 11: Inspect per-dataset logical vs physical writes (what you pay the disks)

cr0x@server:~$ zfs get -o name,property,value -p written,logicalwritten tank/vm
NAME     PROPERTY        VALUE
tank/vm  written         53687091200
tank/vm  logicalwritten  96636764160

Signification : Vous avez écrit logiquement ~90G mais physiquement ~50G. La compression réduit les écritures physiques.
Décision : Sur des systèmes sensibles à l’endurance SSD, c’est l’un des arguments les plus convaincants pour utiliser la compression même si la performance est « acceptable ».

Task 12: Confirm ashift and layout (benchmark results don’t travel well)

cr0x@server:~$ zdb -C tank | grep -E "ashift|vdev_tree" -n | head
12:        ashift: 12
45:    vdev_tree:

Signification : ashift=12 implique des secteurs 4K. Si vous comparez à un autre pool avec un ashift différent, vous ne comparez pas la compression ; vous comparez l’alignement.
Décision : Quand vous publiez des résultats internes, incluez ashift et le type de vdev. Sinon, la personne suivante répétera votre test et « découvrira » une physique différente.

Task 13: Compare algorithms safely on a test dataset

cr0x@server:~$ sudo zfs create -o compression=lz4 -o recordsize=128K tank/bench_lz4
cr0x@server:~$ sudo zfs create -o compression=zstd -o recordsize=128K tank/bench_zstd
cr0x@server:~$ zfs get -o name,compression,recordsize -r tank/bench_lz4 tank/bench_zstd
NAME            COMPRESSION  RECORDSIZE
tank/bench_lz4  lz4          128K
tank/bench_zstd zstd         128K

Signification : Deux datasets identiques sauf la compression. Voilà à quoi ressemble « benchmarker la compression ».
Décision : Isolez toujours la variable. Si vous changez aussi recordsize, vous benchmarkez un bundle.

Task 14: Measure real application-level latency (not just storage stats)

cr0x@server:~$ fio --name=randread --directory=/tank/bench_lz4 --rw=randread --bs=4k --iodepth=32 --numjobs=4 --size=8G --time_based --runtime=60 --group_reporting
randread: (g=0): rw=randread, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=libaio, iodepth=32
...
  read: IOPS=128k, BW=500MiB/s (524MB/s)(29.3GiB/60001msec)
  lat (usec): min=80, max=5400, avg=245.10, stdev=110.12
  clat percentiles (usec):
   |  50.00th=[  230],  90.00th=[  320],  95.00th=[  410],  99.00th=[  980],  99.90th=[ 2900]

Signification : Les percentiles font l’histoire. Si passer à ZSTD augmente le 99.9e percentile alors que la moyenne reste similaire, vous venez d’acheter de la latence en queue.
Décision : Utilisez p99/p99.9 comme critère de validation pour les charges sensibles à la latence (bases de données, hôtes VM).

Recettes FIO qui modélisent des charges réelles

FIO n’est pas « la vérité ». C’est un mensonge contrôlé que vous racontez au système pour voir comment il réagit. Racontez le bon mensonge.
Le but est d’approximer les patterns d’accès : aléatoire vs séquentiel, sync vs async, taille de bloc, concurrence, taille du working set et comportement d’écrasement.

Recette A : Mix VM-like lectures/écritures aléatoires 4K

cr0x@server:~$ fio --name=vm-mix --directory=/tank/bench_zstd --rw=randrw --rwmixread=70 --bs=4k --iodepth=32 --numjobs=8 --size=16G --time_based --runtime=120 --direct=1 --group_reporting
vm-mix: (g=0): rw=randrw, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=libaio, iodepth=32
...
  read: IOPS=84.2k, BW=329MiB/s (345MB/s)
  write: IOPS=36.1k, BW=141MiB/s (148MB/s)
  clat percentiles (usec):
   |  99.00th=[ 2200],  99.90th=[ 6500],  99.99th=[17000]

Comment l’utiliser : Lancez le même job sur bench_lz4 et bench_zstd, comparez p99.9 et CPU.
Décision : Si ZSTD améliore le débit mais aggrave le p99.9 significativement, LZ4 est le choix le plus sûr pour les mixes VM.

Recette B : Style base de données, écritures aléatoires 8K avec pression fsync

cr0x@server:~$ fio --name=db-sync --directory=/tank/bench_lz4 --rw=randwrite --bs=8k --iodepth=1 --numjobs=4 --size=8G --time_based --runtime=120 --fsync=1 --direct=1 --group_reporting
db-sync: (g=0): rw=randwrite, bs=(R) 8192B-8192B, (W) 8192B-8192B, (T) 8192B-8192B, ioengine=libaio, iodepth=1
...
  write: IOPS=4200, BW=32.8MiB/s (34.4MB/s)
  clat percentiles (usec):
   |  95.00th=[ 1800],  99.00th=[ 4200],  99.90th=[12000]

Comment l’utiliser : Ici, le sync et la qualité du SLOG dominent. La compression peut aider en réduisant les octets, mais le CPU peut aussi jouer.
Décision : Si le choix du codec ne bouge guère les chiffres, cessez de discuter de la compression et regardez le chemin de sync (SLOG, latence, queueing).

Recette C : Écritures séquentielles avec grands blocs (cibles de sauvegarde, logs)

cr0x@server:~$ fio --name=seqwrite --directory=/tank/bench_zstd --rw=write --bs=1M --iodepth=8 --numjobs=2 --size=64G --time_based --runtime=120 --direct=1 --group_reporting
seqwrite: (g=0): rw=write, bs=(R) 1024KiB-1024KiB, (W) 1024KiB-1024KiB, (T) 1024KiB-1024KiB, ioengine=libaio, iodepth=8
...
  write: IOPS=680, BW=680MiB/s (713MB/s)
  cpu          : usr=78.2%, sys=8.5%, ctx=12000, majf=0, minf=400

Comment l’utiliser : Les grandes écritures séquentielles peuvent être limitées par la bande passante. La compression peut réduire drastiquement les octets si les données sont de type log/texte.
Décision : Si le %usr CPU explose et que le BW cesse d’augmenter, baissez le niveau ZSTD ou utilisez LZ4. Si le débit augmente et que le CPU a de la marge, ZSTD peut valoir le coût.

Recette D : Test de sanity « données incompressibles » (détecter le placebo)

cr0x@server:~$ fio --name=incompressible --directory=/tank/bench_lz4 --rw=write --bs=128k --iodepth=16 --numjobs=4 --size=16G --time_based --runtime=60 --direct=1 --refill_buffers=1 --buffer_compress_percentage=0 --group_reporting
incompressible: (g=0): rw=write, bs=(R) 131072B-131072B, (W) 131072B-131072B, (T) 131072B-131072B, ioengine=libaio, iodepth=16
...
  write: IOPS=5200, BW=650MiB/s (682MB/s)

Comment l’utiliser : Si les données sont incompressibles, la compression ne devrait pas réduire beaucoup les octets et peut seulement coûter du CPU.
Décision : Si vous observez un « gain massif » sur des écritures incompressibles après activation de la compression, vous mesurez autre chose (cache, alignement, artefact de test).

Deuxième blague (la dernière, promis) : ZSTD level 19 sur un hyperviseur chargé, c’est comme donner un bureau debout à tout le monde au bureau.
La posture s’améliore ; les plaintes s’intensifient.

Playbook de diagnostic rapide

Quand quelqu’un dit « La compression a changé la performance », vous n’avez pas le temps d’une reconstitution de laboratoire d’une semaine. Faites du triage comme un SRE.
L’objectif est d’identifier rapidement la classe de goulot : disque, CPU, chemin de sync, ou illusion de cache.

Première étape : établissez si vous êtes lié à l’I/O ou au CPU

  1. Vérifiez la saturation CPU pendant la charge. Utilisez mpstat et cherchez des cœurs au max et un sys time en hausse.
    Si le CPU est saturé, le niveau de compression est un vrai réglage avec de vraies conséquences.
  2. Vérifiez l’utilisation et les temps d’attente des périphériques. Utilisez zpool iostat -v 1 et iostat -x 1.
    Si les disques sont saturés et que les temps d’attente augmentent avec la bande passante, vous êtes lié à l’I/O et la compression aide souvent.

Deuxième étape : déterminez si le benchmark n’est qu’un effet ARC

  1. Surveillez les misses ARC. Si miss% est minime lors des lectures, vous êtes principalement en RAM.
    L’accélération des lectures due à la compression peut être l’efficacité du cache, pas la vitesse du disque.
  2. Augmentez la taille du working set. Si votre dataset tient dans l’ARC, vous ne pouvez pas tirer de conclusions disque.

Troisième étape : confirmez que le chemin de sync/écriture n’est pas le vrai limiteur

  1. Vérifiez la propriété sync et le comportement sync de la charge. Si vous testez une base de données, le sync domine.
  2. Observez la latence pendant des écritures sync soutenues. La compression peut réduire les octets écrits, mais la latence SLOG et le comportement TXG peuvent toujours dominer.

Quatrième étape : validez que vous n’avez pas changé d’autres variables

  1. Enregistrez les propriétés du dataset : recordsize, atime, xattr, primarycache, logbias, redundant_metadata.
  2. Enregistrez les propriétés/mise en pool : ashift, RAIDZ vs mirrors, vdevs spéciaux, SLOG, L2ARC.

Erreurs courantes : symptôme → cause racine → correctif

1) « Compression activée, le débit a doublé » (mais seulement au second run)

Symptôme : Le premier run est lent, le second est fulgurant, la compression « gagne ».

Cause racine : ARC réchauffé. Vous avez benchmarké la RAM, pas les disques. La compression peut aussi augmenter l’efficacité de l’ARC, exagérant l’effet.

Correctif : Utilisez un working set plus grand que l’ARC, utilisez des noms de fichiers uniques par run, et comparez l’état steady-state après warmup. Suivez le miss% ARC.

2) « ZSTD est plus lent que LZ4, donc ZSTD est mauvais »

Symptôme : Queues de latence plus élevées et IOPS plus faibles après passage à ZSTD.

Cause racine : Saturation CPU ou goulot mono-cœur. Niveau ZSTD trop élevé pour la concurrence et la marge CPU disponible.

Correctif : Testez ZSTD à des niveaux plus bas ; mesurez le CPU. Si vous ne pouvez pas vous permettre le CPU, choisissez LZ4 et avancez.

3) « compressratio est 2.5x, donc on devrait obtenir 2.5x de performance »

Symptôme : Excellents ratios de compression, peu ou pas de changement de performance.

Cause racine : La charge n’est pas limitée par la bande passante ; elle est liée à la latence, au sync, ou au CPU ailleurs (checksums, métadonnées, verrous applicatifs).

Correctif : Utilisez les percentiles de latence et le temps d’attente vdev pour trouver le vrai limiteur. La compression réduit les octets, pas nécessairement la latence des IOPS.

4) « Nous avons benchmarké avec sync=disabled pour voir le max »

Symptôme : Chiffres d’écriture irréels, suivis de « la production ne correspond pas ».

Cause racine : Vous avez retiré les garanties de durabilité et changé entièrement le chemin d’écriture.

Correctif : Benchmarquez avec les mêmes sémanthiques sync que la production. Si vous devez tester sync=disabled, marquez-le « mode non sûr » et ne l’utilisez pas pour décider.

5) « La compression a rendu les snapshots coûteux »

Symptôme : La croissance d’espace des snapshots semble pire après changement de compression.

Cause racine : Mauvais alignement recordsize et patterns de réécriture. Grand recordsize + petites réécritures augmente l’attrition ; la compression change la disposition physique et peut altérer la fragmentation.

Correctif : Alignez recordsize/volblocksize sur la taille des réécritures. Pour images VM et bases de données, des blocs plus petits sont souvent plus stables.

6) « Nous avons activé ZSTD et maintenant le scrub est plus lent »

Symptôme : Le scrub prend plus de temps et entre en compétition avec la charge.

Cause racine : Le scrub lit et vérifie les blocs ; la décompression peut ajouter un overhead CPU pendant le scrub sur des systèmes chargés.

Correctif : Planifiez les scrubs en heures creuses, limitez leur impact et évitez les niveaux de compression élevés sur des systèmes sans marge CPU.

Trois mini-histoires d’entreprise issues des tranchées de la compression

Mini-histoire 1 : L’incident causé par une fausse hypothèse

Une équipe a déployé un nouveau cluster VM et standardisé sur ZSTD parce que les chiffres de staging semblaient excellents. Ils ont fait ce que tout le monde fait :
copier une image golden, lancer quelques tests de copie de fichiers, puis faire une petite célébration.

L’hypothèse erronée était subtile : ils ont supposé que « se compresse bien » équivaut à « tourne vite ». Leur image golden était principalement des fichiers OS — fortement compressibles,
majoritairement en lecture et très favorables au cache. La production était un parc mixte avec des services générant beaucoup de logs et quelques middleware bavards effectuant des petites écritures constantes.

Lundi matin, le cluster a atteint la charge de pointe. Les graphes CPU sont partis verticalement, mais pas de la bonne manière. La latence a grimpé, puis a varié, puis a grimpé de nouveau.
L’équipe hyperviseur a accusé le réseau. L’équipe réseau a accusé le stockage. Le stockage a blâmé des « voisins bruyants ».
Pendant ce temps, la sonnerie d’astreinte ne s’est souciée ni des organigrammes ni des accords.

Le vrai problème : ZSTD à un niveau agressif compressait un flot massif de petites écritures aléatoires. Le pool ne saturait pas les disques ; il saturait le CPU.
La charge avait suffisamment de concurrence pour que les threads de compression deviennent un goulot, et la latence en queue a puni tout le monde.

Le correctif a été ennuyeux : repasser à LZ4 pour les datasets VM et garder ZSTD pour les datasets de sauvegarde/archivage où le débit importait plus que la micro-latence.
Ils ont aussi modifié leur suite de benchmarks pour inclure un job FIO VM-mix avec p99.9 comme seuil.

Mini-histoire 2 : L’optimisation qui a mal tourné

Une autre entreprise a essayé « d’optimiser » la compression en la désactivant pour leur dataset de base de données. Le raisonnement semblait propre :
« Les pages DB sont déjà compactes ; la compression gaspille du CPU. » Ils avaient quelques graphiques montrant une légère baisse CPU.

Un mois plus tard, les indicateurs d’usure SSD ont commencé à bouger plus vite que prévu, et les fenêtres de maintenance nocturnes ont augmenté.
Rien n’était en feu, mais tout semblait plus lourd — comme si le pool avait pris du poids.

L’enquête a montré que bien que les pages DB ne soient pas extrêmement compressibles, elles l’étaient suffisamment pour réduire significativement les écritures physiques.
Désactiver la compression a augmenté les octets écrits, ce qui a augmenté l’amplification d’écriture du périphérique et poussé le pool plus près de son plafond de bande passante.
L’économie CPU était réelle, mais ce n’était pas la ressource limitante.

Pire encore, désactiver la compression a réduit la capacité effective de l’ARC pour ce dataset. L’amplification de lecture a augmenté,
et la base a commencé à manquer le cache plus fréquemment pendant les périodes de churn.

Ils sont revenus à LZ4, pas ZSTD, parce que LZ4 apportait la plupart de la réduction d’écritures avec un coût CPU minimal.
L’optimisation a échoué car elle optimisait la mauvaise ressource. Le stockage est un système ; on ne peut pas tuner une partie en isolation.

Mini-histoire 3 : La pratique ennuyeuse mais correcte qui a sauvé la mise

Une équipe plateforme maintenait une règle simple : toute affirmation de performance nécessite un « manifeste de benchmark ».
Pas un document gigantesque — juste une checklist attachée au ticket : matériel, mise en pool, ashift, propriétés dataset, niveau de remplissage, profil de workload,
position warm/cold cache, et commandes exactes utilisées.

Pendant une vague de réduction des coûts, quelqu’un a proposé de passer tous les datasets à ZSTD à un niveau élevé pour « économiser du stockage ».
La demande venait avec une présentation et un seul chiffre : « ratio de compression 2.1x ».
Le manifeste les a forcés à inclure la latence p99 sous charge mixte et l’utilisation CPU.

Le manifeste a aussi forcé un test sur un pool rempli au même niveau que la production et avec la même disposition de vdevs spéciaux.
C’est là que la surprise est sortie : la marge CPU était suffisante sur la moitié des hôtes mais tendue sur les plus anciens, qui seraient le maillon faible.

Ils ont publié un changement ciblé : ZSTD pour les datasets d’archivage et de logs, LZ4 pour les services sensibles à la latence,
plus une note explicite « ne pas dépasser ZSTD level X sur la classe d’hôte Y » dans la gestion de configuration.

Rien de dramatique ne s’est produit après. C’est le but. La pratique ennuyeuse les a préservés d’un incident très excitant.

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

Étape par étape : un benchmark de compression que vous pouvez défendre en revue de changement

  1. Clonez les hypothèses d’environnement. Même type de pool, niveau de remplissage similaire, classe matérielle similaire, même OS/version OpenZFS.
  2. Créez deux datasets ne différant que par compression (et éventuellement le niveau ZSTD).
  3. Verrouillez les propriétés : recordsize/volblocksize, sync, atime, xattr, primarycache, logbias (si pertinent).
  4. Choisissez 2–3 modèles de workload qui représentent la réalité : mix VM, écritures DB sync, ingestion/backup séquentiel.
  5. Définissez les critères de succès avant d’exécuter : seuil de latence p99.9, débit minimum, utilisation CPU maximale, exigence de gain de capacité.
  6. Exécutez un warmup puis des fenêtres de mesure en steady-state. Capturez p95/p99/p99.9, CPU et wait vdev.
  7. Répétez les runs (au moins 3) et comparez la variabilité. Si la variabilité est énorme, votre environnement n’est pas assez contrôlé.
  8. Enregistrez un manifeste : commandes exactes, propriétés dataset, statut pool, niveau de remplissage et goulot observé.
  9. Décidez par classe de dataset (VM, DB, logs, sauvegardes), pas « un codec pour les gouverner tous ».

Checklist opérationnelle : avant de changer la compression en production

  • Confirmez la marge CPU à la charge de pointe (pas à 2h du matin).
  • Confirmez la compressibilité de la charge (échantillonnez des données réelles, pas des zéros synthétiques).
  • Confirmez le plan de rollback : les changements de propriété sont faciles ; revenir d’une régression de perf dans un cluster chargé ne l’est pas.
  • Confirmez la surveillance : dashboards p99 latence, CPU et temps d’attente des périphériques existent et sont surveillés.
  • Confirmez le rayon d’impact : changez d’abord une classe de dataset ou un groupe d’hôtes.

FAQ

1) Dois-je activer la compression sur ZFS par défaut ?

Oui — LZ4 sur la plupart des datasets généralistes est le choix sensé par défaut. Il économise généralement de l’espace et réduit les écritures physiques avec un coût CPU minimal.
Traitez « pas de compression » comme une exception que vous justifiez par des mesures.

2) ZSTD est-il toujours meilleur que LZ4 ?

Non. ZSTD peut fournir de meilleurs ratios, mais il peut coûter plus de CPU et aggraver la queue de latence sous concurrence d’écritures petites/aléatoires.
Utilisez ZSTD là où l’économie de capacité compte et où vous avez de la marge CPU ; gardez LZ4 là où la latence est reine.

3) Pourquoi compressratio semble bon mais la performance n’améliore pas ?

Parce que votre goulot peut ne pas être la bande passante. La latence, les sémanthiques de sync, la contention métadonnées, le CPU ou les verrous applicatifs peuvent dominer.
La compression réduit les octets ; elle n’enlève pas magiquement le reste de la pile.

4) La compression peut-elle améliorer la lecture ?

Oui, surtout si vous êtes limité par la bande passante ou par le cache. Si des blocs compressés signifient moins d’octets depuis le disque, les lectures peuvent s’accélérer.
De plus, l’ARC peut contenir plus de données logiques lorsque les blocs se compressent bien.

5) Dois-je recomprimer les anciennes données après avoir changé la propriété ?

La compression ZFS s’applique aux nouveaux blocs écrits. Les blocs existants gardent leur état de compression jusqu’à réécriture.
Si vous devez recomprimer d’anciennes données, réécrivez-les habituellement (p.ex. send/receive vers un nouveau dataset ou copie interne).

6) Benchmarker avec dd if=/dev/zero est-il valide ?

C’est valide pour tester le comportement de compression maximum sur des données trivialement compressibles, mais c’est un très mauvais proxy pour la production.
Cela peut produire des résultats spectaculaires mais trompeurs.

7) Comment choisir le niveau ZSTD ?

Commencez sur la valeur par défaut zstd (qui implique un niveau raisonnable selon l’implémentation) ou choisissez explicitement un niveau bas (p.ex. zstd-3 ou zstd-5)
pour les systèmes sensibles à la performance. Augmentez seulement si vous pouvez prouver la marge CPU et obtenir des gains de capacité significatifs.

8) Le recordsize affecte-t-il les résultats de compression ?

Oui. La compression se fait par bloc de record. Un recordsize plus grand peut améliorer le ratio sur des données séquentielles volumineuses mais peut nuire aux workloads à réécritures fréquentes.
Ajustez d’abord recordsize pour la charge, puis évaluez les niveaux de compression.

9) Pourquoi mes résultats FIO varient-ils autant d’un run à l’autre ?

Causes communes : changements d’état ARC, scrubs/resilvers en arrière-plan, autres locataires, dérive du niveau de remplissage du pool, et scaling de fréquence CPU.
Si la variabilité est élevée, votre environnement n’est pas contrôlé ; ne prenez pas de décisions irréversibles dessus.

10) La compression interagit-elle avec le chiffrement ?

Oui. La compression se produit avant le chiffrement dans la pipeline ZFS (donc elle peut encore compresser), mais le chiffrement ajoute aussi un coût CPU.
Benchmarquez avec les deux activés si la production utilise les deux, car la contention CPU est cumulative.

Prochaines étapes à faire cette semaine

Si vous voulez des résultats de compression que vous pouvez défendre en revue de conception (et ne pas regretter en revue d’incident), faites ceci :

  1. Créez deux datasets de test identiques sauf la compression (lz4 vs zstd à un niveau choisi).
  2. Exécutez trois profils FIO : VM mix (4K randrw), sync-heavy (8K + fsync), ingestion séquentielle (1M writes).
  3. Pour chaque run, capturez : latence p99/p99.9, CPU (mpstat), wait vdev (zpool iostat), miss% ARC (arcstat), et écritures physiques vs logiques.
  4. Décidez par classe de dataset. Ne standardisez pas sur un codec juste parce qu’il gagne un test synthétique.
  5. Déployez progressivement avec des barrières de surveillance. Si p99.9 évolue mal, revenez en arrière rapidement et sans honte.

La compression n’est pas une religion. C’est un compromis. Mesurez le compromis honnêtement et vous obtiendrez les économies d’espace et les gains de performance promis — sans le placebo.

← Précédent
Ubuntu 24.04 « Réseau inaccessible » : sérum de vérité de la table de routage (et correctifs)
Suivant →
ZFS vs btrfs : instantanés, RAID, récupération — lequel mord le moins ?

Laisser un commentaire