Vous avez changé recordsize sur un dataset ZFS et rien n’est devenu plus rapide. Ou pire : quelque chose est devenu plus lent et vous regardez maintenant des tableaux de bord comme s’ils vous devaient de l’argent.
Bienvenue au moment le plus fréquent du type « mais j’ai tourné le bouton » dans l’exploitation ZFS.
La stratégie de recordsize est un de ces sujets où la moitié des conseils est correcte, un quart est obsolète et le dernier quart est assurément faux. L’astuce consiste à comprendre
ce qui change instantanément, ce qui n’affecte que les blocs nouvellement écrits, et comment migrer sans détruire tout votre système de fichiers juste pour satisfaire un paramètre.
Ce que fait vraiment le recordsize (et ce qu’il ne fait pas)
recordsize est la taille de bloc de données maximale que ZFS utilisera pour les fichiers dans un dataset de type filesystem. Ce mot « maximale » explique toute l’histoire.
ZFS stockera un fichier dans des blocs jusqu’à recordsize, mais il peut (et le fera) utiliser des blocs plus petits quand le fichier est petit, quand l’application écrit
en petits morceaux, ou quand il n’est pas possible d’imbriquer efficacement les données dans des enregistrements complets.
Voici une vérité opérationnelle non négociable : changer le recordsize ne réécrit pas les blocs existants.
Cela modifie la façon dont les nouvelles données sont disposées à partir de ce moment-là. Les anciens blocs restent de la taille qu’ils avaient lors de leur écriture.
C’est pourquoi « on a réglé le recordsize et ça n’a rien aidé » n’est pas un mystère ; c’est le comportement attendu.
Le recordsize concerne le compromis entre efficacité séquentielle et coût des mises à jour aléatoires. Les grands enregistrements sont excellents quand vous fluxsez de gros fichiers (sauvegardes, médias, logs en append uniquement, grands stores d’objets).
Les petits enregistrements sont meilleurs pour les lectures/écritures aléatoires de petites tailles (certaines bases de données, images de VM, spools de mail, workloads riches en métadonnées).
L’erreur est de penser qu’il existe une valeur unique correcte. Il n’y en a pas. Il existe une valeur correcte par dataset et par charge.
Aussi : recordsize concerne les filesystems (zfs datasets). Pour les périphériques de blocs (zvol), vous traitez avec volblocksize, qui est défini à la création et ne se change pas à la légère.
Si vous faites tourner des VM sur des zvols et que vous aiguisez le recordsize… vous faites du polissage de vélo pour améliorer la consommation d’un camion.
Comment le recordsize interagit avec le monde réel
- Amplification IO : mettre à jour 4K au milieu d’un enregistrement de 128K peut coûter bien plus que 4K. ZFS utilise le copy-on-write, les sommes de contrôle et la compression ; il peut devoir réécrire beaucoup de structures pour modifier peu de données.
- Comportement de l’ARC : les gros blocs peuvent occuper l’ARC en plus gros morceaux ; cela peut être bien (moins de recherches de métadonnées) ou mauvais (usure du cache) selon les schémas d’accès.
- Compression : les enregistrements plus grands compressent souvent mieux. Mais compresser un enregistrement de 1M pour économiser de l’espace n’est pas « gratuit » quand votre workload fait des lectures aléatoires de petites plages.
- Special vdevs : si vous avez des classes d’allocation spéciales pour métadonnées/petits blocs, le changement de recordsize peut déplacer ce qui est considéré « petit » et finir sur votre special vdev. Ça peut être soit un cadeau soit un incendie.
- Snapshots : réécrire des données pour adopter un nouveau recordsize conservera les anciens blocs via les snapshots. Les plans de migration qui ignorent la rétention des snapshots sont la façon dont les équipes de stockage apprennent l’humilité.
Une citation pour garder les pieds sur terre
Idée paraphrasée (Gene Kim) : « Améliorer la fiabilité vient de la compréhension du flux et des boucles de rétroaction, pas des actions héroïques après l’incident. »
Faits intéressants et contexte historique
ZFS existe depuis assez longtemps pour accumuler du folklore. Une partie est utile ; une partie devrait être compostée. Voici quelques faits concrets et points de contexte
qui comptent réellement pour les décisions de migration de recordsize :
- Le recordsize est né à l’époque Solaris comme un compromis pragmatique « assez grand pour le séquentiel, pas trop grand pour la mémoire » — 128K est devenu courant car cela fonctionnait sur des disques et des workloads réels.
- La valeur par défaut est restée stable historiquement précisément parce que c’est un compromis, pas parce que c’est optimal. Elle est conçue pour être « acceptable » pour le partage de fichiers général, pas « idéale » pour votre base OLTP.
- ZFS a toujours supporté des tailles de bloc variables jusqu’à recordsize ; c’est pour cela que les petits fichiers ne sont pas paddés à 128K. C’est aussi la raison pour laquelle recordsize est un plafond, pas un mandat.
- OpenZFS a divergé et évolué selon les plateformes. Des fonctionnalités comme les large dnodes, les special vdevs et le préfetch amélioré ont changé le paysage des performances autour du recordsize, même si recordsize lui-même n’a pas « changé ».
- Les algorithmes de compression se sont améliorés (par ex. LZ4 est devenu le défaut pratique). Cela a rendu « les plus grands enregistrements compressent mieux » plus pertinent opérationnellement parce que la compression est devenue moins coûteuse à utiliser partout.
- Les disques à secteurs 4K ont poussé les réalités d’ashift. Un mauvais agencement entre ashift et les secteurs physiques peut dominer les performances ; le tuning du recordsize ne compensera pas un mauvais alignement physique.
- Les tendances du stockage VM ont imposé de la clarté : la communauté a appris (parfois bruyamment) que le tuning des zvols est différent du tuning des filesystems, et les conseils sur recordsize ne se transfèrent pas automatiquement.
- SSD/NVMe ont changé les modes de défaillance : les IOPS aléatoires sont moins terrifiants que sur des disques rotatifs, mais l’amplification d’écriture et les pics de latence restent importants — surtout avec RAID parité, petites mises à jour et écritures sync.
Pourquoi vous migrez : motivations guidées par les symptômes
« On veut changer recordsize » n’est pas un objectif. C’est une tactique. Votre objectif est généralement l’un de ceux-ci :
- Réduire la latence sur les lectures/écritures aléatoires pour des applications qui touchent de petites régions.
- Augmenter le débit pour des flux séquentiels importants (sauvegardes, traitement média, pipelines ETL, archivage).
- Réduire l’amplification d’écriture pour des workloads à réécritures fréquentes (certains schémas de base de données, images de VM, couches de conteneurs en forte activité).
- Corriger le churn du cache où l’ARC est rempli de gros enregistrements alors que le workload veut de petites tranches.
- Arrêter de saturer un special vdev parce que trop de blocs correspondent au critère « petits ».
- Rendre la réplication et le send/receive prévisibles en alignant les nouvelles écritures sur une stratégie de bloc connue.
Vous migrez la stratégie de recordsize lorsque vous avez mesuré un décalage entre ce que fait votre workload et ce pour quoi votre dataset est optimisé.
Si vous le faites parce que « quelqu’un sur Internet a dit que les bases aiment 16K », vous êtes déjà à mi-chemin d’un rapport d’incident.
Méthode de diagnostic rapide
Quand les performances sont mauvaises et que le recordsize est blâmé (souvent à tort), ne commencez pas par réécrire les données.
Commencez par prouver ce qui est lent et où.
Première étape : identifier le motif de workload
- Est-ce principalement des lectures aléatoires ? Des écritures aléatoires ? Des lectures/écritures séquentielles ?
- Quelle taille d’IO l’application émet-elle réellement ? 4K ? 16K ? 128K ? Mixte ?
- La lenteur est-elle liée à la latence ou au débit ?
Deuxième étape : vérifier le chemin de stockage pour des « limites dures »
- Écritures sync en attente sur SLOG ? (Ou pire : pas de SLOG sur un système sync-lourd.)
- Vdevs parité soumis à de petites réécritures aléatoires ? C’est un percepteur d’impôts qui ne dort jamais.
- Special vdev saturé ? Les IO de métadonnées peuvent mettre à genoux tout le pool.
- Ashift incorrect ? Le mauvais alignement peut dominer tout le reste.
Troisième étape : valider ce que fait recordsize aujourd’hui
- Quel est le recordsize du dataset actuellement ?
- Quelles tailles de bloc existent sur le disque pour les fichiers chauds ?
- Des snapshots épinglent-ils d’anciens agencements si « la migration » ne change pas la réalité physique ?
Le recordsize est un levier. Mais vous ne tirez pas un levier avant d’avoir confirmé que c’est le bon.
Tâches pratiques : commandes, sorties et décisions
Ce sont des tâches opérationnelles réelles que vous pouvez exécuter aujourd’hui. Chacune inclut une commande, une sortie d’exemple, ce que cela signifie et la décision qui en découle.
Pas de magie, pas d’intuitions.
Task 1: Confirm current recordsize (and whether you’re even looking at the right dataset)
cr0x@server:~$ zfs get -o name,property,value,source recordsize tank/app
NAME PROPERTY VALUE SOURCE
tank/app recordsize 128K local
Sens : tank/app a recordsize à 128K défini localement. Si c’est inherited, le réglage vient d’un dataset parent.
Décision : Si vous vouliez tuner seulement un dataset enfant, arrêtez et corrigez les frontières d’héritage avant de changer quoi que ce soit.
Task 2: Verify whether you’re on a filesystem dataset or zvol
cr0x@server:~$ zfs list -o name,type,volblocksize,recordsize,mountpoint tank
NAME TYPE VOLBLOCKSIZE RECORDSIZE MOUNTPOINT
tank filesystem - 128K /tank
tank/app filesystem - 128K /tank/app
tank/vm-01 volume 16K - -
Sens : tank/vm-01 est un zvol ; recordsize ne s’applique pas. volblocksize s’applique.
Décision : Si votre problème concerne l’IO disque des VM et qu’il est sur des zvols, arrêtez de tuner recordsize et évaluez volblocksize, l’alignement zvol et les schémas IO du guest.
Task 3: Check ashift and vdev layout (recordsize can’t rescue bad fundamentals)
cr0x@server:~$ zdb -C tank | egrep "ashift|vdev_tree|type:|path:"
ashift: 12
type: 'raidz'
path: '/dev/disk/by-id/nvme-SAMSUNG_MZQLB1T9HAJR-00007_...'
Sens : ashift=12 signifie secteurs 4K. C’est généralement raisonnable. Si vous voyez ashift=9 sur des disques modernes, attendez-vous à des douleurs d’alignement.
Décision : Si ashift est incorrect, planifiez une reconstruction du pool. Ne perdez pas une semaine à « tuner recordsize » comme mécanisme d’atténuation.
Task 4: Confirm compression and its interaction with IO size
cr0x@server:~$ zfs get -o name,property,value compression,compressratio tank/app
NAME PROPERTY VALUE SOURCE
tank/app compression lz4 local
tank/app compressratio 1.43x -
Sens : LZ4 est activé ; les données se compressent raisonnablement. Les enregistrements plus grands améliorent souvent le ratio de compression, mais peuvent augmenter la latence pour de petites lectures aléatoires.
Décision : Si vous êtes sensible à la latence et voyez un high compressratio avec de grands enregistrements, réfléchissez au coût de décompression sur des lectures de petites tranches.
Task 5: Observe real-time IO size patterns (are you guessing or measuring?)
cr0x@server:~$ iostat -x 1 5
avg-cpu: %user %nice %system %iowait %steal %idle
8.12 0.00 3.94 1.25 0.00 86.69
Device r/s w/s rMB/s wMB/s avgrq-sz await svctm %util
nvme0n1 820.0 610.0 12.8 9.6 32.0 1.9 0.4 58.0
Sens : avgrq-sz ~32K par requête. Ce n’est pas du « gros streaming ». C’est des IO de taille modérée. Si votre recordsize est 1M et que vous faites des requêtes de 32K, vous devriez être suspicieux.
Décision : Si les tailles de requête sont petites et aléatoires, envisagez un recordsize plus petit (ou admettez que votre application est le facteur limitant, pas votre dataset).
Task 6: Check ZFS-level IO and latency with zpool iostat
cr0x@server:~$ zpool iostat -v tank 1 3
capacity operations bandwidth
pool alloc free read write read write
-------------------------- ----- ----- ----- ----- ----- -----
tank 2.10T 5.15T 1.20K 900 85.0M 60.0M
raidz1-0 2.10T 5.15T 1.20K 900 85.0M 60.0M
nvme0n1 - - 400 300 28.0M 20.0M
nvme1n1 - - 400 300 28.0M 20.0M
nvme2n1 - - 400 300 28.0M 20.0M
-------------------------- ----- ----- ----- ----- ----- -----
Sens : Le pool effectue beaucoup de petites opérations. Si les ops sont élevées mais le débit modeste, vous êtes probablement lié par les IOPS/latence, pas par le débit.
Décision : Si vous êtes limité en IOPS sur un RAIDZ, un recordsize plus petit pourrait réduire l’amplification read-modify-write pour certaines séquences d’écritures, mais le surcoût de parité peut toujours dominer. Envisagez des mirrors pour les workloads à écritures aléatoires intensives.
Task 7: Check sync write pressure (recordsize isn’t the fix for fsync storms)
cr0x@server:~$ zpool get -o name,property,value,source sync,logbias tank
NAME PROPERTY VALUE SOURCE
tank sync standard default
tank logbias latency local
Sens : sync=standard est normal. logbias=latency privilégie le SLOG pour les écritures sync (si présent).
Décision : Si votre application est fsync-intensive et que la latence est mauvaise, vérifiez la santé/performance du SLOG avant de toucher au recordsize. Le recordsize ne rendra pas un dispositif de sync lent plus rapide.
Task 8: Verify you have (or don’t have) a SLOG device
cr0x@server:~$ zpool status -v tank
pool: tank
state: ONLINE
config:
NAME STATE READ WRITE CKSUM
tank ONLINE 0 0 0
raidz1-0 ONLINE 0 0 0
nvme0n1 ONLINE 0 0 0
nvme1n1 ONLINE 0 0 0
nvme2n1 ONLINE 0 0 0
logs
nvme3n1 ONLINE 0 0 0
Sens : Il y a un dispositif de log séparé. Bien. Vérifiez maintenant que ce n’est pas un SSD consommateur aux caractéristiques PLP médiocres.
Décision : Si vous n’avez pas de SLOG et que vous avez des workloads sync, ne laissez pas recordsize devenir votre bouc émissaire.
Task 9: Inspect file block sizes on disk (are your “new settings” actually present?)
cr0x@server:~$ zdb -ddddd tank/app | egrep "Object|blksz|type" | head -n 12
Object lvl iblk dblk dsize dnsize bonustype bsize data
7 1 128K 128K 9.00M 512 DMU dnode 16K ZFS plain file
dblk [0] L0 128K 1.00M
dblk [1] L0 128K 1.00M
Sens : Les blocs de données de ce fichier sont de 128K. Si vous avez changé recordsize en 16K hier et que vous voyez encore des blocs 128K, ce fichier n’a pas été réécrit.
Décision : Si les fichiers chauds utilisent encore les anciennes tailles, il vous faut une réécriture ciblée/migration (ou accepter que ce n’est pas rentable de réécrire).
Task 10: Identify snapshot pinning before planning rewrites
cr0x@server:~$ zfs list -t snapshot -o name,used,refer,creation -s creation tank/app | tail -n 5
tank/app@snap-2025-12-01 85.2G 120G Mon Dec 1 03:00 2025
tank/app@snap-2025-12-08 91.7G 120G Mon Dec 8 03:00 2025
tank/app@snap-2025-12-15 96.4G 120G Mon Dec 15 03:00 2025
tank/app@snap-2025-12-22 101.9G 120G Mon Dec 22 03:00 2025
tank/app@snap-2025-12-26 104.1G 120G Thu Dec 26 03:00 2025
Sens : La colonne « used » des snapshots augmente. Réécrire du contenu dupliquera les blocs et les snapshots garderont les anciens, ce qui peut doubler l’espace utilisé.
Décision : Si vous prévoyez de réécrire/migrer, coordonnez la rétention des snapshots ou assurez-vous d’avoir une marge. Pas de marge signifie pas de migration ; cela signifie panique plus tard.
Task 11: Estimate dataset headroom realistically (not “df says we’re fine”)
cr0x@server:~$ zfs list -o name,used,avail,refer,logicalused,logicalavail tank/app
NAME USED AVAIL REFER LOGICALUSED LOGICALAVAIL
tank/app 2.1T 3.4T 1.9T 2.8T 4.5T
Sens : L’usage physique et l’usage logique peuvent diverger à cause de la compression et des snapshots. Les calculs de marge doivent prendre en compte le comportement des snapshots pendant la réécriture.
Décision : Si l’espace disponible est serré, évitez toute approche qui duplique les données (rsync dans le même dataset, copie de fichiers générant du churn, réécriture naïve).
Task 12: Check whether special small blocks are in play
cr0x@server:~$ zfs get -o name,property,value special_small_blocks tank
NAME PROPERTY VALUE SOURCE
tank special_small_blocks 0 default
Sens : special_small_blocks=0 signifie que seuls les métadonnées vont sur les special vdevs (si présents). Si cela est défini (par ex. 16K), alors les petits blocs de fichiers peuvent aller sur des devices spéciaux.
Décision : Si special_small_blocks est activé et que le special vdev est chaud, envisagez d’augmenter recordsize ou d’ajuster special_small_blocks pour éviter de pousser trop de données vers le spécial.
Task 13: Confirm what changed when you set recordsize (property sources matter)
cr0x@server:~$ zfs get -r -o name,property,value,source recordsize tank | head
NAME PROPERTY VALUE SOURCE
tank recordsize 128K default
tank/app recordsize 16K local
tank/app/logs recordsize 16K inherited from tank/app
Sens : Vous avez changé tank/app et l’enfant a hérité. Cela peut être correct — ou un dommage collatéral.
Décision : Si les logs sont en append-only et que vous les avez forcés à 16K, vous avez probablement réduit le débit et augmenté le coût des métadonnées sans bénéfice.
Task 14: Do a controlled “rewrite a subset” test with a sacrificial directory
cr0x@server:~$ zfs create -o recordsize=16K tank/app_test
cr0x@server:~$ rsync -aHAX --info=progress2 /tank/app/hotset/ /tank/app_test/hotset/
sending incremental file list
3.42G 12% 95.11MB/s 0:00:32
28.40G 100% 102.88MB/s 0:04:38 (xfr#231, to-chk=0/232)
Sens : Vous avez copié le hot set dans un dataset avec le recordsize désiré. Maintenant les blocs de données de ces fichiers seront créés sous la nouvelle politique.
Décision : Benchmarkez l’application contre ce dataset test. Si ça s’améliore, vous avez une preuve. Sinon, arrêtez de blâmer recordsize.
Blague #1 : Changer le recordsize en s’attendant à ce que les anciennes données changent, c’est comme changer la marque de café du bureau et s’attendre à ce que les incidents du trimestre précédent disparaissent.
Stratégies de migration sans réécrire tout le dataset
La promesse en titre — « changer de stratégie sans tout réécrire » — est seulement partiellement réalisable, parce que la physique et la réalité disque existent.
Vous pouvez changer la politique immédiatement. Vous pouvez éviter de réécrire les données froides. Vous pouvez cibler des réécritures. Vous pouvez aussi décider de ne rien réécrire du tout et quand même gagner.
Strategy A: Accept that only new writes matter (and make that enough)
C’est la stratégie la plus simple et la plus sous-estimée. Beaucoup de datasets sont « majoritairement append » ou « majoritairement en churn » même s’ils paraissent statiques.
Si le working set chaud est naturellement réécrit, alors un changement de politique vous migrera avec le temps.
Où cela fonctionne :
- Datasets de logs où les fichiers pivotent et les anciens logs vieillissent.
- Artefacts de build temporaires et caches CI.
- Object stores où les objets sont remplacés (pas modifiés en place) et où des politiques de lifecycle expirent les anciens objets.
Ce que vous faites :
- Définissez le nouveau recordsize.
- Assurez-vous que les nouvelles écritures atterrissent dans le bon dataset (points de montage séparés si nécessaire).
- Surveillez la distribution des tailles de bloc sur les fichiers nouvellement écrits pendant des jours/semaines.
Strategy B: Split datasets by workload, not by org chart
La plupart des échecs de recordsize arrivent parce que quelqu’un a essayé de faire servir un dataset à deux maîtres : IO aléatoire petit et débit séquentiel élevé.
Ne le faites pas. Utilisez plusieurs datasets avec des propriétés différentes et déplacez les données le long de frontières qui correspondent à leur usage.
Découpages pratiques qui fonctionnent :
- db/ (aléatoire petit) : recordsize 16K–32K a souvent du sens pour de nombreux patterns OLTP, mais mesurez.
- wal-or-redo/ (append séquentiel, sync) : veut généralement des enregistrements plus grands et une attention stricte au sync/SLOG.
- backups/ (gros séquentiels) : 1M peut être excellent.
- home/ ou shared/ (mixte) : gardez le 128K par défaut sauf preuve du contraire.
Séparer les datasets vous donne : snapshots séparés, quotas séparés, stratégies de réservation distinctes et — critique — un tuning séparé.
C’est ainsi que les équipes matures gèrent ZFS en production.
Strategy C: Targeted rewrite of hot data only
Si la douleur de performance est concentrée sur un sous-ensemble chaud (fichiers de base de données, images de VM, un répertoire d’index particulier),
réécrivez seulement ce sous-ensemble dans un nouveau dataset avec le bon recordsize. Puis basculez le chemin de l’application.
Techniques :
- Créez un nouveau dataset avec le recordsize désiré.
- Copiez l’arborescence du répertoire chaud (rsync ou export/import au niveau application).
- Basculez les points de montage ou les limites par symlink (préférez les points de montage ; les symlinks perturbent à 3h du matin).
- Gardez les anciennes données disponibles pour rollback, mais ne les laissez pas montées où l’application peut les utiliser par erreur.
Pour les bases de données, les outils au niveau application (dump/restore, backup physique, déplacement de tablespaces) produisent souvent une réécriture plus propre que la copie au niveau fichier.
Ils permettent aussi à la base de données de réorganiser et réduire la fragmentation interne. Le rsync fichier-à-fichier est brut ; parfois le brutal suffit.
Strategy D: Rewrite in place by copying file-to-file (use with caution)
Le truc classique : copier un fichier sur lui-même via un nom temporaire pour que ZFS alloue de nouveaux blocs sous le nouveau recordsize.
Cela peut fonctionner pour un ensemble limité de fichiers quand vous tolérez l’espace et l’IO supplémentaires.
Les parties laides :
- Les snapshots gardent les anciens blocs en vie. L’usage d’espace peut exploser.
- Vous pouvez déclencher une énorme amplification d’écriture et fragmentation.
- C’est difficile à faire en toute sécurité à grande échelle sans un plan d’orchestration.
Si vous le faites, réalisez-le pendant une fenêtre de maintenance dédiée, sur une liste connue de fichiers, avec monitoring et politique de snapshot temporairement ajustée.
Strategy E: ZFS send/receive as a migration engine
ZFS send/receive est fantastique pour déplacer des datasets entre pools ou réorganiser des arbres de datasets. Ce n’est pas par lui-même un réécraseur de taille de bloc magique.
Un send/receive reproduit le contenu du dataset et conserve typiquement la structure on-disk des blocs dans le stream, ce qui signifie qu’il ne « re-recordsize » pas vos données existantes.
Ce que send/receive fait bien dans ce contexte :
- Déplacer des données vers un nouveau pool avec une autre topologie de vdev mieux adaptée à votre pattern IO (mirrors vs RAIDZ).
- Réorganiser les datasets pour que les écritures futures suivent les bonnes politiques.
- Fournir un point de rollback propre : si votre plan de réécriture tourne mal, vous pouvez revenir en réimportant le dataset reçu.
Strategy F: Don’t migrate recordsize; migrate the workload
Parfois le goulot n’est pas le recordsize. C’est :
- Une application mono-thread faisant de petites écritures synchrones.
- Une base configurée avec un ordonnancement IO hostile aux SSD.
- Un guest VM émettant des écritures aléatoires 4K sur un pool RAIDZ sans SLOG et avec de nombreux snapshots.
Si vous pouvez changer le workload (regrouper les écritures, changer la taille de page DB, ajuster les checkpoints, déplacer le WAL sur un dataset séparé),
vous pouvez garder le recordsize stable et obtenir un gain qui n’exige pas de réécritures risquées.
Trois mini-récits d’entreprise issus du terrain
Mini-récit 1 : L’incident causé par une mauvaise hypothèse
Une entreprise de taille moyenne faisait tourner une plateforme analytique interne sur ZFS. L’équipe stockage a remarqué une montée de la latence des requêtes et a vu une chorale de posts :
« Les bases aiment un recordsize plus petit. » Quelqu’un a changé le recordsize du dataset de 128K à 16K en plein cycle de release.
Rien ne s’est amélioré. Deux jours plus tard, c’était pire. L’équipe a conclu que « l’astuce 16K » ne fonctionnait pas et l’a rétabli.
Ce rollback n’a également rien fait. Ils avaient maintenant deux changements et zéro causalité.
Le vrai problème était un ensemble de scans séquentiels lourds en collision avec un job nocturne qui écrivait de gros fichiers parquet puis les relisait immédiatement.
Les fichiers chauds étaient principalement nouvellement générés et en streaming ; les 16K ont augmenté l’overhead des métadonnées et réduit l’efficacité du read-ahead.
Le pic de latence corrélait avec ce job, pas avec l’IO aléatoire de la base.
La mauvaise hypothèse était de penser que « base = IO aléatoire », mais le workload n’était pas OLTP ; c’était un mix analytique avec de fortes lectures séquentielles.
La correction était ennuyeuse : garder recordsize à 128K (ou plus pour le dataset de sortie parquet), isoler les sorties du job dans leur propre dataset,
et planifier les jobs concurrents pour qu’ils n’écrasent pas les mêmes files d’attente vdev.
L’action post-incident qui est restée : tout changement de recordsize nécessite deux mesures — distribution des tailles IO applicatives et ops/bande passante ZFS — avant et après.
Pas de mesure, pas de changement.
Mini-récit 2 : L’optimisation qui s’est retournée contre eux
Une autre organisation avait un grand dépôt de sauvegardes sur ZFS et voulait une réplication plus rapide. Quelqu’un a mis recordsize à 1M sur le dataset de backup.
Le débit de réplication a augmenté. Tout le monde a applaudi et est rentré tôt, ce qui est la meilleure façon d’inviter des ennuis.
Un mois plus tard, les restaurations de petits fichiers sont devenues sensiblement plus lentes. Pas catastrophique, juste agaçant — jusqu’à ce qu’un incident force la restauration de millions de petits fichiers de config
et de manifests de paquets. Le SLA de restauration a été manqué, et la direction s’en est préoccupée.
Le retour de flamme n’était pas que 1M soit « mauvais ». C’était que le dataset n’était pas uniquement des backups ; il contenait aussi un grand arbre de petits fichiers fréquemment consultés lors des restores.
Avec d’énormes enregistrements, lire un petit fichier entraînait souvent le chargement de plus de données que nécessaire et provoquait du churn ARC. Le système faisait plus de travail pour le même résultat.
La résolution a été de scinder le dépôt : un dataset optimisé pour les gros flux séquentiels de sauvegarde (1M), un autre pour les catalogues de petits fichiers et index (128K ou moins).
La réplication est restée rapide et le chemin de restauration a cessé de pénaliser l’accès aux petits fichiers.
La leçon opératoire : « notre dataset de backup » n’est pas une description de workload. C’est une étiquette comptable. Le recordsize n’obéit qu’aux workloads, pas aux étiquettes.
Mini-récit 3 : La pratique ennuyeuse mais correcte qui a sauvé la mise
Une entreprise régulée faisait tourner des services clients sur ZFS avec un contrôle des changements strict. Elle voulait migrer un dataset contenant des images VM
d’un pool legacy vers un nouveau pool avec du matériel plus rapide. Il y avait aussi le désir de « corriger recordsize pendant qu’on y est », parce qu’il y en a toujours un.
L’équipe a fait quelque chose d’impopulaire : elle a construit un dataset de staging et exécuté une charge canari dessus pendant une semaine.
Ils ont capturé des métriques zpool iostat, les tailles IO des guests et la latence p99 applicative. Ils ont aussi documenté la procédure de rollback
et l’ont testée deux fois.
Pendant le canary, ils ont appris que recordsize n’était même pas en jeu parce qu’il s’agissait de disques backés par zvols. Le bon réglage était volblocksize,
mais le changer nécessitait de recréer les zvols. C’est le genre de détail qui transforme un « changement simple » en « migration planifiée ».
Ils ont migré en créant de nouveaux zvols avec le bon volblocksize, effectué des copies au niveau stockage et basculé l’hyperviseur sur les nouveaux volumes.
Ennuyeux, prudent, et indéniablement correct. Ils ont aussi évité une panne causée par quelqu’un essayant de tuner recordsize sur le mauvais type d’objet.
La semaine de canary a paru longue. Elle les a sauvés d’un type de semaine bien plus long : celui où l’on reconstruit des systèmes sous pression.
Erreurs courantes : symptômes → cause racine → correction
Ces cas apparaissent dans de vrais timelines d’incident. Les symptômes sont familiers. Les causes racines sont souvent évitables. Les corrections sont spécifiques.
1) « On a changé recordsize, mais les performances n’ont pas changé du tout. »
- Symptômes : aucune amélioration après le changement ; les benchmarks sont identiques ; zdb montre d’anciennes tailles de blocs.
- Cause racine : les données existantes n’ont pas été réécrites ; recordsize n’affecte que les blocs nouvellement écrits.
- Correction : réécriture ciblée des données chaudes (copie vers un nouveau dataset) ou accepter la migration progressive via le churn. Vérifiez avec
zdbsur les fichiers chauds.
2) « Les petites écritures aléatoires sont lentes ; on a abaissé recordsize et c’est toujours lent. »
- Symptômes : latence élevée, ops élevées, faible débit ; pool RAIDZ ; réécritures fréquentes ; peut-être snapshots lourds.
- Cause racine : le surcoût de lecture-modification-écriture de la parité domine ; les écritures sync peuvent forcer le comportement du ZIL ; les snapshots augmentent la fragmentation.
- Correction : envisager des mirrors pour les datasets à écritures aléatoires lourdes ; isoler les composants sync-heavy ; évaluer le SLOG ; réduire le churn de snapshots sur les données chaudes.
3) « Le débit a chuté après avoir mis recordsize à 16K. »
- Symptômes : jobs séquentiels plus lents ; plus de CPU noyau ; IO métadonnées plus élevé ; moins d’efficacité de prefetch.
- Cause racine : le dataset est séquentiel/streaming mais recordsize a été réduit, augmentant le nombre de blocs et l’overhead.
- Correction : restaurez un recordsize plus grand (128K–1M) pour les datasets de streaming ; séparez les workloads en datasets distincts.
4) « On a réécrit les données et on a manqué d’espace. »
- Symptômes : le pool se remplit pendant la migration ; les suppressions ne libèrent pas d’espace ; tout empire rapidement.
- Cause racine : les snapshots épinglent les anciens blocs ; la réécriture duplique les données ; marge insuffisante.
- Correction : ajustez la rétention des snapshots pendant la migration ; clonez vers un nouveau dataset et basculez ; ajoutez de la capacité temporaire ; planifiez la marge avant la réécriture.
5) « Le special vdev est saturé après avoir tuné recordsize. »
- Symptômes : le device spécial montre un %util élevé ; latence métadonnées en pic ; latence globale du pool augmente.
- Cause racine :
special_small_blocksa fait atterrir plus de blocs de données sur le special vdev ; la distribution recordsize/petits-blocs a changé. - Correction : réévaluez le seuil
special_small_blocks; déplacez les données petites-blocs hors du spécial ; assurez-vous que le special vdev a la performance/endurance requise.
6) « On a tuné recordsize pour les VM et rien n’a changé. »
- Symptômes : performance VM inchangée ; vous avez changé recordsize sur un dataset qui stocke des zvols ou des fichiers creux avec d’autres patterns IO.
- Cause racine : les VM sont sur des zvols où
volblocksizecompte ; ou l’hyperviseur utilise des patterns direct IO non alignés avec vos hypothèses. - Correction : tunez volblocksize à la création des zvols ; utilisez des datasets séparés pour les images VM si basées sur des fichiers ; mesurez les tailles IO des guests.
Blague #2 : La façon la plus rapide de découvrir que vous n’avez pas assez de marge dans le pool est de lancer une « réécriture rapide » à 16h55 un vendredi.
Listes de contrôle / plan étape par étape
Voici le plan que j’aimerais avoir devant moi pendant une fenêtre de changement : assez court pour être utilisé, assez strict pour prévenir les blessures auto-infligées.
Checklist 1: Decide whether recordsize is actually the lever
- Mesurez la distribution des tailles IO (
iostat -x, métriques applicatives, métriques hyperviseur). - Mesurez ops ZFS vs bande passante (
zpool iostat -v). - Identifiez si vous êtes sync-bound (recherchez du fsync-heavy ; vérifiez présence/perf du SLOG).
- Confirmez le type de dataset (filesystem vs zvol).
- Confirmez snapshots et marge pour tout plan de réécriture.
Checklist 2: Choose a migration strategy
- Changement de politique (sans réécriture) : à utiliser quand les données churnent naturellement ou que le hot data est majoritairement de nouvelles écritures.
- Scinder les datasets : à utiliser quand vous avez des workloads mixtes dans un même dataset.
- Réécriture ciblée : à utiliser quand un répertoire/ensemble de fichiers spécifique cause la douleur.
- Migrer le workload : à utiliser quand l’application peut changer (batching, tailles de page, séparation du WAL).
- Migrer la topologie du pool : à utiliser quand l’overhead de parité est le problème et que vous avez besoin de mirrors ou d’un matériel différent.
Checklist 3: Execute a safe targeted rewrite cutover
- Créez un nouveau dataset avec le recordsize désiré et autres propriétés (compression, atime, xattr, etc.).
- Réalisez une petite copie canari et validez les tailles de bloc sur quelques fichiers chauds (
zdb -ddddd). - Benchmarkez l’application contre la nouvelle location (même jeu de requêtes, même charge VM, même job de backup).
- Planifiez la politique de snapshots : réduisez la rétention pendant la migration ou assurez la marge suffisante.
- Rsync/copiez le sous-ensemble chaud ; vérifiez checksums ou la cohérence au niveau application.
- Basculez via switch de mountpoint ; laissez l’ancien dataset en lecture seule pour rollback.
- Surveillez p95/p99 latence, profondeur de file du pool, special vdev, SLOG si présent.
- Après stabilisation, retirez les anciennes données et restaurez la rétention des snapshots.
Checklist 4: Post-change validation (don’t skip the boring part)
- Confirmez les sources de propriété : recordsize défini là où prévu, hérité là où prévu.
- Confirmez que les fichiers chauds sont écrits sous la nouvelle politique.
- Confirmez qu’aucun dataset inattendu n’a hérité du changement.
- Comparez métriques avant/après : ops, bande passante, latence, CPU.
- Documentez la topologie finale des datasets et pourquoi chaque recordsize existe.
FAQ
1) If I change recordsize, will my existing files be re-blocked automatically?
Non. Les blocs existants restent tels quels. Seuls les blocs nouvellement écrits suivent la nouvelle taille maximale.
Si vous avez besoin que des fichiers existants changent, vous devez les réécrire (directement ou indirectement) pour que ZFS alloue de nouveaux blocs.
2) What recordsize should I use for databases?
Cela dépend des schémas d’accès et de la taille de page de la base. Beaucoup de workloads OLTP bénéficient d’un recordsize plus petit (souvent 16K–32K),
mais les scans analytiques préfèrent souvent des tailles plus grandes. Mesurez d’abord les tailles IO et la latence ; testez ensuite sur un dataset de test.
3) Is 1M recordsize always best for backups?
Souvent, mais pas toujours. Si le dataset contient vraiment des flux séquentiels larges, 1M peut améliorer le débit et la compression.
Si ce dataset contient aussi des petits catalogues fréquemment consultés, séparez-les dans un autre dataset.
4) Can ZFS send/receive rewrite data into a new recordsize?
Pas à lui seul. Send/receive préserve la structure de blocs existante dans le stream.
Utilisez send/receive pour déplacer des datasets entre pools ou réorganiser ; utilisez des réécritures ciblées ou des rebuilds au niveau applicatif pour changer la taille des blocs sur disque.
5) Why does lowering recordsize sometimes make things slower?
Les enregistrements plus petits augmentent le nombre de blocs, l’overhead des métadonnées, et peuvent réduire l’efficacité du read-ahead pour les workloads séquentiels.
Vous échangez l’efficacité des mises à jour aléatoires contre le débit séquentiel. Si votre workload est majoritairement streaming, un petit recordsize est de l’auto-sabotage.
6) How do snapshots affect recordsize migration?
Les snapshots gardent les anciens blocs en vie. Si vous réécrivez des données pour forcer de nouvelles tailles de blocs, vous allouerez de nouveaux blocs pendant que les snapshots retiendront les anciens.
L’usage d’espace peut fortement augmenter. Planifiez la marge et la rétention des snapshots avant de réécrire.
7) Should I tune recordsize for VM disks?
Si vos disques VM sont des zvols, recordsize est sans objet ; volblocksize importe et est généralement choisi à la création du zvol.
Si les disques VM sont des fichiers dans un filesystem dataset, le recordsize peut compter — mais les patterns IO du guest dominent. Mesurez-les.
8) How can I tell if my hot files are using the new recordsize?
Utilisez zdb -ddddd sur des fichiers représentatifs et regardez les tailles des blocs de données (par ex. 16K vs 128K).
Aussi, copiez un petit jeu de test dans un nouveau dataset avec le recordsize désiré et comparez les performances.
9) Does compression change the “right” recordsize?
Ça peut. Les enregistrements plus grands compressent souvent mieux, ce qui peut réduire l’IO physique.
Mais si vous lisez fréquemment de petites tranches, vous pouvez payer le coût de décompression et l’amplification de lecture. Testez avec des schémas d’accès réels.
10) If recordsize is only a maximum, why do people obsess over it?
Parce qu’il influence fortement la façon dont ZFS organise les gros fichiers, et ce sont les gros fichiers qui cachent les problèmes de performance :
bases, images VM, gros logs, sauvegardes, index. L’obsession est justifiée ; le conseil simpliste ne l’est pas.
Étapes pratiques suivantes
Si vous ne retenez que trois choses : recordsize est un plafond, il ne réécrit pas les anciennes données, et la voie la plus rapide vers une migration sûre est la séparation des datasets plus les réécritures ciblées.
Tout le reste n’est que détail d’implémentation — et le détail d’implémentation est l’endroit où naissent les pannes.
- Mesurez d’abord : capturez la distribution des tailles IO, ops/bande passante du pool et le comportement sync.
- Délimitez le changement : confirmez les types de dataset et l’héritage pour ne pas « optimiser » vos logs en regression.
- Choisissez la migration la moins risquée : politique seulement pour les données churnantes, réécritures ciblées pour les hot subsets, scission de datasets pour les workloads mixtes.
- Validez avec des preuves :
zdbpour les tailles de blocs, p99 applicatif pour l’impact utilisateur, etzpool iostatpour le comportement du pool. - Notez ce que vous avez fait et pourquoi : le vous du futur sera fatigué, et les gens fatigués méritent une bonne documentation.