La déduplication ressemble à de l’argent gratuit jusqu’à ce que votre pool commence à avancer comme s’il était sous l’eau.
Le mode de défaillance le plus courant n’est pas « la déduplication ne fonctionne pas », mais « la déduplication fonctionne, puis le DDT ne tient plus en RAM,
et chaque écriture aléatoire se transforme en une petite chasse au trésor sur disque ».
Si vous êtes ici parce que quelqu’un a demandé « Peut-on juste activer la dédup ? », vous êtes déjà en avance. La bonne réponse est :
« Peut-être, après avoir prédit la taille du DDT et prouvé que la machine peut la porter. » Faisons-le avec des chiffres que vous pourrez défendre
lors d’une revue de changement.
DDT, ce que c’est (et pourquoi ça fait mal)
La déduplication ZFS est au niveau bloc : quand vous écrivez un bloc, ZFS le hache et vérifie si ce contenu existe déjà.
Si c’est le cas, ZFS stocke une référence au lieu d’une copie supplémentaire. L’index qui rend cela possible est la
Deduplication Table (DDT). Chaque bloc unique a une entrée. Chaque bloc partagé ajoute des références.
Cette table n’est pas un accessoire mignon. C’est une dépendance centrale du chemin d’écriture. Quand la dédup est activée, un écriture
devient « hachage + recherche + éventuelle allocation + mise à jour des compteurs de références ». Si la recherche DDT est rapide (hit ARC), la dédup peut être
tolérable. Si la recherche est lente (miss DDT dans l’ARC → lecture disque), les performances chutent dramatiquement.
Voici la vérité opérationnelle : la dédup est essentiellement un problème de dimensionnement mémoire déguisé en fonctionnalité de stockage.
Cela peut aussi être un problème de dimensionnement IOPS, de latence, ou de « expliquer à la direction pourquoi les sauvegardes sont en retard ».
Mais tout commence par la RAM.
Une chose que l’on oublie souvent : activer la dédup est par dataset, mais le DDT est effectivement par pool. Un dataset trop zélé
peut empoisonner le comportement du cache du pool en gonflant l’ensemble de travail DDT.
Faits intéressants et contexte à utiliser en réunion
- La déduplication précède ZFS dans l’esprit : le stockage adressé par contenu et les concepts de dédup basés sur des hachages étaient utilisés dans des appliances de sauvegarde bien avant d’être courants dans les systèmes de fichiers.
- La dédup ZFS des débuts a une réputation : beaucoup de déploiements de première vague l’ont activée sur des pools généralistes et ont découvert que « ça marche » et « c’est rapide » ne sont pas synonymes.
- Le DDT suit les blocs uniques, pas la taille logique : deux datasets de 1 To peuvent produire des tailles DDT très différentes selon recordsize, compression et churn.
- La compression change les maths de la dédup : ZFS hache le bloc stocké (après compression), donc des données identiques non compressées qui se compressent différemment ne se dédupliqueront pas comme prévu.
- La taille de bloc est déterminante : des recordsize plus petites augmentent le nombre de blocs, ce qui augmente les entrées DDT et les besoins en RAM. C’est pourquoi le stockage de VM est un piège courant pour la dédup.
- Le DDT est des métadonnées, mais toutes les métadonnées ne se valent pas : si vous ajoutez un special vdev pour les métadonnées, le DDT peut y atterrir et réduire les I/O aléatoires sur des disques lents.
- La dédup n’est pas gratuite en lecture : elle peut amplifier la fragmentation et augmenter l’indirection, surtout après beaucoup de snapshots et de suppressions.
- Dédup et chiffrement ne sont pas amis par défaut : si les données sont chiffrées avant que ZFS ne les voie (au niveau application), des textes identiques sembleront différents et la dédup sera quasi nulle.
La règle empirique qui fait licencier (et celle qui ne le fait pas)
La mauvaise règle empirique : « La dédup nécessite environ 1–2 Go de RAM par To. » Vous l’entendrez répétée avec la confiance
d’une personne qui ne s’est jamais fait appeler en urgence à cause de ça.
Pourquoi c’est faux : cela suppose une certaine recordsize, un certain ratio de duplication, un certain churn, et une certaine taille d’entrée DDT en mémoire. Changez l’un de ces éléments et votre chiffre « par To » devient fictif.
La bonne règle empirique : dimensionnez la RAM pour les entrées DDT, pas pour les téraoctets bruts. Estimez le nombre de blocs,
estimez le nombre de blocs uniques, multipliez par un coût mémoire par entrée réaliste, puis appliquez un facteur d’ensemble de travail.
Si vous ne pouvez pas estimer le nombre de blocs, vous n’êtes pas prêt à activer la dédup.
Blague #1 : La dédup sans dimensionnement du DDT, c’est comme acheter un camion en fonction du volume de chargement en ignorant la limite de poids du pont. Le pont gagne toujours.
Une méthode de dimensionnement défendable : estimer les entrées DDT et la RAM
Étape 1 : Estimer combien de blocs vous allez dédupliquer
Les entrées DDT évoluent grossièrement avec le nombre de blocs uniques écrits dans les datasets avec dédup activée.
Vous pouvez approximer le nombre de blocs comme :
Nombre de blocs ≈ octets logiques référencés / taille moyenne de bloc
« Taille moyenne de bloc » n’est pas nécessairement votre recordsize ou volblocksize, parce que les blocs réels sont
plus petits en queue de fichier, les métadonnées ne sont pas comptées de la même façon, et la compression change la taille stockée. Mais pour
un chiffre de planification, partez de la taille de bloc configurée pour le type de dataset :
- zvols VM :
volblocksize(souvent 8K–32K dans des configurations orientées performance) - Datasets de fichiers :
recordsize(souvent 128K par défaut)
Étape 2 : Estimer la fraction unique (ratio de dédup)
Le ratio de dédup vous dit combien de données sont dupliquées. Mais pour dimensionner le DDT vous voulez l’inverse :
la fraction qui est unique.
Exemple : si vous attendez un ratio de 2.0x, alors environ 50% des blocs sont uniques. Si vous attendez 1.1x, alors environ 91%
sont uniques, ce qui signifie que presque chaque bloc nécessite sa propre entrée DDT et vous n’avez presque rien économisé.
Étape 3 : Estimer la mémoire DDT par entrée
Le format des entrées DDT et le surcoût en mémoire varient selon la version d’OpenZFS, les feature flags, et selon que vous comptez
seulement l’entrée sur disque ou les structures en mémoire dans l’ARC (qui incluent tables de hachage, pointeurs et comportement d’éviction). En pratique, les planificateurs utilisent une fourchette conservatrice.
Nombres de planification opérationnellement sûrs :
- Minimum optimiste : ~200 octets par bloc unique (rarement sûr pour la planification production)
- Planification commune : ~320 octets par bloc unique
- Planification paranoïaque : 400–500 octets par bloc unique (à utiliser pour des pools VM à fort churn)
Si vous vous êtes déjà fait avoir, utilisez la planification paranoïaque. La RAM coûte moins cher que votre temps.
Étape 4 : Appliquer un facteur d’ensemble de travail
Le DDT n’a pas besoin d’être 100% résident pour fonctionner, mais les performances dépendent de la fréquence à laquelle le chemin d’écriture
a besoin d’une entrée DDT qui n’est pas dans l’ARC. Si votre charge est principalement séquentielle et en append-only, vous pouvez parfois
vous en sortir avec un ensemble de travail DDT partiel. Si votre charge est des écritures aléatoires sur un large espace d’adresses (bonjour,
virtualisation), vous voulez une grande fraction du DDT chaude dans l’ARC.
Conseils pratiques :
- Images VM, bases de données, runners CI : visez 80–100% du DDT dans l’ensemble de travail ARC
- Cibles de sauvegarde, ingestion majoritairement séquentielle : 30–60% peut être survivable si le DDT est sur un média rapide
- Charges mixtes : partez du pire cas sauf si vous pouvez isoler la dédup dans un pool séparé
Étape 5 : Ajouter une marge pour l’ARC, les métadonnées et le reste de l’OS
Même si le DDT tient, vous avez encore besoin d’ARC pour d’autres métadonnées (dnode cache, dbufs), et vous avez besoin de RAM pour le noyau,
les services et les pics. Si la mémoire est surengagée, ZFS arrêtera poliment de mettre en cache avant de commencer à swapper. Ensuite votre
pool commencera à vous détester personnellement.
Un exemple concret
Supposons que vous prévoyez de dédupliquer 100 To de données référencées sur un dataset avec recordsize 128K. Approximation des blocs :
100 To / 128K ≈ 800 millions de blocs. Si le ratio de dédup est 2.0x, blocs uniques ≈ 400 millions.
RAM DDT à 320 octets/entrée : 400 000 000 × 320 ≈ 128 Gio. Ce n’est que le DDT. Si vous avez besoin de 80% de résidency dans l’ensemble de travail,
vous voudrez encore ~100 Gio d’ARC disponible pour lui, plus tout le reste.
Si votre recordsize est 16K à la place, le nombre de blocs est 8× plus élevé. Même données, même ratio de dédup, maintenant le chiffrage DDT ressemble à un acompte pour une petite maison. Voilà pourquoi le dimensionnement « par To » ment.
Formes de charge : quand la dédup se comporte et quand elle mord
Bonnes candidates (parfois)
- Clones VDI avec images de base identiques où la plupart des blocs correspondent réellement et restent stables.
- Dépôts de sauvegarde où vous écrivez de grands blocs répétitifs et rarement réécrivez d’anciennes données.
- Stores d’artefacts où les mêmes binaires atterrissent de façon répétée et sont essentiellement immuables.
Mauvaises candidates (souvent)
- Stockage VM général avec petits blocs et churn constant : mises à jour OS, swap, logs, bases de données.
- Bases de données qui réécrivent fréquemment des pages. Vous dédupliquerez moins que prévu et paierez les coûts de recherche indéfiniment.
- Données chiffrées à la source (chiffrement applicatif) qui détruisent la redondance.
Le churn est le tueur silencieux
La dédup aime les duplications stables. Le churn change les hachages. Même si votre ratio de dédup est correct à un instant T, un churn élevé force des mises à jour fréquentes du DDT, augmente l’amplification d’écriture et génère plus d’I/O aléatoires. Les snapshots peuvent préserver d’anciens blocs, ce qui rend le DDT plus grand et le pool plus fragmenté. Vos « économies » deviennent une « dette de métadonnées ».
Special vdev, métadonnées et où vit vraiment le DDT
Le DDT est stocké sur disque comme partie des métadonnées du pool. Lorsqu’il n’est pas dans l’ARC, ZFS doit le récupérer depuis le disque. Si ce disque
est un ensemble de HDD, vos recherches de dédup s’aligneront et feront la queue pour des accès à 10 ms chacune. C’est comme ça que vous obtenez des latences de plusieurs secondes sur du matériel par ailleurs correct.
Un special vdev peut contenir des métadonnées (et éventuellement des petits blocs), ce qui peut inclure des blocs DDT. Placer
le DDT sur un SSD/NVMe rapide peut réduire drastiquement la pénalité quand il sort de l’ARC. Ça ne supprime pas le besoin de RAM, mais ça peut transformer « catastrophique » en « ennuyeux et mesurable ».
Conseils d’opinion :
- Si vous envisagez la dédup sur du rust (HDD), prévoyez un special vdev ou ne faites pas de dédup.
- Si vous avez un special vdev, mettez-le en miroir. Sa perte peut signifier la perte du pool, selon la configuration et ce qui y a été déplacé.
- Ne traitez pas le special vdev comme un « accélérateur magique de dédup ». C’est un réducteur de latence pour les misses, pas un substitut aux hits ARC.
Tâches pratiques : commandes, sorties et décisions (12+)
Voilà les choses que j’exécute réellement avant que quiconque n’active la dédup. Chaque tâche inclut : une commande, ce que signifie la sortie,
et la décision qu’elle entraîne. Remplacez les noms de pool/dataset pour correspondre à votre environnement.
Task 1: Confirm dedup is currently off (or where it’s on)
cr0x@server:~$ zfs get -r -o name,property,value,source dedup tank
NAME PROPERTY VALUE SOURCE
tank dedup off default
tank/vm dedup off default
tank/backups dedup off default
Signification : La dédup est désactivée partout où c’est affiché. Si certains datasets affichent on, vous avez déjà un DDT.
Décision : Si un dataset est on, vous devez mesurer le DDT existant et son comportement de cache avant d’étendre l’usage.
Task 2: Check pool feature flags relevant to dedup
cr0x@server:~$ zpool get -H -o property,value feature@extensible_dataset tank
feature@extensible_dataset active
Signification : Les feature flags affectent les formats sur disque et parfois le comportement. Cela confirme que le pool utilise des features modernes.
Décision : Si vous êtes sur une version de pool ancienne ou en mode compatibilité, reconsidérez la dédup ou planifiez d’abord une migration.
Task 3: Capture recordsize / volblocksize to estimate block count
cr0x@server:~$ zfs get -o name,property,value recordsize tank/backups
NAME PROPERTY VALUE
tank/backups recordsize 1M
Signification : Un recordsize de 1M signifie moins de blocs plus larges : le DDT croît plus lentement que sur des workloads 128K/16K.
Décision : Les workloads à recordsize large sont plus favorables à la dédup du point de vue du dimensionnement DDT.
Task 4: For zvols, check volblocksize (this is where trouble starts)
cr0x@server:~$ zfs get -o name,property,value volblocksize tank/vm/zvol0
NAME PROPERTY VALUE
tank/vm/zvol0 volblocksize 8K
Signification : Des blocs de 8K signifient un énorme nombre de blocs pour des données logiques volumineuses. Cela gonfle rapidement les entrées DDT.
Décision : Si vous prévoyez de dédupliquer des zvols 8K, supposez un octet-par-entrée paranoïaque et un besoin de résidence proche de 100%.
Task 5: Measure referenced bytes (what dedup must index)
cr0x@server:~$ zfs list -o name,used,refer,logicalused,logicalrefer -p tank/vm
NAME USED REFER LOGICALUSED LOGICALREFER
tank/vm 54975581388800 54975581388800 87960930222080 87960930222080
Signification : Les données référencées logiquement sont ~80 To. La taille du DDT se corrèle davantage avec les blocs logiques qu’avec les octets physiques.
Décision : Utilisez logicalrefer et la taille de bloc pour estimer le nombre de blocs. Si des snapshots existent, considérez aussi la croissance des données référencées.
Task 6: Check compression (it changes stored blocks and dedup effectiveness)
cr0x@server:~$ zfs get -o name,property,value compression tank/vm
NAME PROPERTY VALUE
tank/vm compression lz4
Signification : LZ4 est bon. La compression peut réduire les I/O physiques, mais la dédup hache les blocs compressés.
Décision : Si la compression est désactivée, pensez à l’activer avant la dédup. Si la compression est déjà efficace, l’avantage marginal de la dédup peut être faible.
Task 7: Inspect ARC size and pressure (can it even hold a big DDT?)
cr0x@server:~$ arcstat.py 1 3
time read miss miss% dmis dm% pmis pm% mmis mm% arcsz c
12:40:01 482 41 8 3 7% 8 20% 30 73% 96G 110G
12:40:02 501 45 9 4 9% 7 16% 34 76% 96G 110G
12:40:03 490 42 9 3 7% 6 14% 33 79% 96G 110G
Signification : L’ARC est ~96G avec un target ~110G. Taux de misses ~8–9% pendant l’échantillonnage. C’est correct, mais cela ne renseigne pas encore sur la résidence du DDT.
Décision : Si l’ARC est déjà contraint, activer la dédup luttera pour le cache avec tout le reste. Planifiez des montées de RAM ou isolez la dédup dans un pool séparé.
Task 8: Check whether swap is in use (a red flag for dedup plans)
cr0x@server:~$ free -h
total used free shared buff/cache available
Mem: 256Gi 142Gi 12Gi 3.0Gi 102Gi 104Gi
Swap: 16Gi 0.0Gi 16Gi
Signification : Pas d’utilisation de swap. Bien. Si le swap est utilisé en charge normale, vous manquez déjà de mémoire.
Décision : Si le swap est actif, n’activez pas la dédup avant d’avoir réglé la pression mémoire. Dédup + swap = légende d’opération.
Task 9: Estimate duplication potential without enabling dedup (sample with hashes)
cr0x@server:~$ find /tank/backups -type f -size +128M -print0 | xargs -0 -n1 shasum | awk '{print $1}' | sort | uniq -c | sort -nr | head
12 0c4a3a3f7c4d5f0b3e1d6f1c3b9b8a1a9c0b5c2d1e3f4a5b6c7d8e9f0a1b2c
8 3f2e1d0c9b8a7f6e5d4c3b2a19080706050403020100ffeeddccbbaa998877
5 aabbccddeeff00112233445566778899aabbccddeeff001122334455667788
Signification : Cet échantillon grossier montre des fichiers volumineux dupliqués (même hash apparaissant plusieurs fois). Ce n’est pas de la dédup au niveau bloc, mais c’est un test d’odeur.
Décision : Si vous ne voyez presque aucune duplication même au niveau fichier, la dédup au niveau bloc ne justifiera probablement pas son coût.
Task 10: If dedup already exists, measure DDT size and hits
cr0x@server:~$ zpool status -D tank
pool: tank
state: ONLINE
config:
NAME STATE READ WRITE CKSUM
tank ONLINE 0 0 0
raidz2-0 ONLINE 0 0 0
sda ONLINE 0 0 0
sdb ONLINE 0 0 0
sdc ONLINE 0 0 0
sdd ONLINE 0 0 0
sde ONLINE 0 0 0
sdf ONLINE 0 0 0
dedup: DDT entries 182345678, size 54.3G on disk, 76.9G in core
DDT histogram (aggregated over all DDTs):
bucket allocated referenced
______ ______________________________ ______________________________
refcnt blocks LSIZE PSIZE DSIZE blocks LSIZE PSIZE DSIZE
------ ------ ----- ----- ----- ------ ----- ----- -----
1 141M 17.6T 12.4T 12.5T 141M 17.6T 12.4T 12.5T
2 31.2M 3.9T 2.7T 2.7T 62.4M 7.8T 5.4T 5.5T
4 7.8M 1.0T 0.7T 0.7T 31.2M 4.0T 2.8T 2.8T
Signification : « in core » est l’utilisation mémoire des structures DDT actuellement mises en cache. Si cela approche l’espace libre ARC disponible, vous vivez dangereusement.
L’histogramme montre où la dédup rapporte : des buckets avec un refcount élevé signifient des blocs partagés.
Décision : Si « in core » concurrence l’ARC et que votre latence est déjà tendue, ajoutez de la RAM ou déplacez le DDT vers un stockage plus rapide (special vdev).
Task 11: Watch latency and queueing during workload (dedup will amplify this)
cr0x@server:~$ iostat -x 1 3
avg-cpu: %user %nice %system %iowait %steal %idle
6.12 0.00 3.44 8.70 0.00 81.74
Device r/s w/s rkB/s wkB/s rrqm/s wrqm/s %util aqu-sz await
sda 12.0 95.0 512 8200 0.0 2.0 84.0 6.2 55.1
sdb 10.0 90.0 488 7900 0.0 1.0 79.0 5.7 50.3
Signification : Un await élevé et une aqu-sz croissante indiquent que les disques font la queue. Les misses de dédup ajoutent des lectures aléatoires, augmentant la mise en file.
Décision : Si vous poussez déjà fortement les disques, la dédup dégradera probablement la latence sauf si vous déplacez les lectures DDT sur un média rapide et le maintenez chaud dans l’ARC.
Task 12: Confirm special vdev presence and ashift (metadata speed plan)
cr0x@server:~$ zpool status tank
pool: tank
state: ONLINE
config:
NAME STATE READ WRITE CKSUM
tank ONLINE 0 0 0
raidz2-0 ONLINE 0 0 0
sda ONLINE 0 0 0
sdb ONLINE 0 0 0
sdc ONLINE 0 0 0
sdd ONLINE 0 0 0
sde ONLINE 0 0 0
sdf ONLINE 0 0 0
special
mirror-1 ONLINE 0 0 0
nvme0n1 ONLINE 0 0 0
nvme1n1 ONLINE 0 0 0
Signification : Un special vdev en miroir existe. Bien. C’est votre « voie rapide métadonnées ».
Décision : Si vous prévoyez la dédup, validez que les blocs DDT sont éligibles pour le special vdev et qu’il dispose d’endurance et de marge de capacité.
Task 13: Check special_small_blocks setting (don’t accidentally move data you didn’t intend)
cr0x@server:~$ zfs get -o name,property,value special_small_blocks tank
NAME PROPERTY VALUE
tank special_small_blocks 0
Signification : Seules les métadonnées vont sur le special vdev ; les petits blocs de données n’y vont pas. C’est plus sûr pour la planification de capacité.
Décision : Envisagez de le laisser à 0 sauf si vous avez un objectif clair et un modèle de capacité. Déplacer les petits blocs vers le special peut être excellent ou catastrophique selon les taux de remplissage.
Task 14: Model DDT entry count from block size and logical data
cr0x@server:~$ python3 - <<'PY'
logical_tb = 80
block_kb = 8
dedup_ratio = 1.3
logical_bytes = logical_tb * (1024**4)
block_bytes = block_kb * 1024
blocks = logical_bytes / block_bytes
unique_blocks = blocks / dedup_ratio
for bpe in (320, 450):
ddt_gb = unique_blocks * bpe / (1024**3)
print(f"blocks={blocks:,.0f} unique={unique_blocks:,.0f} bytes_per_entry={bpe} => DDT~{ddt_gb:,.1f} GiB")
PY
blocks=10,737,418,240 unique=8,259,552,492 bytes_per_entry=320 => DDT~2,462.0 GiB
blocks=10,737,418,240 unique=8,259,552,492 bytes_per_entry=450 => DDT~3,461.9 GiB
Signification : C’est la sortie « ne le faites pas ». 80 To à des blocs 8K avec seulement 1.3x de dédup implique des DDT en mémoire de plusieurs téraoctets. Ce n’est pas un problème de tuning.
Décision : N’activez pas la dédup sur ce workload. Changez la conception : blocs plus grands, technologie de stockage différente, ou acceptez le coût en capacité.
Task 15: Check snapshot count and churn potential
cr0x@server:~$ zfs list -t snapshot -o name,used,refer -S creation | head -n 5
NAME USED REFER
tank/vm@daily-2025-12-26 0 80T
tank/vm@daily-2025-12-25 0 79T
tank/vm@daily-2025-12-24 0 79T
tank/vm@daily-2025-12-23 0 78T
Signification : Beaucoup de snapshots peuvent épingler d’anciens blocs, maintenant des entrées DDT vivantes et empêchant la libération d’espace. Même des snapshots avec « USED 0 » peuvent représenter de grands arbres référencés.
Décision : Si la rétention est longue et le churn élevé, supposez que le DDT grandit et reste volumineux. Envisagez de limiter la dédup aux datasets avec des politiques de snapshot contrôlées.
Playbook de diagnostic rapide : trouver le goulot en quelques minutes
Quand les pools avec dédup activée ralentissent, les gens devinent. Ne le faites pas. Voici le chemin le plus rapide vers la vérité, dans l’ordre.
Premièrement : Le DDT tient-il dans l’ARC ou thrashing ?
- Exécutez
zpool status -Det regardez la taille « in core ». Si elle est énorme par rapport à l’ARC disponible, vous êtes en danger. - Vérifiez les stats ARC pendant la charge (
arcstat.py) : si miss% explose quand les écritures augmentent, c’est votre signe. - Cherchez une latence soutenue élevée même à faible débit : symptôme classique des recherches de métadonnées qui bloquent les écritures.
Deuxièmement : Les misses frappent-ils un média lent ?
- Exécutez
iostat -xet surveillezawait/aqu-szsur les vdevs. - Si vous avez un special vdev, vérifiez s’il est lui-même saturé. Si ce sont des HDD qui sont saturés, le DDT provient probablement du rust.
- Faible CPU + fort iowait + await disque élevé = signature de douleur de dédup.
Troisièmement : Le système manque-t-il de mémoire ou swappe-t-il ?
free -hetvmstat 1: toute activité de swap en charge normale est un drapeau rouge.- Si l’ARC est limité ou réduit agressivement, la dédup empirera à mesure que le DDT s’évincera.
Quatrièmement : Le workload est-il simplement un mauvais candidat pour la dédup ?
- Vérifiez l’histogramme DDT : si la plupart des blocs sont refcnt=1, la dédup est surtout un surcoût.
- Vérifiez le ratio de dédup depuis
zfs get dedup(si déjà activé) et comparez à vos attentes. - Si la dédup est ~1.0x–1.2x, vous payez beaucoup pour économiser très peu.
Si vous êtes bloqué : capturez un court snapshot de performance en charge et décidez si vous êtes lié par la mémoire (misses DDT),
par les IOPS (queues disques), ou par la conception (faible duplication). Ne tunnez pas à l’aveugle.
Erreurs courantes : symptôme → cause → correction
1) Les écritures deviennent saccadées et lentes après activation de la dédup
Symptôme : La latence augmente, le débit s’effondre, le CPU est tranquille, les disques montrent des lectures aléatoires.
Cause : L’ensemble de travail DDT ne tient pas dans l’ARC ; les recherches de dédup missent et récupèrent des blocs DDT depuis le disque.
Correction : Ajouter de la RAM (solution réelle), ou déplacer les métadonnées/DDT vers un special vdev en miroir (atténuation), ou désactiver la dédup et réécrire les données sans dédup (douloureux mais honnête).
2) Le ratio de dédup est décevant (près de 1.0x) mais les performances sont pires
Symptôme : Le pool a ralenti ; les économies d’espace sont minimes.
Cause : Le workload a une duplication réelle faible (données chiffrées, compression différente, churn élevé), mais la dédup impose quand même des recherches et des mises à jour de refcount.
Correction : Arrêtez d’utiliser la dédup pour ce dataset. Utilisez la compression, de meilleurs choix de recordsize, ou une dédup applicative (logiciel de sauvegarde) adaptée.
3) Le pool tourne bien pendant des semaines, puis empire « sans raison »
Symptôme : Pool en dégradation progressive ; les redémarrages aident temporairement.
Cause : Le DDT grandit avec de nouveaux blocs uniques, les snapshots épinglent d’anciens blocs, la pression ARC augmente jusqu’à ce que les misses dominent.
Correction : Revoir la rétention et le churn. Ajouter de la RAM, raccourcir la rétention des snapshots sur les datasets dédupliqués, et s’assurer que le DDT est sur un média rapide. Envisagez de séparer les workloads en pools distincts.
4) Le special vdev se remplit et le pool panique opérationnellement
Symptôme : Le special vdev atteint une forte utilisation ; les allocations de métadonnées échouent ; les performances s’effondrent.
Cause : Special vdev mal dimensionné, ou special_small_blocks a déplacé plus de données que prévu, ou la croissance des métadonnées de la dédup a dépassé le plan.
Correction : Planifiez la capacité du special vdev avec marge. Mettez-le en miroir. S’il est déjà trop petit, migrez les données vers un nouveau pool ; étendre un special vdev n’est pas toujours trivial selon la disposition.
5) « On ajoutera de la RAM plus tard » se transforme en calcul de temps d’arrêt
Symptôme : L’approbation du changement suppose que la RAM peut être ajoutée sans impact, mais les fenêtres de maintenance sont rares.
Cause : Dédup activée sans budget capacité/performance ferme ; maintenant vous ne pouvez pas revenir facilement parce que les données sont déjà dédupliquées.
Correction : Traitez la dédup comme une décision architecturale. Si vous devez tester, faites-le sur un pool clone ou sur un dataset limité avec un plan de rollback.
Trois mini-récits d’entreprise tirés du terrain
Mini-récit 1 : L’incident causé par une mauvaise hypothèse
Une entreprise de taille moyenne a consolidé du « stockage divers » sur un seul pool ZFS. Il hébergeait des images VM, des artefacts CI et quelques
répertoires de sauvegarde. L’équipe de stockage a vu des fichiers ISO dupliqués et des templates VM répétés et a décidé que la dédup serait un gain propre. La demande de changement disait, essentiellement, « la dédup réduit l’espace ; impact perf attendu minimal ».
L’hypothèse fausse était subtile : ils ont dimensionné la dédup par To bruts et supposé un surcoût RAM modeste. Ils n’ont pas modélisé le
nombre de blocs. Le côté VM utilisait des zvols avec petits blocs, et le schéma d’écriture était aléatoire. En quelques heures, les alarmes de latence
ont commencé à se déclencher sur des systèmes sans lien. Les builds ont ralenti. Les consoles VM se figeaient par intermittence. Personne n’a pu corréler car rien de « gros » n’avait changé—pas de nouveau hardware, pas de release applicative.
Le SRE on-call a extrait la télémétrie de base : le CPU était inoccupé, le réseau calme, les disques hurlaient. Les lectures aléatoires ont explosé sur
les vdevs HDD, et l’await moyen est monté dans les dizaines de millisecondes. La machine n’était pas à court d’espace. Elle était à court de métadonnées rapides. Les recherches DDT missaient l’ARC et touchaient le rust.
La correction n’était pas brillante. Ils ont désactivé la dédup pour les écritures futures et commencé à migrer les pires éléments vers un pool non-dedup.
Cela a pris du temps, car les blocs déjà dédupliqués ne se « undedupliquent » pas sans être réécrits. L’action post-mortem a été claire : les changements de dédup exigent une estimation DDT basée sur le nombre de blocs et le churn, revue comme un plan de capacité, pas un simple toggle.
Mini-récit 2 : L’optimisation qui a mal tourné
Une autre organisation a tenté d’être astucieuse. Ils savaient que les recherches de dédup étaient coûteuses, alors ils ont ajouté un special vdev sur des SSD rapides
et ont activé special_small_blocks pour déplacer aussi les petits blocs, raisonnant que « les petits I/O aléatoires devraient aller sur SSD ».
Sur le papier, cela semblait du génie performance.
En réalité, leurs charges avaient beaucoup de petits blocs. Pas seulement des métadonnées, mais des données réelles : logs, caches de paquets, petits artefacts, et le bruit des systèmes de fichiers VM. Le special vdev a commencé à se remplir plus vite que prévu. À mesure qu’il approchait d’une utilisation inconfortable, le comportement d’allocation est devenu tendu. La latence est devenue erratique, et le risque opérationnel a augmenté : le special vdev était désormais critique pour bien plus du dataset qu’intentionné.
Le retour de bâton n’était pas que les SSD sont mauvais. C’était le fait d’avoir traité le special vdev comme un tiroir à bazar de performances. Ils ont mélangé des objectifs (accélérer les misses métadonnées, accélérer les petits blocs, accélérer la dédup) sans un modèle de capacité unifié. Quand on fait ça, on n’a pas de conception. On a une surprise.
Le plan de récupération a été ennuyeux mais douloureux : migrer vers un nouveau pool avec un special vdev correctement dimensionné, garder
special_small_blocks conservateur, et traiter la dédup comme un outil limité aux datasets avec duplication prouvée. Les performances se sont améliorées, mais le vrai gain a été de redevenir lisible dans l’enveloppe de risque.
Mini-récit 3 : La pratique ennuyeuse mais correcte qui a sauvé la mise
Une grande équipe plateforme interne voulait la dédup pour un environnement de type VDI où les images de base étaient identiques et les cycles de patchs prévisibles. Ils étaient tentés d’activer la dédup largement, mais un ingénieur a insisté pour une approche progressive :
créer un pool séparé dédié aux candidats à la dédup, collecter une semaine de traces de charge, et dimensionner la RAM en utilisant un octets-par-entrée conservateur avec marge.
Ils ont démarré par un pilote : un sous-ensemble de postes et seulement les volumes dérivés du template. Ils ont surveillé zpool status -D
quotidiennement, suivi les ratios de hit ARC pendant les pics de connexion, et mesuré la latence au niveau hyperviseur. Ils ont aussi défini un critère de rollback
strict : si la latence d’écriture au 95ᵉ percentile dépassait un seuil pendant plus de quelques minutes, le pilote serait arrêté et les données redirigées.
Rien de dramatique ne s’est produit. C’est le but. Le DDT a grandi comme prévu, l’ARC est resté sain, et le special vdev a géré les misses occasionnels sans traîner les HDD dans la mêlée. Les économies de dédup étaient réelles parce que la charge était effectivement dupliquée et relativement stable.
Quand la direction a demandé pourquoi le déploiement avait pris plus de temps que de basculer une propriété, la réponse a été simple : « Nous payons pour la certitude. » La pratique ennuyeuse—pilote + mesure + dimensionnement conservateur—a évité de transformer un projet de capacité en projet d’intervention incident.
Checklists / plan étape par étape
Checklist pré-vol (avant toute demande de changement)
- Lister les datasets candidats et confirmer la portée de la dédup. N’activez pas accidentellement pool-wide.
- Enregistrer les tailles de bloc :
recordsizepour fichiers,volblocksizepour zvols. - Mesurer
logicalreferpour chaque dataset et les schémas de rétention des snapshots. - Estimer le ratio de dédup de façon réaliste (échantillonner, ou utiliser le comportement connu du workload comme les templates VDI).
- Calculer les blocs uniques estimés et la RAM DDT (octets-par-entrée commun et paranoïaque).
- Vérifier l’espace ARC actuel et la pression mémoire du système.
- Décider où tomberont les misses DDT : HDD vs special vdev vs tout-flash.
- Noter les critères de rollback : seuils de latence, taux de misses, triggers d’impact métier.
Plan pilote (la manière sûre d’apprendre)
- Créer un dataset dédié (ou un pool dédié si possible) pour les candidats à la dédup.
- Activer la dédup seulement sur ce dataset et migrer un sous-ensemble représentatif de données.
- Surveiller la croissance du DDT quotidiennement via
zpool status -Det suivre « in core » vs taille ARC. - Mesurer la latence applicative au niveau perçu par les utilisateurs (latence IO VM, temps de commit DB), pas seulement les stats ZFS.
- Stresser une période de pic intentionnellement (rush de connexions, fenêtre de job batch) et observer le comportement en pire cas.
- Élargir la portée seulement après pouvoir prédire la croissance du DDT dans le temps, pas seulement au jour 1.
Plan de déploiement en production (ce que je signerais)
- S’assurer que la capacité RAM répond à l’objectif conservateur d’ensemble de travail DDT plus la marge ARC.
- S’assurer que le special vdev (si utilisé) est en miroir et dimensionné avec une grande marge pour la croissance des métadonnées.
- Activer la dédup sur un ensemble limité de datasets ; ne pas la propager par héritage partout sauf si vous aimez les surprises.
- Définir explicitement les politiques de rétention des snapshots pour les datasets dédupliqués afin de contrôler la croissance DDT à long terme.
- Planifier des points de validation post-changement (1 jour, 1 semaine, 1 mois) avec les mêmes métriques à chaque fois.
Blague #2 : La dédup est la seule fonctionnalité où économiser de l’espace peut vous coûter du temps, de l’argent et l’opinion de plusieurs collègues.
FAQ
1) Puis-je prédire la taille du DDT précisément avant d’activer la dédup ?
Précisément, non. Vous pouvez le prédire suffisamment bien pour prendre une décision sûre en estimant le nombre de blocs, la fraction unique,
et en utilisant des octets-par-entrée conservateurs. L’approche pilote vous rapproche de la « précision » car elle mesure le comportement réel dans le temps.
2) « Le DDT doit tenir en RAM » est-ce toujours vrai ?
Pour de bonnes performances sur des workloads à écritures aléatoires, fonctionnellement oui : l’ensemble de travail chaud doit tenir. Si le workload est
principalement d’ingestion séquentielle et que les blocs DDT sont sur un média rapide, une résidence partielle peut être survivable. Mais « survivable » n’est pas synonyme d’« agréable ».
3) Quel est un nombre raisonnable d’octets par entrée DDT ?
Utilisez 320 octets comme base commune de planification et 400–500 octets pour des workloads petit-bloc à fort churn. Si le choix change votre décision, prenez le chiffre le plus élevé pour dormir tranquille.
4) La compression aide-t-elle ou nuit-elle à la dédup ?
Elle peut aider la capacité et réduire les I/O physiques, mais elle peut réduire les correspondances de dédup si des données « similaires » se compressent en séquences d’octets différentes. Rappelez-vous aussi : ZFS hache le bloc stocké, qui est typiquement compressé.
5) Si j’active la dédup et que je le regrette, puis-je la désactiver en toute sécurité ?
Vous pouvez désactiver la dédup pour les écritures futures en mettant dedup=off sur le dataset. Les blocs déjà dédupliqués restent dédupliqués jusqu’à réécriture. Revenir à un état vraiment non-dedupé implique généralement de copier les données vers un dataset non-dedup (ou un nouveau pool) et de basculer.
6) Un special vdev résoudra-t-il mes problèmes de dédup ?
Il aide en rendant les misses DDT/métadonnées moins pénibles. Il ne remplace pas la RAM. Si votre taux de misses DDT est élevé, vous payez toujours des I/O et de la latence supplémentaires ; vous les payez simplement sur NVMe plutôt que sur HDD.
7) Dois-je activer la dédup pour le stockage VM ?
Généralement non, sauf si vous avez un schéma de duplication très spécifique et stable (comme des clones VDI) et que vous avez modélisé la taille de bloc et le churn. Pour des fermes de VM générales, la compression + de bons templates + le thin provisioning sont des stratégies plus sûres.
8) Quels métriques prouvent que la dédup en vaut la peine ?
Deux catégories : capacité et latence. Capacité : un ratio de dédup significatif (pas juste au-dessus de 1.0x) et stable dans le temps.
Latence : la latence IO p95/p99 reste dans les SLO pendant les périodes d’écriture de pointe. Si l’un ou l’autre échoue, la dédup est un mauvais compromis.
9) La dédup interagit-elle avec les snapshots ?
Oui. Les snapshots épinglent d’anciens blocs, ce qui maintient les entrées DDT vivantes et peut augmenter la fragmentation. Une rétention longue associée à un churn élevé est une recette classique pour la croissance du DDT et la détérioration de la pression du cache dans le temps.
10) Quelle est la manière la plus sûre d’essayer la dédup en production ?
Ne la « testez » pas largement. Pilotez sur un dataset isolé (ou un pool séparé), avec des critères de rollback explicites, et mesurez la croissance du DDT et la latence pendant les pics réels.
Conclusion : prochaines étapes sans ruiner votre week-end
La dédup n’est pas une case à cocher. C’est un engagement à porter un index volumineux et critique pour les performances en mémoire et à le maintenir suffisamment chaud pour que votre chemin d’écriture ne devienne pas une thérapie d’I/O aléatoire.
Prochaines étapes pratiques :
- Inventorier les datasets candidats et enregistrer leurs tailles de bloc (
recordsize/volblocksize). - Mesurer
logicalreferet la rétention des snapshots pour comprendre combien de blocs vous devrez réellement indexer. - Modéliser la RAM DDT avec des octets-par-entrée conservateurs et un facteur d’ensemble de travail adapté au workload.
- Si le calcul est défavorable, croyez-le. Changez la conception : évitez la dédup, isolez-la, ou migrez vers un workload où la duplication est réelle.
- Si le calcul est raisonnable, lancez un pilote avec des critères de rollback stricts sur la latence et surveillez la croissance « in core » du DDT dans le temps.
Une idée paraphrasée d’une voix notable de la fiabilité convient ici : « L’espoir n’est pas une stratégie, » souvent attribuée dans l’esprit à des penseurs opérationnels. Dimensionnez le DDT comme vous dimensionnez le risque : avec des preuves, de la marge et un plan de rollback.