Si votre système de stockage paraît rapide pour le streaming de gros fichiers mais commence à tousser dès que quelqu’un lance
find sur un arbre de petits fichiers, vous rencontrez l’ennemi le plus ancien du stockage d’entreprise : les métadonnées et
les E/S de petits blocs. ZFS est excellent pour protéger les données, mais il ne change pas la physique — les lectures aléatoires sur des médias lents restent
des lectures aléatoires sur des médias lents. La bonne nouvelle est que ZFS vous donne une issue : les special vdevs et les
special small blocks.
Bien fait, c’est une de ces optimisations rares qui ressemble à de la triche : les traversées de répertoires s’accélèrent, les
checkouts git ne traînent plus, l’extraction d’images de conteneurs devient plus réactive, et les tickets « pourquoi ls est lent ? »
disparaissent. Mal fait, c’est aussi un moyen spectaculaire d’apprendre combien les métadonnées sont cruciales pour la survie de votre pool.
Ce guide explique comment le faire correctement — en production, avec les contraintes qui surviennent quand ça sonne à l’astreinte.
Ce que « special small blocks » signifie vraiment
Dans ZFS, « special small blocks » est un raccourci pour une politique : stocker les blocs en dessous d’un seuil de taille choisi sur un
special vdev — typiquement SSD ou NVMe — tout en laissant les données volumineuses sur les vdevs principaux (souvent HDD).
Ce n’est pas un cache. Ce n’est pas du best-effort. Ces blocs y vivent.
Le special vdev a déjà une tâche même sans seuil : il peut héberger les métadonnées. Les métadonnées incluent des éléments comme
les blocs indirects, les dnodes, les entrées de répertoire et d’autres structures qui rendent les opérations du système de fichiers possibles.
Quand vous réglez special_small_blocks sur un dataset, vous étendez cette tâche : les petits blocs de données de fichier
atterrissent aussi sur le special vdev.
La magie n’est pas que le SSD soit « plus rapide ». La magie est que les charges de petits fichiers sont dominées par des E/S aléatoires, et
les E/S aléatoires sur HDD sont essentiellement un test de sympathie mécanique que vous perdrez. Si vous pouvez diriger ces lectures vers des médias à faible latence,
le caractère de la charge change : moins de seeks, moins de blocages IOPS, et une file d’attente plus courte au mauvais endroit.
En une phrase : les special vdevs permettent à ZFS de séparer « ce qu’il faut trouver vite » de « ce qui est gros à lire ».
Blague n°1 : Pensez aux special small blocks comme si vous donniez une voiture de sport à vos métadonnées. Souvenez-vous simplement : si vous grillez la voiture,
vous ne ratez pas seulement une réunion — vous oubliez où est le bureau.
Faits et contexte intéressants (pourquoi ça existe)
Voici quelques éléments d’histoire et de contexte concrets qui comptent quand vous prenez des décisions de conception :
-
ZFS est né avec l’hypothèse de RAM coûteuse et de disques plus lents. L’ARC a été conçu pour être malin au sujet du cache parce que « ajouter de la RAM »
n’était pas toujours la solution dans les premières déploiements. -
La « classe d’allocation spéciale pour métadonnées » est arrivée plus tard. Au départ ZFS n’avait pas de moyen propre
pour épingler les métadonnées sur un autre type de vdev ; les special vdevs ont ajouté une classe explicite pour ça. -
La performance sur petits fichiers a toujours fait la réputation d’un système de fichiers. Les utilisateurs pardonnent
moins les flux lents que les sauvegardes lentes — mais leslslents et les builds lents provoquent une colère immédiate. -
Les tailles de secteur 4K ont changé les règles du jeu. « Les petits fichiers » se traduisent encore en plusieurs
blocs de métadonnées, et des lectures alignées sur 4K impliquent que vous payez une taille I/O minimale même pour un petit contenu. -
Le copy-on-write rend les motifs d’écritures aléatoires plus courants. ZFS doit écrire de nouveaux blocs et mettre à jour
les pointeurs ; il évite les mises à jour in-place, ce qui est excellent pour la cohérence et les snapshots mais peut augmenter l’agitation des métadonnées. -
NVMe n’a pas seulement ajouté de la vitesse ; il a changé le comportement des files d’attente. L’écart de latence entre NVMe et HDD
signifie que déplacer une petite fraction des E/S (métadonnées + petits blocs) peut changer dramatiquement les temps de réponse bout en bout. -
« Special » n’est pas « cache ». L2ARC est un cache et peut être reconstruit ; l’allocation vers special vdev est
authoritative. Ce seul fait guide la plupart des décisions de risque opérationnel. -
Les dnodes sont un vilain silencieux. Les dnodes sont des métadonnées par objet et peuvent devenir un hotspot pour des arbres à millions de fichiers ;
placer les dnodes sur le special vdev peut être un énorme gain. -
La comptabilité d’espace compte plus que vous ne le pensez. Si le special vdev se remplit, ZFS ne « déverse » pas
les métadonnées sur les HDD de façon amicale ; vous pouvez vous retrouver avec des échecs d’allocation et des choix opérationnels désagréables.
Comment ça marche sous le capot (suffisant pour bien décider)
The special vdev allocation class
Les pools ZFS sont composés de vdevs. Un « special vdev » est un vdev assigné à une classe d’allocation spéciale. ZFS utilise
des classes d’allocation pour décider où placer les blocs. Lorsqu’un pool dispose d’un special vdev, ZFS peut y allouer certains types de blocs —
en particulier les métadonnées. Si vous activez le placement des petits blocs, les petits blocs de données y vont aussi.
Qu’est-ce qui compte comme « métadonnées » ? C’est plus que des répertoires. Cela inclut l’arbre des blocs indirects qui pointent vers les blocs de contenu de fichier,
et ces blocs indirects sont accédés pendant les lectures. Les mettre sur SSD peut même bénéficier aux lectures de gros fichiers — particulièrement pour des fichiers fragmentés
ou des arbres de blocs profonds.
special_small_blocks : le réglage politique
special_small_blocks est une propriété par dataset. Si elle est réglée sur une taille (par exemple 16K), alors les blocs
de données de fichier jusqu’à cette taille sont alloués sur le special vdev.
Trois vérités opérationnelles :
- Ce n’est pas rétroactif. Les blocs existants restent là où ils sont sauf s’ils sont réécrits.
-
Ça interagit avec recordsize. Si votre recordsize est 128K et que votre charge écrit beaucoup de petits fichiers, vous ne stockez
que la portion utilisée, mais le comportement d’allocation et de compression peut importer. -
Ça peut augmenter rapidement la consommation du special vdev. Les datasets de petits fichiers sont souvent « entièrement petits blocs ».
C’est le but, mais aussi le piège.
Dnodes, dnodesize et « métadonnées qui se comportent comme des données »
ZFS stocke les métadonnées par fichier dans les dnodes. Quand les attributs étendus et les ACL sont lourds (pensez : partages corporatifs avec audit agressif et clients Windows),
les dnodes peuvent gonfler. Si les dnodes ne tiennent pas dans le « bonus buffer », ZFS peut déverser des métadonnées dans des blocs séparés, augmentant les lectures.
Des réglages comme xattr=sa et dnodesize=auto peuvent réduire les déversements et amplifier l’avantage du special vdev.
Pourquoi ça ressemble à un « turbo »
Pour de nombreuses charges de petits fichiers, le chemin critique est :
- Rechercher les entrées de répertoire (E/S de métadonnées).
- Lire les attributs de fichier (plus d’E/S de métadonnées).
- Lire les petites données de fichier (petites lectures aléatoires).
Si ces étapes se passent sur HDD, vous êtes limité par les seeks et la profondeur de file d’attente. Si elles se passent sur NVMe, vous êtes limité par
le CPU, les taux de hit ARC, et le modèle de threads de l’application. C’est un problème différent, et généralement meilleur.
Blague n°2 : Les HDD sont excellents pour enseigner la patience. Malheureusement, votre pipeline CI ne s’était pas inscrit au cours.
Charges de travail qui gagnent (et celles qui ne gagnent pas)
Meilleurs candidats
Les special small blocks brillent quand votre working set inclut beaucoup de petits fichiers et des opérations lourdes en métadonnées :
- Arbres source et artefacts de build (millions de petits fichiers, traversées répétées, beaucoup d’appels stat).
- Couches d’images de conteneurs (tempêtes d’unpack, nombreux petits fichiers, extraction parallèle).
- Workloads de type Maildir (beaucoup de petits messages en tant que fichiers individuels).
- Hébergement web et contenu CMS (milliers de petits fichiers PHP, plugins, vignettes, churn de métadonnées).
- Partages corporatifs avec arbres profonds (navigation de répertoire intensive, vérifications ACL, clients Windows).
- Dépôts de sauvegarde avec nombreux petits chunks (dépend de l’outil ; certains créent beaucoup de petits objets).
Résultats mitigés
Images VM et bases de données peuvent bénéficier indirectement parce que les métadonnées et blocs indirects sont plus rapides,
mais elles sont généralement dominées par des E/S aléatoires moyennes à grandes et par le comportement des écritures synchrones. Si vous cherchez à corriger
la latence d’une base, vous devez probablement parler de SLOG, recordsize, ashift et tuning applicatif en premier.
Mauvais candidats
Si votre charge est principalement de grosses lectures/écritures séquentielles et que vous avez déjà un prefetch sain et un recordsize large,
les special small blocks ne changeront pas la donne. Vous pouvez néanmoins vouloir mettre les métadonnées sur special pour des opérations de gestion plus réactives,
mais n’attendez pas de miracles pour des flux séquentiels.
Concevoir un special vdev : redondance, dimensionnement et domaine de panne
La règle non négociable : redondance ou regret
Le special vdev fait partie du pool. S’il meurt et que vous n’avez pas de redondance, votre pool n’est pas « dégradé » de façon sympathique.
Vous pouvez perdre le pool parce que les métadonnées peuvent manquer. Traitez les special vdevs comme du stockage haut de gamme, pas comme des accélérateurs jetables.
En pratique, cela signifie mirror au minimum, et dans certains environnements RAIDZ (pour la classe special) si vous pouvez tolérer l’amplification d’écritures
et voulez l’efficacité de capacité. Les mirrors sont courants parce qu’ils gardent la latence faible et simplifient les temps de rebuild.
Dimensionnement : la partie que tout le monde sous-estime
Le dimensionnement du special vdev est là où le projet « turbo » réussit ou devient un générateur d’incidents trimestriels. Vous devez estimer :
- L’empreinte des métadonnées pour les données existantes et futures.
- L’empreinte des petits fichiers sous votre seuil
special_small_blocks. - La croissance due aux snapshots (métadonnées et petits blocs qui peuvent se multiplier avec le churn).
- De l’espace libre pour la performance et la santé de l’allocateur.
Un modèle mental pratique : si vous stockez des millions de petits fichiers, les « données » peuvent être petites, mais le
nombre d’objets rend les métadonnées non triviales. Un arbre avec 50 millions de fichiers peut faire « seulement » quelques téraoctets, et
rester une machine à métadonnées.
Un autre modèle pratique : le special vdev est là où vit le cerveau de votre système de fichiers. Ne construisez pas un cerveau avec les plus petites pièces que vous avez trouvées dans le placard des achats.
Choisir un seuil
Les seuils typiques sont 8K, 16K, 32K, parfois 64K. La bonne valeur dépend de :
- Votre distribution de tailles de fichiers.
- À quelle vitesse vous pouvez augmenter la capacité special.
- Que les « petits fichiers » du dataset soient vraiment sensibles à la latence.
Commencez conservateur. Vous pouvez augmenter le seuil plus tard ; le réduire ne déplace pas les blocs existants.
Compression et special
La compression change la taille effective des blocs stockés. Si vous avez compression=zstd, de nombreux fichiers « moyens » peuvent se compresser
en dessous de votre seuil et atterrir sur special. Parfois c’est excellent, parfois c’est une surprise.
Sélection des dispositifs et endurance
Les special vdevs subissent beaucoup d’écritures aléatoires petites, surtout pour les charges lourdes en métadonnées et les snapshots. Choisissez des dispositifs
avec une endurance décente et une protection contre la perte de puissance si votre environnement l’exige. Les NVMe grand public peuvent fonctionner en labo ;
en production c’est souvent un ticket de support au ralenti.
Implémentation : pas à pas avec commandes réelles
Les commandes ci‑dessous supposent OpenZFS sur Linux. Si vous êtes sur FreeBSD, les concepts et la plupart des commandes sont les mêmes,
mais les chemins de périphérique et certains outils diffèrent. Remplacez les noms de pool/dataset et les identifiants de périphériques pour correspondre à votre système.
Task 1: Confirm pool topology and current health
cr0x@server:~$ sudo zpool status -v
pool: tank
state: ONLINE
scan: scrub repaired 0B in 0 days 02:11:32 with 0 errors on Sun Dec 22 03:15:12 2025
config:
NAME STATE READ WRITE CKSUM
tank ONLINE 0 0 0
raidz2-0 ONLINE 0 0 0
/dev/disk/by-id/... ONLINE 0 0 0
/dev/disk/by-id/... ONLINE 0 0 0
/dev/disk/by-id/... ONLINE 0 0 0
/dev/disk/by-id/... ONLINE 0 0 0
/dev/disk/by-id/... ONLINE 0 0 0
/dev/disk/by-id/... ONLINE 0 0 0
errors: No known data errors
Interprétation : confirmez qu’il n’y a pas de classe special existante et que le pool est stable. N’ajoutez pas de complexité structurelle
à un pool déjà malade.
Task 2: Identify candidate SSD/NVMe devices by stable IDs
cr0x@server:~$ ls -l /dev/disk/by-id/ | grep -E 'nvme|ssd|ata' | head
lrwxrwxrwx 1 root root 13 Dec 24 09:10 nvme-SAMSUNG_MZVLB1T0HBLR-00000_S4EWNX0R123456 -> ../../nvme0n1
lrwxrwxrwx 1 root root 13 Dec 24 09:10 nvme-SAMSUNG_MZVLB1T0HBLR-00000_S4EWNX0R789012 -> ../../nvme1n1
Interprétation : utilisez /dev/disk/by-id pour éviter les surprises de renumérotation des périphériques après des redémarrages ou des swaps HBA.
Task 3: Check ashift and general pool properties
cr0x@server:~$ sudo zdb -C tank | grep -E 'ashift|name'
name: 'tank'
ashift: 12
Interprétation : ashift=12 implique des secteurs 4K, ce qui est bon. Les special vdevs doivent aussi être alignés de façon appropriée ;
vous ne voulez généralement pas de mélange qui crée une amplification d’écritures inutile.
Task 4: Add a mirrored special vdev (metadata class)
cr0x@server:~$ sudo zpool add tank special mirror \
/dev/disk/by-id/nvme-SAMSUNG_MZVLB1T0HBLR-00000_S4EWNX0R123456 \
/dev/disk/by-id/nvme-SAMSUNG_MZVLB1T0HBLR-00000_S4EWNX0R789012
cr0x@server:~$ sudo zpool status tank
pool: tank
state: ONLINE
config:
NAME STATE READ WRITE CKSUM
tank ONLINE 0 0 0
raidz2-0 ONLINE 0 0 0
/dev/disk/by-id/... ONLINE 0 0 0
...
special
mirror-1 ONLINE 0 0 0
nvme-SAMSUNG_MZVLB1T0HBLR...123456 ONLINE 0 0 0
nvme-SAMSUNG_MZVLB1T0HBLR...789012 ONLINE 0 0 0
Interprétation : le pool a maintenant une classe special. À partir de ce moment, les allocations de métadonnées peuvent y atterrir (selon les flags de fonctionnalité et le type de bloc).
Le destin du pool est désormais lié à ce mirror. Surveillez-le sérieusement.
Task 5: Verify special vdev allocation is recognized
cr0x@server:~$ sudo zpool list -v tank
NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
tank 54.5T 31.2T 23.3T - - 23% 57% 1.00x ONLINE -
raidz2-0 54.5T 31.2T 23.3T - - 23% 57%
special 1.8T 120G 1.7T - - 2% 6%
Interprétation : vous pouvez voir la capacité special séparément. Gardez un œil là-dessus ; « special n’est utilisé qu’à 6% » est une phrase rassurante que vous voulez continuer à prononcer.
Task 6: Create a dataset for the small-file workload with sane defaults
cr0x@server:~$ sudo zfs create -o atime=off -o compression=zstd -o xattr=sa tank/work
cr0x@server:~$ sudo zfs get -o name,property,value atime,compression,xattr tank/work
NAME PROPERTY VALUE
tank/work atime off
tank/work compression zstd
tank/work xattr sa
Interprétation : désactiver atime réduit les écritures de métadonnées pour les workloads en lecture. xattr=sa réduit souvent le déversement
vers des objets xattr séparés pour les attributs étendus Linux, ce qui aide lors des tempêtes de petits fichiers.
Task 7: Enable special small blocks on that dataset
cr0x@server:~$ sudo zfs set special_small_blocks=16K tank/work
cr0x@server:~$ sudo zfs get special_small_blocks tank/work
NAME PROPERTY VALUE SOURCE
tank/work special_small_blocks 16K local
Interprétation : les nouveaux blocs de données de fichier jusqu’à 16K seront alloués sur le special vdev. Cela ne réécrira pas les anciennes données.
Task 8: Ensure dnodes can scale with metadata-heavy workloads
cr0x@server:~$ sudo zfs set dnodesize=auto tank/work
cr0x@server:~$ sudo zfs get dnodesize tank/work
NAME PROPERTY VALUE SOURCE
tank/work dnodesize auto local
Interprétation : dnodesize=auto permet des dnodes plus grands quand nécessaire, ce qui peut réduire les blocs de métadonnées supplémentaires
et améliorer les traversées de répertoire et les accès aux attributs. Cela peut augmenter l’empreinte des métadonnées — planifiez la capacité.
Task 9: Baseline small-file behavior before and after (practical micro-benchmark)
cr0x@server:~$ mkdir -p /tank/work/testtree
cr0x@server:~$ /usr/bin/time -f "elapsed=%E" bash -c 'for i in $(seq 1 200000); do echo x > /tank/work/testtree/f_$i; done'
elapsed=0:04:31
cr0x@server:~$ /usr/bin/time -f "elapsed=%E" bash -c 'find /tank/work/testtree -type f -exec stat -c %s {} \; > /dev/null'
elapsed=0:00:46
Interprétation : ne prenez pas les chiffres absolus au pied de la lettre entre systèmes ; cherchez les changements relatifs et où le temps se déplace (CPU vs I/O).
Aussi : 200k fichiers suffisent pour montrer la douleur sans transformer votre système de fichiers en projet scientifique à long terme.
Task 10: Observe real-time I/O distribution (special vs main)
cr0x@server:~$ sudo zpool iostat -v tank 2 5
capacity operations bandwidth
pool alloc free read write read write
---------- ----- ----- ----- ----- ----- -----
tank 31.3T 23.2T 210 1800 8.3M 145M
raidz2-0 31.2T 23.3T 40 220 7.9M 40M
special 140G 1.6T 170 1580 420K 105M
---------- ----- ----- ----- ----- ----- -----
Interprétation : vous voulez voir beaucoup d’IOPS d’écriture de métadonnées/petits blocs sur le special vdev au lieu de marteler les vdevs HDD.
Le débit sur special peut sembler « petit » tandis que les opérations sont élevées — c’est normal.
Task 11: Check dataset properties that commonly interact with small blocks
cr0x@server:~$ sudo zfs get -o name,property,value recordsize,primarycache,logbias,sync tank/work
NAME PROPERTY VALUE
tank/work recordsize 128K
tank/work primarycache all
tank/work logbias latency
tank/work sync standard
Interprétation : pour les arbres de petits fichiers, recordsize n’est généralement pas votre levier (il importe plus pour les gros fichiers et bases).
Mais primarycache peut être ajusté si vous manquez d’ARC, et sync peut tuer la performance si vous faites des écritures synchrones involontaires.
Task 12: Find whether special is filling up and why
cr0x@server:~$ sudo zfs list -o name,used,available,usedbysnapshots,usedbydataset,usedbychildren -r tank/work
NAME USED AVAIL USEDSNAP USEDDS USEDCHILD
tank/work 1.02T 9.1T 120G 900G -
cr0x@server:~$ sudo zpool list -v tank | sed -n '1,5p'
NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
tank 54.5T 32.2T 22.3T - - 24% 59% 1.00x ONLINE -
raidz2-0 54.5T 31.6T 22.9T - - 24% 58%
special 1.8T 600G 1.2T - - 9% 33%
Interprétation : si l’utilisation du special augmente de manière disproportionnée par rapport à l’utilisation du dataset, vous pouvez avoir un dataset à fort churn,
une promotion causée par la compression, ou des snapshots qui épinglent des petits blocs. Suivez l’used-by-snapshots attentivement.
Task 13: Force rewriting old blocks to migrate to special (controlled)
Parce que special_small_blocks n’est pas rétroactif, vous pouvez vouloir réécrire les données en place. Vous pouvez le faire
en copiant à l’intérieur du dataset (ce qui alloue de nouveaux blocs selon la politique actuelle) ou en utilisant send/receive vers un nouveau dataset.
cr0x@server:~$ sudo zfs snapshot tank/work@rewrite-start
cr0x@server:~$ sudo rsync -aHAX --delete /tank/work/ /tank/work_rewritten/
Interprétation : c’est lourd en I/O. Faites-le avec des garde-fous (limitation d’IO, fenêtres hors pics) et assurez-vous que le special a la capacité pour absorber les blocs migrés.
Task 14: Verify special class is actually being used for small blocks
cr0x@server:~$ sudo zfs get -r special_small_blocks tank/work
NAME PROPERTY VALUE SOURCE
tank/work special_small_blocks 16K local
cr0x@server:~$ sudo zdb -dddd tank/work | head -n 20
Dataset tank/work [ZPL], ID 123, cr_txg 45678, 1.02T, 720000 objects
Object lvl iblk dblk dsize dnsize lsize %full type
123 1 128K 16K 16K 512 16K 100% ZFS plain file
124 1 128K 16K 16K 512 12K 75% ZFS plain file
Interprétation : la sortie de zdb varie selon la version, mais vous cherchez des preuves que les blocs de données sont petits et alloués sous la politique actuelle.
Pour une vérification plus approfondie, corrélez avec zpool iostat -v pendant les pics de charge et regardez les opérations special augmenter.
Exploitation : surveillance, planification de capacité et mises à niveau
Ce que vous surveillez change quand special existe
Avant les special vdevs, la « capacité du pool » était la métrique principale. Après special, vous avez deux capacités qui
peuvent indépendamment ruiner votre journée :
- Espace libre des vdevs principaux (la classique).
- Espace libre du special vdev (la nouvelle falaise).
Le special peut se remplir plus vite que prévu parce qu’il contient les blocs qui churnent le plus : métadonnées, petits fichiers,
et parfois des blocs compressés qui tombent sous votre seuil.
Trois mini-histoires du monde corporate
Mini-histoire 1 : L’incident causé par une mauvaise hypothèse
Une grande organisation interne d’ingénierie a déployé des special vdevs pour accélérer une ferme de build monorepo. Les résultats initiaux étaient
glorieux : les checkouts et scans de dépendances se sont accélérés, et la file CI a cessé de s’accumuler chaque matin.
L’hypothèse erronée était subtile : « special, c’est juste des métadonnées ; ça ne va pas beaucoup croître. » Ils ont dimensionné le miroir NVMe
special sur la base d’une règle empirique d’un ancien déploiement metadata-only. Ensuite ils ont mis special_small_blocks=64K pour « être sûrs », car beaucoup
de leurs fichiers faisaient moins de 50KB.
Deux mois plus tard, le special vdev de la ferme de build atteignait une utilisation inconfortable. Le système a commencé à émettre des avertissements d’allocation et des
stalls intermittents. Les développeurs décrivaient cela comme « le stockage fait des crises de panique », ce qui est injuste mais pas totalement inexact.
Le postmortem ne portait pas sur ZFS étant fragile. Il portait sur la responsabilité : personne n’avait une alerte sur la capacité de la classe special.
Ils regardaient l’espace libre global du pool et se sentaient calmes, tandis que le cerveau du pool se vidait doucement.
La correction fut ennuyeuse et efficace : ajouter plus de capacité special (mirror), baisser le seuil pour certains datasets, et
introduire une politique selon laquelle tout dataset activant special_small_blocks doit déclarer un modèle de croissance et un plan d’alerte. La seconde correction
fut la vraie victoire ; la première n’était qu’un paiement.
Mini-histoire 2 : L’optimisation qui a mal tourné
Une équipe de services de fichiers a essayé d’« optimiser tout » dans une même fenêtre de changement : special vdev ajouté, seuil réglé à 32K, compression passée de lz4 à zstd, et
atime modifié — plus une migration massive de données pour réécrire les anciens blocs sur special.
Le retour de bâton n’était pas que zstd soit mauvais ni que special soit risqué. C’était la combinaison de boutons et le timing. La migration a réécrit d’énormes volumes
de petits données et métadonnées. La compression a réduit certains blocs sous le seuil de 32K, ce qui a fait atterrir bien plus de données sur special que prévu. Les snapshots
ont épinglé des blocs supplémentaires. L’amplification d’écritures du mirror special a explosé.
Le symptôme en production était une « lenteur aléatoire ». Pas une panne totale — pire. Un mélange de pics de latence, de longues listes de répertoire, et des plaintes
éparses d’utilisateurs. La surveillance montrait les dispositifs NVMe à haute latence pendant les rafales tandis que les vdevs HDD semblaient presque oisifs.
La récupération fut de l’humilité opérationnelle : arrêter la migration de réécriture, revenir sur le seuil le plus agressif sur les datasets les plus chargés, et relancer la migration
plus tard en lots plus petits avec des limites d’IO explicites. Ils mirent aussi en place un SLO de latence d’écriture pour les dispositifs special. Ce n’est pas une métrique sophistiquée ;
c’est la différence entre « nous savons qu’il est malade » et « on l’apprendra par un VP ».
Mini-histoire 3 : La pratique ennuyeuse mais correcte qui a sauvé la mise
Une autre équipe gérait un store d’artefacts ZFS pour des releases internes. Ils n’utilisèrent les special vdevs que pour les métadonnées au départ et activèrent
special_small_blocks seulement sur un dataset unique utilisé pour index et manifests. Rien d’héroïque : un miroir de SSD entreprise, seuil conservateur (8K),
et une alerte agressive sur l’utilisation special.
Six mois plus tard, un SSD commença à signaler des erreurs média. Le pool passa en degraded, mais rien d’autre ne se produisit — pas de drame, pas de panique.
Ils avaient un runbook : remplacer le périphérique, resilver, vérifier. Ils l’avaient pratiqué en staging avec la même topologie. (Oui, staging. La chose que tout le monde prétend avoir et que personne ne finance.)
Le vrai sauvetage vint d’une chose encore moins excitante : ils avaient des scrubs planifiés et ils examinaient les rapports de scrub.
Le comptage d’erreurs du disque augmenta lentement sur des semaines, et ils le remplacèrent avant que ça devienne une alerte à 3h du matin. Le resilver fut rapide parce que le special était mirroré, petit et sain.
La leçon n’était pas « achetez de meilleurs SSD ». C’était « traitez les special vdevs comme des citoyens de première classe ». Si le cerveau de votre pool y vit, votre maturité opérationnelle doit y vivre aussi.
Métriques opérationnelles pratiques
Choses que j’ai appris à surveiller en environnements réels :
- Capacité du special vdev (absolue et tendance). Les alertes doivent déclencher tôt.
- Latence du special vdev (lecture et écriture) pendant les pics connus.
- Fragmentation du pool et si le comportement de l’allocateur change après des modifications de politique.
- Rétention des snapshots et datasets à churn élevé épinglant des blocs special.
- Pression ARC car le cache des métadonnées change vos patterns hit/miss.
Mises à niveau et cycle de vie : planifier le « comment ajouter plus plus tard »
Vous pouvez ajouter des mirrors special vdev supplémentaires pour étendre la capacité special (comme ajouter un autre mirror vdev à la classe special).
Vous ne pouvez pas facilement retirer des special vdevs d’un pool dans la plupart des configurations de production pratiques. C’est une porte sans retour pour de nombreux opérateurs.
Donc votre conception doit supposer la croissance. Si vous pensez « nous n’aurons jamais besoin de plus de 2TB special », vous êtes probablement sur le point d’apprendre quelque chose
sur votre distribution de tailles de fichiers.
Playbook de diagnostic rapide
Quand quelqu’un dit « les petits fichiers sont lents » ou « git checkout rame », vous pouvez perdre une journée en théories. Voici une
triage pratique en trois passes qui trouve rapidement le goulot d’étranglement.
Premier : confirmer que le goulot est le stockage et pas le client
- Vérifiez les contraintes côté client : saturation CPU, extraction mono‑thread, antivirus, ou latence réseau.
- Sur le serveur, surveillez la charge système et l’I/O wait.
cr0x@server:~$ uptime
09:41:22 up 102 days, 3:12, 5 users, load average: 12.11, 11.90, 10.80
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
8 2 0 812344 91232 9023112 0 0 420 1800 3200 7000 12 6 55 27 0
Interprétation : un wa élevé suggère une latence de stockage. Ce n’est pas une preuve, mais c’est un indice fort.
Second : identifier si special aide ou nuit
- Vérifiez la capacité et la santé du special.
- Surveillez les IOPS et la latence par vdev pendant l’opération lente.
cr0x@server:~$ sudo zpool status tank | sed -n '1,25p'
pool: tank
state: ONLINE
config:
NAME STATE READ WRITE CKSUM
tank ONLINE 0 0 0
raidz2-0 ONLINE 0 0 0
special
mirror-1 ONLINE 0 0 0
cr0x@server:~$ sudo zpool list -v tank | tail -n 3
raidz2-0 54.5T 31.6T 22.9T - - 24% 58%
special 1.8T 1.4T 420G - - 18% 77%
Interprétation : special à 77% est un signal d’alarme. Même si la performance semble correcte aujourd’hui, l’allocateur et la croissance future ne seront pas indulgents.
cr0x@server:~$ sudo zpool iostat -v tank 1 10
Interprétation : si special a d’énormes files/ops et une latence en hausse tandis que les HDD sont inoccupés, special est votre goulot
(trop petit, trop lent, ou on lui demande trop).
Troisième : valider la politique du dataset et la réalité de la charge
- Confirmez que le dataset en question a les propriétés attendues.
- Vérifiez si les écritures sync, les snapshots ou la compression ont changé le comportement.
- Cherchez les surprises de type « pas rétroactif » : les anciens blocs toujours sur HDD.
cr0x@server:~$ sudo zfs get -o name,property,value special_small_blocks,compression,atime,xattr,dnodesize -r tank/work
NAME PROPERTY VALUE
tank/work special_small_blocks 16K
tank/work compression zstd
tank/work atime off
tank/work xattr sa
tank/work dnodesize auto
Interprétation : si le dataset n’a pas la propriété réglée, vous n’utilisez pas la fonctionnalité. S’il l’a, mais que la performance n’a pas changé,
vous pouvez être lié par des écritures sync, le CPU, ou les données n’ont pas été réécrites.
Erreurs courantes, symptômes et corrections
Mistake 1: Adding a non-redundant special vdev
Symptôme : Tout fonctionne jusqu’au jour où ça ne fonctionne plus ; quand le périphérique meurt, le pool peut devenir irrécupérable.
Fix : Toujours mirror (ou RAIDZ) les special vdevs. Si vous avez déjà fait cette erreur, traitez-la comme une dette technique urgente et planifiez une migration vers un pool conçu correctement.
La « correction » in-place n’est pas le type d’aventure excitante que vous voulez.
Mistake 2: Setting special_small_blocks too high “just in case”
Symptôme : La capacité special croît plus vite que prévu ; la performance peut s’améliorer initialement puis se dégrader à mesure que special approche une utilisation élevée.
Fix : Abaissez le seuil pour les nouvelles écritures lorsque c’est approprié, et ajoutez plus de capacité special. Souvenez-vous qu’abaisser la propriété ne déplace pas les blocs existants.
Pour certains datasets, reconstruire (send/receive ou réécriture) est l’approche propre.
Mistake 3: Forgetting that compression can move more data onto special
Symptôme : Après avoir activé zstd ou augmenté le niveau de compression, l’utilisation de special accélère, surtout pour les fichiers « moyens » très compressibles.
Fix : Réévaluez le seuil et la sélection des datasets. Envisagez un seuil plus petit (8K/16K) pour les datasets compressés qui contiennent beaucoup de fichiers modérément grands mais hautement compressibles.
Mistake 4: No alerting on special vdev usage
Symptôme : Le pool a beaucoup d’espace libre, mais les opérations échouent ou la latence augmente ; l’équipe est confuse parce que « le pool n’est rempli qu’à 60% ».
Fix : Mettez en place des alertes sur l’utilisation de la classe special séparément, avec un seuil d’avertissement précoce (par exemple avertir à 60–70%, critique à 80–85%,
adapté à votre taux de croissance et tolérance au risque).
Mistake 5: Assuming enabling the property makes existing data faster
Symptôme : Vous avez réglé special_small_blocks, les utilisateurs ne voient aucune amélioration, et quelqu’un affirme « ça ne marche pas ».
Fix : Expliquez et planifiez la réécriture : déplacez les données vers un nouveau dataset via send/receive, ou réécrivez les fichiers en place (avec prudence).
Validez avec zpool iostat -v pendant les exécutions de charge.
Mistake 6: Overloading special with too many datasets at once
Symptôme : Les dispositifs special montrent une latence d’écriture élevée, tandis que les vdevs principaux semblent sous-utilisés ; ralentissements intermittents lors de tempêtes de métadonnées.
Fix : Priorisez les datasets qui en ont vraiment besoin ; utilisez des seuils conservateurs ; scalez la capacité special avant d’élargir la portée. Traitez-le comme une ressource critique partagée, pas comme une décharge pour chaque souhait de performance.
Mistake 7: Ignoring snapshots and churn
Symptôme : L’utilisation de special augmente même quand « la taille des données » semble stable ; la suppression de fichiers ne libère pas beaucoup d’espace car des snapshots retiennent des blocs.
Fix : Auditez la rétention des snapshots et les datasets à fort churn. Ajustez la politique de snapshots ou déplacez les workloads à fort churn vers des pools/datasets séparés avec une rétention adaptée.
Listes de contrôle / plan pas à pas
Checklist A: Decide whether special small blocks is the right tool
- Confirmez que la charge est dominée par métadonnées/petits fichiers (lent
find, lentstat, lente extraction untar). - Confirmez que le goulot est la latence/IOPS de stockage, pas le réseau ou le CPU.
- Mesurez la distribution des tailles de fichiers et le nombre d’objets (approximatif suffit, mais mesurez).
- Décidez quels datasets en ont vraiment besoin (ne l’activez pas globalement).
Checklist B: Design the special vdev safely
- Choisissez une topologie redondante (mirror minimum).
- Choisissez des dispositifs avec endurance appropriée et comportement d’alimentation stable pour votre environnement.
- Dimensionnez pour métadonnées + petits blocs attendus + overhead snapshots + croissance.
- Planifiez l’expansion : laissez des baies/emplacements ou des lanes PCIe, et budgetez l’ajout d’un autre mirror vdev plus tard.
- Créez des alertes spécifiques pour l’utilisation special et les taux d’erreurs des dispositifs.
Checklist C: Rollout plan that won’t wake you up at 3 a.m.
- Ajoutez le special vdev et vérifiez la santé du pool.
- Activez sur un dataset d’abord avec un seuil conservateur (8K ou 16K).
- Mesurez avant/après sur des opérations réelles (checkout, untar, indexation, scans de répertoire).
- Réécrivez les données seulement si nécessaire, et limitez le débit des migrations.
- Étendez le périmètre dataset par dataset en surveillant la tendance d’utilisation special.
Checklist D: Ongoing operations
- Revue hebdomadaire de la tendance d’utilisation special et de la croissance des snapshots.
- Scrubs planifiés et rapports de scrub examinés.
- Vérifications santé des dispositifs (SMART/NVMe logs) intégrées aux seuils d’alerte.
- Validation trimestrielle : tester une procédure de remplacement de périphérique et de resilver (en environnement sûr).
Extra practical tasks (ops-focused)
Si vous voulez des tâches pratiques au‑delà des douze de base, celles-ci rapportent souvent rapidement.
Task 15: Check ARC stats (metadata caching pressure)
cr0x@server:~$ sudo arcstat 1 5
time read miss miss% dmis dm% pmis pm% mmis mm% arcsz c
09:52:01 812 102 12 48 47 40 39 14 14 96G 110G
Interprétation : si le taux de miss ARC augmente pendant les tempêtes de répertoire, le special vdev sera plus sollicité. C’est acceptable si
c’est dimensionné et rapide. Sinon, vous verrez de la latence.
Task 16: Quick check of mountpoint and dataset mapping (avoid tuning the wrong dataset)
cr0x@server:~$ mount | grep tank
tank/work on /tank/work type zfs (rw,xattr,noacl)
cr0x@server:~$ sudo zfs list -o name,mountpoint -r tank | grep -E '^tank/work|/tank/work'
tank/work /tank/work
Interprétation : dans les vraies boîtes, « le chemin » et « le dataset » dérivent avec le temps. Réglez le dataset qui sert réellement le chemin utilisé par les utilisateurs.
Task 17: Check snapshot count and retention pressure
cr0x@server:~$ sudo zfs list -t snapshot -o name,used,creation -S creation | head
NAME USED CREATION
tank/work@daily-2025-12-24 1.2G Wed Dec 24 02:00 2025
tank/work@daily-2025-12-23 1.1G Tue Dec 23 02:00 2025
tank/work@daily-2025-12-22 1.3G Mon Dec 22 02:00 2025
Interprétation : les snapshots ne sont pas « gratuits ». Sur des datasets churny, les snapshots épinglent d’anciens petits blocs et métadonnées.
FAQ
1) Is a special vdev the same thing as L2ARC?
Non. L2ARC est un cache et peut être supprimé et reconstruit. Le special vdev stocke de vrais blocs (métadonnées et éventuellement petites données de fichier).
Si le special est perdu sans redondance, le pool peut être perdu.
2) Do I need special small blocks if I already have lots of RAM for ARC?
Peut-être. L’ARC aide si votre working set tient dedans et le pattern d’accès est favorable au cache. Mais les charges de petits fichiers dépassent souvent l’ARC
ou impliquent des scans froids (pensez aux runners CI qui démarrent à zéro). Le special vdev améliore la pénalité de miss en faisant atterrir les misses sur SSD/NVMe
plutôt que sur HDD.
3) What’s a good starting value for special_small_blocks?
8K ou 16K est un point de départ sensé pour de nombreux environnements. Si vous sautez à 64K, vous vous engagez à stocker beaucoup de données réelles sur special.
Cela peut être correct, mais prenez cette décision avec des calculs de dimensionnement, pas des impressions.
4) Can I enable it on the whole pool at once?
Techniquement vous pouvez le définir largement sur des datasets, mais opérationnellement vous ne devriez pas. Déployez dataset par dataset, mesurez,
et surveillez la tendance d’utilisation special. Special est une ressource partagée ; un dataset bruyant peut la consommer.
5) Why didn’t performance improve after I set the property?
Raisons courantes : vos données n’ont pas été réécrites (la propriété n’est pas rétroactive), votre goulot est des écritures sync ou le réseau,
la charge est CPU‑bound, ou le special est saturé/lent. Validez avec zpool iostat -v pendant la charge.
6) What happens if the special vdev fills up?
Des choses mauvaises de façon ennuyeuse : des allocations peuvent échouer pour les métadonnées et petits blocs, et le pool peut devenir instable ou commencer à retourner des erreurs.
Traitez « special approche du plein » comme une condition sérieuse — ajoutez de la capacité ou réduisez les facteurs de croissance avant que ça ne devienne un incident de production.
7) Can I remove a special vdev later?
Dans de nombreuses configurations réelles, retirer des special vdevs n’est pas simple ou pas pris en charge comme les gens l’imaginent.
Planifiez comme si c’était permanent. Si vous avez besoin de réversibilité, envisagez de construire un nouveau pool et de migrer.
8) Should I also move metadata to SSD by using a “metadata-only” pool layout?
Les special vdevs sont exactement ce concept, intégré aux classes d’allocation ZFS. Si votre pool est entièrement SSD, les special vdevs n’apporteront pas beaucoup.
Si votre pool est basé HDD, special peut être un gros gain sans déplacer toutes les données sur SSD.
9) Does xattr=sa matter for this topic?
Souvent oui. Quand les xattrs vivent dans la zone d’attributs système, ZFS peut éviter des objets xattr séparés dans beaucoup de cas,
réduisant les E/S de métadonnées. Cela accélère les scans de répertoire et les workloads lourds en attributs et peut réduire la pression sur la capacité et les IOPS du special.
10) Should I use RAIDZ for the special vdev instead of mirrors?
Les mirrors sont courants pour special parce qu’ils gardent la latence basse et simplifient le rebuild. RAIDZ peut être viable pour l’efficacité de capacité,
mais vous devez accepter l’amplification d’écritures et la dynamique de rebuild. Pour la plupart des workloads sensibles à la latence sur petits fichiers,
les mirrors special sont le choix sûr par défaut.
Conclusion
Les special vdevs ZFS et special_small_blocks sont l’un des moyens les plus efficaces de changer la sensation d’un système de fichiers qui se noie dans les petits fichiers.
Ils ne se contentent pas de « rendre plus rapide » — ils déplacent la partie la plus difficile de la charge (métadonnées et petites E/S aléatoires) vers un média qui peut réellement le faire.
Mais ce n’est pas un déjeuner gratuit, et ce n’est pas un cache que vous pouvez balayer d’un revers de main. Le special vdev devient une infrastructure critique : il nécessite
redondance, surveillance, planification de capacité et discipline opérationnelle. Si vous le traitez comme un simple hack de performance, il finira par vous traiter comme quelqu’un qui aime lire les logs du noyau à minuit.
Commencez conservateur, mesurez honnêtement, et étendez avec intention. C’est comme ça que vous obtenez le turbo sans le joint de culasse fissuré.