Tables de déduplication ZFS (DDT) : ce qu’elles sont et pourquoi elles posent problème

Cet article vous a aidé ?

Vous avez activé dedup sur ZFS parce que quelqu’un a dit que c’était de « l’espace gratuit ». Puis votre pool a commencé à se comporter comme si chaque requête I/O devait être soumise à une réunion de comité.
Pics de latence. Écritures ralenties. Les redémarrages ressemblent à de la roulette. Et la machine qui chauffait avant est maintenant occupée.

Le coupable n’est rarement « dedup » en tant que concept abstrait. Ce sont les tables de déduplication (DDT) : leur emplacement, leur taille, la fréquence des cache miss,
et la façon impitoyable dont ZFS ira sur disque pour tenir ses promesses.

DDT en termes clairs : ce que c’est

La déduplication ZFS fonctionne en constatant que deux blocs sont identiques et en ne conservant qu’une seule copie physique. Super en théorie. En pratique, pour ne pas perdre vos données,
ZFS doit être absolument sûr d’avoir déjà vu le bloc. Cette certitude provient d’une structure proche d’une base de données : la Deduplication Table, ou DDT.

La DDT est essentiellement un grand index clé par le checksum d’un bloc (avec quelques métadonnées supplémentaires). Lorsqu’une écriture arrive, ZFS calcule le checksum et interroge la DDT :
« Est-ce que j’ai déjà ce bloc quelque part ? » Si oui, il incrémente un compteur de références et n’écrit pas une nouvelle copie. Sinon, il écrit le nouveau bloc et ajoute une entrée.

Cela signifie que la DDT est sur le chemin critique pour chaque écriture avec dedup activé. Pas « parfois ». Chaque fois. Et parce que ZFS est ZFS, il maintiendra cette table correctement
même si cela nuit aux performances. Surtout si cela nuit.

La première chose à intégrer : dedup n’est pas une option que l’on saupoudre sur des datasets. C’est un engagement à réaliser une recherche en table pour chaque bloc et à assumer
les conséquences en mémoire et I/O pour toujours — jusqu’à ce que vous réécriviez les données sans dedup.

Pourquoi la DDT pose problème : modèle de coûts réel

1) La DDT veut de la RAM comme un bambin veut des snacks

Les entrées DDT sont des métadonnées. Les métadonnées veulent être en mémoire parce que les lire depuis le disque de façon répétée est lent et provoque des variations.
Quand l’ensemble actif d’entrées DDT tient dans l’ARC (le cache de ZFS), dedup peut être « relativement acceptable ». Quand ce n’est pas le cas, vous avez des cache miss DDT, ce qui implique
des lectures aléatoires supplémentaires, donc de la latence, et donc des propriétaires d’applications mécontents.

ZFS continuera de fonctionner avec une DDT qui ne tient pas en RAM. Il fonctionnera juste comme s’il avait des poids aux chevilles en montant une côte sous la pluie.

2) Les recherches DDT transforment des écritures séquentielles en lectures aléatoires

Sans dedup, une charge en streaming peut être principalement des écritures séquentielles. Avec dedup, vous faites :
calculer le checksum → rechercher la DDT → peut-être lire la DDT depuis le disque → puis écrire (ou pas). Ce motif de recherche est souvent aléatoire, et il se produit au moment de l’écriture.
Les lectures aléatoires sur des HDD sont une taxe. Sur des SSD, la taxe est moindre, mais l’addition arrive quand même.

3) « Ça a permis d’économiser 30 % d’espace » n’est pas un budget performance

Le succès de dedup se mesure en espace économisé. Le succès en production se mesure en latence stable sous charge. Ces objectifs sont liés, mais pas alignés.
Vous pouvez obtenir un excellent ratio de déduplication et avoir des performances épouvantables parce que la DDT ne tient pas, ou parce que le stockage est trop lent pour le taux de recherches.

4) Dedup change les modes de défaillance

Avec dedup, plusieurs blocs logiques pointent vers un bloc physique unique. C’est sûr — ZFS est copy-on-write et utilise des checksums — mais cela augmente les enjeux sur la santé des métadonnées
et sur la capacité du système à accéder aux entrées DDT. Vous pouvez toujours récupérer d’une défaillance de périphérique ; vous ne voulez juste pas le faire avec une ARC affamée et une DDT
qui va frapper le disque à chaque groupe de transactions.

5) Le coût « pour toujours » caché

La partie douloureuse n’est pas seulement d’activer dedup. C’est d’en vivre. Une fois les données écrites avec dedup=on, mettre dedup=off ne déduplique pas les blocs existants.
Vous avez engagé cette organisation sur disque dans un monde gouverné par la DDT jusqu’à ce que vous réécriviez les données (send/receive vers une cible sans dedup, ou copie hors puis retour).

Une vérité opérationnelle sèche : si vous activez dedup à la légère, vous planifierez plus tard un « projet de migration de données » qui n’est autre que le remboursement d’un emprunt que vous n’aviez pas vu venir.

Une citation pour la route, parce qu’elle s’applique parfaitement ici :
L’espoir n’est pas une stratégie. — idée souvent paraphrasée attribuée aux responsables opérations

Faits et contexte historique (court, concret)

  1. La dédup a été conçue pour résoudre un vrai problème : les premières fermes VM et jeux de sauvegarde produisaient d’énormes blocs dupliqués entre images et sauvegardes complètes.
  2. La dédup ZFS est au niveau bloc, pas fichier : elle ne tient pas compte des noms de fichiers, seulement des blocs de taille fixe (et quelques cas variables).
  3. La DDT est persistée sur disque : ce n’est pas « juste un cache ». L’ARC contient les ensembles actifs, mais la table canonique vit dans le pool.
  4. Dedup dans ZFS est une propriété par dataset : mais la DDT est une réalité au niveau pool. Plusieurs datasets avec dedup partagent les mêmes structures DDT au niveau pool.
  5. Le choix du checksum est important : les versions modernes de ZFS utilisent des checksums forts ; dedup s’appuie dessus, et la paranoïa des collisions est une raison pour laquelle ce n’est pas « bon marché ».
  6. Les premières recommandations étaient approximatives : beaucoup d’administrateurs ont appris la règle « 1–2GB RAM par TB dédupliqué », puis ont découvert que la réalité dépend du workload.
  7. Le comptage DDT s’est amélioré au fil du temps : les versions récentes d’OpenZFS exposent de meilleures histogrammes et statistiques pour estimer l’impact mémoire plus précisément.
  8. Les special vdev ont changé la donne : placer les métadonnées (y compris la DDT) sur des appareils rapides peut aider, mais ce n’est pas un laissez-passer gratuit et comporte des risques.
  9. La compression a souvent remplacé dedup : pour les données modernes, lz4 apporte fréquemment des économies significatives avec beaucoup moins de volatilité de performance.

Comment dedup fonctionne réellement (et où s’insère la DDT)

Dedup est un problème de recherche déguisé en fonctionnalité d’économie d’espace

À haut niveau, le flux dedup ressemble à ceci :

  • Le bloc logique entrant est assemblé (taille recordsize pour les systèmes de fichiers, volblocksize pour les zvols).
  • Le checksum est calculé (et la compression peut être appliquée ; l’ordre exact dépend de la configuration et de l’implémentation).
  • La DDT est interrogée : ce checksum existe-t-il, et correspond-il aux métadonnées du bloc ?
  • Si trouvé, le refcount augmente ; le bloc logique pointe vers le bloc physique existant.
  • Si non trouvé, le bloc est écrit, la DDT reçoit une nouvelle entrée.

Ce que représente grossièrement une entrée DDT

Une entrée DDT n’est pas juste « checksum → pointeur de bloc ». Elle contient aussi suffisamment d’informations pour :
vérifier la correspondance, localiser le bloc physique, et suivre combien de références logiques pointent dessus.
C’est pourquoi l’empreinte mémoire n’est pas négligeable. La DDT ressemble davantage à un index de base de données qu’à une simple table de hachage.

Pourquoi les misses sont catastrophiques comparés aux misses de cache normaux

Beaucoup de caches manquent et le système survit. Le miss DDT est spécial parce qu’il est sur le chemin d’écriture et tend à générer des I/O aléatoires. Un miss de cache de lecture normal peut
retarder une lecture. Un miss DDT ajoute de la latence avant même que l’écriture puisse décider quoi faire.

Sur un système chargé, cela signifie accumulation dans les files, augmentation des temps de txg sync, et finalement le symptôme familier : tout « se fige » par rafales, puis revient, puis se fige à nouveau.
Ce schéma est le classique « thrash des métadonnées » et la DDT est l’une des voies les plus rapides pour y parvenir.

Le ratio de dédup n’est pas votre objectif ; l’ensemble actif DDT l’est

Le ratio de dédup (logique vs physique) vous indique le bénéfice. L’ensemble actif DDT vous indique le coût opérationnel. Vous voulez les deux. Vous les obtenez rarement tous les deux.

Blague #1 : Dedup, c’est comme acheter une valise plus grosse pour éviter des frais de bagages, puis réaliser que la valise elle-même pèse 20 kilos.

Mode opératoire de diagnostic rapide

Quand un pool avec dedup activé est lent, vous pouvez passer des jours à débattre de « la taille de l’ARC », « SLOG », « sync », « le réseau », ou « ces disques sont vieux ».
Ou bien vous pouvez d’abord vérifier le chemin DDT et décider rapidement si vous êtes en enfer de recherches.

Première étape : confirmer que dedup est réellement en jeu

  • Vérifiez la propriété dedup sur les datasets et zvols qui effectuent des écritures.
  • Vérifiez si le pool a une DDT non triviale (si dedup a été activé historiquement).

Deuxième étape : vérifier si la DDT tient dans le cache

  • Inspectez la taille DDT, l’histogramme, et si les entrées DDT manquent fréquemment dans l’ARC.
  • Regardez la pression ARC et le comportement d’éviction.

Troisième étape : prouver que c’est lié aux lectures aléatoires

  • Comparez iostat : observez-vous des IOPS de lecture élevés pendant des écritures intensives ?
  • Vérifiez la latence : les lectures aléatoires augmentent-elles quand le chemin de recherche DDT va sur disque ?
  • Corrélez avec les temps de txg sync et le temps CPU dans les chemins checksum/compression.

Quatrième étape : décider la voie de sortie

  • Si la DDT ne tient pas et que vous ne pouvez pas ajouter de RAM ou de périphériques métadonnées rapides, planifiez une migration hors dedup.
  • Si la DDT est gérable, ajustez les tailles de record, les profils de charge, et envisagez un special vdev / stockage plus rapide.

Tâches pratiques : commandes, sorties, ce que ça signifie, et vos décisions

Ci‑dessous des tâches pratiques à exécuter sur un hôte Linux OpenZFS typique. Les commandes sont réalistes ; les sorties sont des exemples représentatifs. Vos champs et
chiffres exacts varieront selon la version et la distribution.

Task 1: Find where dedup is enabled (and where you forgot it was enabled)

cr0x@server:~$ zfs get -r -o name,property,value,source dedup tank
NAME                    PROPERTY  VALUE  SOURCE
tank                    dedup     off    default
tank/vm                 dedup     on     local
tank/vm/win10           dedup     on     inherited from tank/vm
tank/home               dedup     off    default

Ce que cela signifie : les écritures vers tank/vm et ses enfants effectuent des recherches DDT. tank/home non.

Décision : si dedup est « on » quelque part, inventoriez les workloads présents. Si c’est « on » pour des datasets à usage général, supposez une mauvaise configuration jusqu’à preuve du contraire.

Task 2: Verify whether existing data is deduped (dedup property lies about history)

cr0x@server:~$ zdb -DD tank
DDT-sha256-zap-duplicate: 123456 entries, size 350 on disk, 180 in core
DDT-sha256-zap-unique:    987654 entries, size 2100 on disk, 1200 in core

DDT histogram (aggregated over all DDTs):

bucket              allocated                       referenced
______   ______________________________   ______________________________
refcnt   blocks   LSIZE   PSIZE   DSIZE   blocks   LSIZE   PSIZE   DSIZE
-----    ------   -----   -----   -----   ------   -----   -----   -----
1        900K     110G    72G     72G     900K     110G    72G     72G
2        80K      10G     6G      6G      160K     20G     12G     12G
4        10K      2G      1G      1G      40K     8G      4G      4G

Ce que cela signifie : le pool contient déjà des blocs dedupés (des entrées DDT existent). Désactiver dedup maintenant ne supprimera pas ces données.

Décision : si la DDT est significative et que les performances sont mauvaises, vous envisagez un plan de réécriture/migration, pas un simple changement de propriété.

Task 3: Quick dedup “is it worth it?” sanity check (ratio vs pain)

cr0x@server:~$ zpool get dedupratio tank
NAME  PROPERTY    VALUE  SOURCE
tank  dedupratio  1.14x  -

Ce que cela signifie : 1.14x est une économie modeste. Sur des pétaoctets, cela peut représenter de l’argent ; sur des pools modestes, cela vaut rarement le chaos opérationnel.

Décision : si dedupratio est proche de 1.0x et que vous payez en latence, planifiez une sortie de dedup sauf si vous avez une raison très spécifique de la garder.

Task 4: Check ARC size and pressure (are you starving the DDT?)

cr0x@server:~$ cat /proc/spl/kstat/zfs/arcstats | egrep "^(size|c |c_min|c_max|memory_throttle_count|arc_no_grow|demand_data_hits|demand_data_misses) "
size 4 25769803776
c 4 34359738368
c_min 4 8589934592
c_max 4 68719476736
arc_no_grow 4 0
memory_throttle_count 4 12
demand_data_hits 4 91522344
demand_data_misses 4 1822331

Ce que cela signifie : l’ARC est ~24GiB, cible ~32GiB, max ~64GiB ; du throttling mémoire a eu lieu (pression). La DDT concurrence tout le reste.

Décision : si la machine manque de mémoire ou subit des throttlings fréquents, dedup est un mauvais voisin. Ajoutez de la RAM, réduisez la charge ou sortez de dedup.

Task 5: Measure pool latency and read IOPS during writes (DDT miss smell test)

cr0x@server:~$ iostat -x 1 5
avg-cpu:  %user   %nice %system %iowait  %steal   %idle
           6.20    0.00    5.10    9.80    0.00   78.90

Device            r/s     w/s   rkB/s   wkB/s  avgrq-sz avgqu-sz   await  r_await  w_await  svctm  %util
nvme0n1         320.0   180.0  9200.0  6400.0     56.0     4.20   10.8     9.4     13.3    0.9   45.0
sda              90.0    20.0  1100.0   400.0     27.0     8.80   78.0    85.0     45.0    7.2   79.0
sdb              95.0    22.0  1200.0   420.0     28.0     9.10   81.0    88.0     47.0    7.4   82.0

Ce que cela signifie : beaucoup de lectures se produisent alors que vous attendez principalement des écritures, et r_await est énorme sur les HDD. Cela correspond à des recherches DDT manquant l’ARC.

Décision : si vous observez une pression de lectures aléatoires liée à des rafales d’écritures, considérez les misses DDT comme suspect principal et validez avec les stats/histogrammes DDT.

Task 6: Confirm recordsize/volblocksize and why it matters for dedup

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

Ce que cela signifie : des blocs plus grands impliquent moins d’entrées DDT pour le même volume de données, mais peuvent réduire les opportunités de dedup et affecter les motifs d’I/O aléatoire.

Décision : pour les zvols VM, envisagez d’aligner volblocksize sur le workload et évitez les petits blocs sauf raison valable. Mais ne pensez pas qu’un réglage sauvera une ARC sous-dimensionnée.

Task 7: Check if compression could replace dedup for your workload

cr0x@server:~$ zfs get -o name,property,value compression,compressratio tank/vm
NAME     PROPERTY       VALUE
tank/vm  compression    lz4
tank/vm  compressratio  1.48x

Ce que cela signifie : vous obtenez déjà 1.48x grâce à la compression, ce qui surpasse souvent des ratios « moyens » de dedup en conditions réelles.

Décision : si la compression vous apporte des économies significatives, c’est votre levier par défaut. Réservez dedup aux cas étroits et validés.

Task 8: See if a special vdev exists (metadata acceleration) and whether it’s sized sanely

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
          special                   ONLINE       0     0     0
            mirror-1                ONLINE       0     0     0
              nvme0n1p2             ONLINE       0     0     0
              nvme1n1p2             ONLINE       0     0     0

errors: No known data errors

Ce que cela signifie : il y a un special vdev en miroir. Les métadonnées (et potentiellement les petits blocs) peuvent y atterrir, réduisant la latence des recherches DDT lorsque l’ARC manque.

Décision : si vous utilisez dedup, un special vdev peut aider. Mais traitez-le comme du tier‑0 : si vous le perdez sans redondance, vous pouvez perdre le pool.

Task 9: Check where metadata is going (special_small_blocks policy)

cr0x@server:~$ zfs get -o name,property,value special_small_blocks tank
NAME  PROPERTY             VALUE
tank  special_small_blocks 0

Ce que cela signifie : seules les métadonnées sont allouées au special vdev, pas les petits blocs de fichiers. La DDT est proche de métadonnée, elle peut donc bénéficier même avec 0.

Décision : ne définissez pas special_small_blocks à l’aveugle sans comprendre la capacité et le domaine de défaillance. Cela peut déplacer beaucoup de données vers le special vdev.

Task 10: Check txg sync behavior (when the system “stalls”)

cr0x@server:~$ cat /proc/spl/kstat/zfs/txg | head
0 0 0x01 7 448 13056000 2112000
# txgs synced  txg  ...

Ce que cela signifie : les stats txg varient selon la version ; l’important est de corréler les « blocages » avec la cadence des syncs. Les lectures DDT provoquées peuvent
étendre le travail de sync et bloquer la progression.

Décision : si les intervalles de sync explosent sous charge d’écriture, suspectez les métadonnées et les misses DDT ; validez ensuite avec iostat et la pression ARC.

Task 11: Prove the dataset is paying the dedup tax right now (observe write latency via fio)

cr0x@server:~$ fio --name=randwrite --filename=/tank/vm/testfile --direct=1 --rw=randwrite --bs=16k --iodepth=16 --numjobs=1 --size=2g --runtime=30 --time_based --group_reporting
randwrite: (g=0): rw=randwrite, bs=(R) 16.0KiB-16.0KiB, (W) 16.0KiB-16.0KiB, (T) 16.0KiB-16.0KiB, ioengine=psync, iodepth=16
fio-3.33
...
write: IOPS=850, BW=13.3MiB/s (14.0MB/s)(400MiB/30001msec)
    slat (usec): min=7, max=300, avg=18.5, stdev=6.2
    clat (msec): min=2, max=180, avg=18.9, stdev=22.1
     lat (msec): min=2, max=180, avg=18.9, stdev=22.1

Ce que cela signifie : la latence sur les queues élevées (clat max 180ms) est typique quand des recherches de métadonnées vont sur des disques lents, même si la moyenne paraît « correcte ».

Décision : si la latence tail est mauvaise et corrélée aux workloads heavy en dedup, cessez d’« optimiser l’app ». Réparez le chemin de stockage ou retirez dedup.

Task 12: Check for dedup property drift (someone enabled it “temporarily”)

cr0x@server:~$ zfs get -r -s local,inherited -o name,property,value,source dedup tank | head
NAME           PROPERTY  VALUE  SOURCE
tank/vm        dedup     on     local
tank/vm/old    dedup     on     inherited from tank/vm
tank/vm/tmp    dedup     on     inherited from tank/vm

Ce que cela signifie : dedup se propage par héritage, c’est ainsi qu’« une expérience sur un dataset » devient « tout le pool paie la taxe de recherche ».

Décision : brisez l’héritage là où ce n’est pas nécessaire. Dedup doit être opt-in, pas ambiant.

Task 13: Turn dedup off for new writes (without pretending it fixes existing data)

cr0x@server:~$ sudo zfs set dedup=off tank/vm

Ce que cela signifie : les nouveaux blocs ne seront plus dédupliqués, mais les anciens blocs restent dédupliqués et dépendent toujours de la DDT pour les lectures.

Décision : faites cela tôt pour contenir les dégâts, puis planifiez la vraie solution : réécrire les données vers une cible sans dedup.

Task 14: Estimate “how bad could the rewrite be?” by checking used and logicalused

cr0x@server:~$ zfs get -o name,property,value used,logicalused,referenced,logicalreferenced tank/vm
NAME     PROPERTY           VALUE
tank/vm  used               8.50T
tank/vm  logicalused        11.2T
tank/vm  referenced         8.50T
tank/vm  logicalreferenced  11.2T

Ce que cela signifie : les données logiques sont plus volumineuses que les données physiques. Si vous réécrivez sans dedup, vous aurez besoin d’espace pour l’expansion physique (ou d’une destination plus grande).

Décision : planifiez la capacité avant de migrer hors dedup. Beaucoup de projets « on va juste copier » échouent quand l’usage physique saute.

Task 15: Migrate safely via send/receive (the boring tool that works)

cr0x@server:~$ sudo zfs snapshot -r tank/vm@move-001
cr0x@server:~$ sudo zfs send -R tank/vm@move-001 | sudo zfs receive -u tank_nodedup/vm

Ce que cela signifie : vous avez créé une copie cohérente. Si la destination a dedup=off, les nouveaux blocs seront écrits sans dedup.

Décision : c’est la rampe de sortie propre. Utilisez -u pour recevoir démonté, validez, puis basculez de façon délibérée.

Trois mini-récits d’entreprise issus du terrain

1) Incident causé par une mauvaise hypothèse : « Dedup est par dataset, donc ça ne peut pas nuire au pool »

Une entreprise de taille moyenne avait un cluster de virtualisation reposant sur ZFS. L’équipe de stockage a activé dedup sur un dataset utilisé pour des VM modèles, en s’attendant à de grandes économies.
L’hypothèse était rassurante : ce n’est qu’un dataset ; au pire, cela ne ralentira que ce dataset.

Deux semaines plus tard, le helpdesk a commencé à recevoir des tickets de « lenteurs aléatoires ». Les démarrages de VM se bloquaient parfois. Les fenêtres de sauvegarde nocturnes se sont allongées, puis ont manqué les SLA.
La réponse initiale fut prévisible : ajouter des disques au RAIDZ, ajuster le caching de l’hyperviseur, blâmer le réseau, puis les « voisins bruyants ».

Le tournant a été de remarquer l’augmentation des IOPS de lecture pendant les périodes d’écriture intense. Ce n’est pas normal. Il est devenu évident que les recherches DDT manquaient le cache et
forçaient des lectures aléatoires sur tout le pool. Bien que dedup fût activé par dataset, la DDT vivait au niveau pool et le chemin I/O affectait tous ceux qui partageaient ces vdev.

La correction n’a pas été héroïque. Ils ont désactivé dedup pour les nouvelles écritures immédiatement, puis effectué une migration par send/receive vers un pool sans dedup pendant le week-end.
Les performances se sont stabilisées avant la fin de la migration, parce que l’ensemble actif a migré en premier et la pression sur la DDT a diminué.

Le post-mortem était franc : dans un stockage partagé, les fonctionnalités « par dataset » ont des conséquences au niveau pool. Si le chemin critique touche des métadonnées pool-wide,
traitez-le comme un changement au niveau pool, pas comme une expérience sur un dataset.

2) Optimisation qui a échoué : « Mettons dedup pour les backups, c’est majoritairement dupliqué »

Un autre environnement avait une cible de sauvegarde sur ZFS et voulait réduire la croissance de capacité. Quelqu’un a regardé les données de backup, vu de la répétition, et fait un pari raisonnable :
dedup devrait écraser les fulls redondants. L’environnement avait beaucoup de CPU et « assez de RAM », et les disques étaient un mélange de HDD et d’un petit SSD pour cache.

Pendant un mois, tout semblait gagné. Le dedupratio montait. L’achat de stockage était reporté. Tout le monde se félicitait et est passé au suivant.
Puis ils ont augmenté la rétention. La DDT a grandi. L’ARC non. Les réhydratations ont ralenti. Les tests de restauration sont devenus pénibles.

Le jour de l’incident, une grosse restauration a tourné pendant que des backups étaient encore en ingestion. La latence a explosé sur le pool. Le monitoring montrait des disques occupés,
mais le débit était faible. Saturation classique d’I/O aléatoire. Les lectures de métadonnées ont explosé parce que l’ensemble actif DDT ne tenait plus en mémoire.
L’équipe pensait optimiser la capacité ; en fait, elle avait transformé chaque écriture en une opération amplifiée en lecture au pire moment.

La correction fut contre-intuitive : ils ont arrêté dedup, mis davantage l’accent sur la compression, et modifié le pipeline de sauvegarde pour éviter d’écrire des données identiques
(chaînes incrémentales améliorées, chunking différent, politiques de copie plus intelligentes).

La leçon : dedup peut sembler excellent au départ, puis chuter brutalement quand la table franchit un seuil de cache. L’échec n’est pas progressif ; c’est une fonction en escalier.

3) Pratique ennuyeuse mais correcte qui a sauvé la journée : « Mesurer la DDT avant d’activer quoi que ce soit »

Une équipe de services financiers avait une demande : stocker plusieurs versions d’images VM avec une croissance minimale. Le business a demandé dedup. L’équipe stockage a répondu « peut-être »,
ce qui est la version polie de « nous allons mesurer avant de regretter ».

Ils ont créé un petit pool de test reflétant le type de vdev de production et exécuté un ingest représentatif : quelques centaines de gigaoctets du churn réel d’images VM.
Ils ont collecté des histogrammes via zdb -DD, observé le comportement de l’ARC, et mesuré la latence sous charge concurrente. Ils ont répété le test avec compression seule et
avec différentes valeurs de recordsize/volblocksize. Rien de sophistiqué — juste de la discipline.

Les résultats furent ennuyeux dans le bon sens. Dedup économisait moins que prévu parce que certaines données étaient déjà compressées et parce que les changements étaient dispersés.
La compression seule produisait la majeure partie des économies avec des performances stables. Les estimations de l’ensemble actif DDT indiquaient qu’ils auraient besoin de plus de RAM que la plateforme ne pouvait offrir
sans mise à niveau matérielle.

Ils ont refusé dedup en production et documenté la raison. Six mois plus tard, un nouveau dirigeant a reposé la question. L’équipe a rouvert le rapport,
relancé une validation plus petite sur des données actuelles, et a encore évité une panne auto-infligée.

La leçon : l’optimisation la plus précieuse est celle que vous décidez de ne pas déployer après l’avoir mesurée.

Blague #2 : La DDT est un tableur qui ne cesse de grandir, sauf qu’il est sur votre chemin critique et n’accepte pas « masquer la colonne » comme correctif de performance.

Erreurs courantes : symptômes → cause racine → correction

1) Les écritures sont lentes, mais les disques montrent beaucoup de lectures

Symptômes : tâches à fort taux d’écriture déclenchent des IOPS de lecture, fort r_await, et latence en rafales.

Cause racine : recherches DDT manquant l’ARC ; les entrées DDT sont récupérées depuis le disque de façon aléatoire pendant les écritures.

Correction : augmentez la RAM / la marge ARC, déplacez les métadonnées vers des appareils plus rapides (special vdev), ou migrez hors dedup. Si vous ne pouvez pas garder l’ensemble actif DDT chaud, dedup n’est pas adapté.

2) « Nous avons désactivé dedup mais c’est toujours lent »

Symptômes : la propriété dedup est off, mais le pool reste lourd en métadonnées et imprévisible.

Cause racine : les blocs existants restent dédupliqués ; les lectures dépendent toujours de la DDT et de son comportement de cache.

Correction : réécrivez les données vers une destination sans dedup (send/receive), puis mettez hors service la copie dédupliquée.

3) Le pool va bien jusqu’au jour où tout s’écroule

Symptômes : mois de performances acceptables, puis soudain des pics, timeouts, longs txg sync, et comportement erratique des applications.

Cause racine : la DDT franchit le seuil de l’ensemble actif ARC ; le taux de misses explose et les lectures aléatoires se multiplient.

Correction : traitez-le comme un échec de planification de capacité : la croissance de la DDT doit être budgétée comme toute autre capacité. Ajoutez de la RAM / métadonnées rapides ou sortez de dedup.

4) Le reboot prend une éternité, l’import semble fragile

Symptômes : les imports de pool sont lents, le système semble bloqué après reboot sous charge.

Cause racine : grands ensembles de métadonnées (y compris la DDT) en interaction avec un cache limité ; le système doit « chauffer » un ensemble actif tout en servant des I/O.

Correction : réduisez l’empreinte dedup en migrant ; assurez une RAM adéquate ; évitez de mélanger charge prod lourde avec des imports à froid si vous pouvez contrôler l’ordre.

5) Special vdev ajouté, mais les performances n’ont pas progressé

Symptômes : vous avez installé des SSD rapides comme special vdev, mais la douleur dedup reste.

Cause racine : l’ensemble actif DDT est toujours trop grand ; ou le special vdev est saturé/trop petit ; ou vous attendiez qu’il résolve un problème CPU lié au checksum/compression.

Correction : confirmez l’allocation des métadonnées et la latence des appareils. Dimensionnez correctement le special vdev, mettez-le en miroir, et prévoyez toujours de la RAM. Le special vdev est un accélérateur, pas un miracle.

6) Dedup activé sur un dataset contenant déjà des blocs compressés/chiffrés

Symptômes : amélioration négligeable du dedupratio, mais augmentation notable de la latence.

Cause racine : les opportunités de dedup sont limitées lorsque les données sont déjà hautement entropiques (compressées, chiffrées ou uniques par conception).

Correction : gardez dedup désactivé. Utilisez la compression, des stratégies de sauvegarde intelligentes, ou une déduplication/chunking côté application si nécessaire.

7) Dedup activé sur des partages de fichiers généraux « parce que ça peut aider »

Symptômes : lenteurs intermittentes sur des workloads mixtes ; corrélation floue.

Cause racine : taxe dedup appliquée à des données larges et imprévisibles ; churn DDT avec mauvaise localité.

Correction : restreignez dedup aux datasets étroits où la duplication est prouvée et stable. Sinon désactivez et migrez.

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

Checklist de décision : devez-vous activer dedup ?

  1. Mesurez la duplication sur des données réelles : ne devinez pas. Si vous ne pouvez pas tester, par défaut dites « non ».
  2. Estimez la taille et l’ensemble actif DDT : utilisez zdb -DD sur un pool de test ou un échantillon ; supposez une croissance avec la rétention.
  3. Budgétez la RAM avec marge : vous avez besoin d’ARC pour les lectures normales aussi. Si dedup doit évincer tout le reste, c’est un non-départ.
  4. Validez la latence en concurrence : testez avec des écritures et lectures parallèles réalistes, pas un benchmark mono‑thread.
  5. Décidez d’un plan de sortie à l’avance : si ça tourne mal, comment réécrivez-vous les données et où la hausse physique ira‑t‑elle ?

Checklist opérationnelle : vous avez déjà dedup et ça fait mal

  1. Arrêtez l’hémorragie : mettez dedup=off pour les nouvelles écritures sur les datasets affectés.
  2. Quantifiez le périmètre : collectez zpool get dedupratio, zdb -DD, stats ARC, et latence iostat.
  3. Identifiez les datasets chauds actifs : d’où proviennent les écritures et lectures pendant les incidents ?
  4. Choisissez votre remède : ajoutez de la RAM, accélérez les métadonnées (special vdev), ou migrez hors dedup. Ne « tweakez » pas autour d’une DDT fondamentalement surdimensionnée.
  5. Planifiez la capacité de migration : comparez logicalused à used. Assurez-vous que la destination peut absorber l’expansion physique.
  6. Exécutez avec snapshots et send/receive : gardez la procédure simple, vérifiable et réversible.
  7. Basculez délibérément : recevez démonté, validez, puis changez les points de montage ou consommateurs.
  8. Nettoyage post-basculement : détruisez les anciens datasets dédupliqués uniquement quand vous êtes sûr ; vérifiez ensuite que la DDT diminue au fil du temps au fur et à mesure que les blocs sont libérés.

Checklist préventive : empêcher dedup de se propager discrètement

  1. Auditez l’héritage de la propriété dedup mensuellement sur les pools de production.
  2. Exigez un dossier de changement pour activer dedup n’importe où.
  3. Alertez sur les variations de dedupratio et les IOPS de lecture inhabituels pendant les fenêtres d’écriture.
  4. Conservez des zones d’atterrissage « sans dedup » pour les workloads généraux ; faites des datasets dedup explicites et rares.

FAQ

Est-ce que la déduplication ZFS est « mauvaise » ?

Non. Elle est juste coûteuse. Elle est excellente quand la duplication est élevée, stable, et que vous pouvez garder l’ensemble actif DDT chaud (RAM et/ou périphériques métadonnées rapides).
C’est un piège quand on l’active par espoir.

Pourquoi dedup ralentit tant les écritures ?

Parce que ça ajoute une recherche sur le chemin d’écriture. Si l’entrée DDT n’est pas dans l’ARC, ZFS effectue des lectures aléatoires supplémentaires pour la trouver avant de décider d’écrire ou non.

Puis-je désactiver dedup et récupérer immédiatement mes performances ?

Vous pouvez empêcher les nouvelles écritures d’être dédupliquées en mettant dedup=off. Mais les blocs existants restent dédupliqués et dépendent toujours de la DDT pour les lectures.
La récupération réelle nécessite généralement de réécrire/migrer les données.

Comment savoir si ma DDT tient en RAM ?

Vous estimez via zdb -DD (histogrammes et tailles « in core ») puis vous validez le comportement : si les périodes d’écriture causent des lectures aléatoires et des pics de latence,
vous manquez probablement. Le « fit » concerne l’ensemble actif, pas le total.

La compression réduit-elle l’efficacité de dedup ?

Souvent, oui, parce que la compression modifie la représentation sur disque et les données compressées à haute entropie ont moins de blocs identiques.
En pratique, si la compression offre déjà de bonnes économies, dedup peut ne pas valoir la peine.

Dedup est-elle adaptée aux images VM ?

Parfois. Les images de référence et de nombreux clones peuvent bien se dédupliquer, mais les workloads VM sont sensibles à la latence et génèrent des écritures aléatoires.
Si vous ne pouvez pas garantir la localité DDT et des métadonnées rapides, vous transformerez peut‑être « espace économisé » en « dette de performance ».

Puis‑je placer la DDT sur un SSD ?

Avec un special vdev, vous pouvez orienter les allocations de métadonnées vers des appareils rapides, ce qui peut inclure des structures comme la DDT. Cela peut réduire la douleur des misses de cache.
Mais cela accroît la dépendance sur ces appareils ; ils doivent être redondants et correctement dimensionnés.

Ajouter plus de disques résoudra‑t‑il les performances dedup ?

Cela peut aider si le goulot est les IOPS de lecture aléatoire et que vous êtes sur HDD, mais c’est généralement une solution inefficace comparée à ajouter de la RAM ou déplacer les métadonnées vers du stockage rapide.
Si votre DDT ne tient pas, plus de plateaux risquent juste d’atténuer un peu le thrash.

Quelle est la manière la plus sûre de « dédupliquer » un dataset ?

Snapshot puis zfs send | zfs receive vers un dataset de destination avec dedup=off. Validez, puis basculez.
Cela réécrit les blocs et supprime la dépendance à la DDT pour ces données.

Existe‑t‑il une règle pratique de RAM par TB avec dedup ?

Des règles pratiques existent parce que les gens ont besoin de quelque chose à dire en réunion. La vraie réponse dépend de la distribution des tailles de blocs, des motifs de duplication,
et de votre ensemble actif. Si vous ne pouvez pas le mesurer, supposez que ce sera plus gros que ce que vous souhaitez.

Conclusion : prochaines étapes sans sacrifier le week-end

La déduplication ZFS n’est pas malveillante. Elle est juste honnête. Elle vous fait payer d’avance — en RAM, en I/O métadonnées, et en complexité opérationnelle — pour économiser de l’espace ensuite.
Si vous n’êtes pas prêt à payer en continu, la DDT accumulera des intérêts sous forme de latence.

Prochaines étapes pratiques :

  1. Auditez : exécutez un zfs get dedup récursif et repérez où dedup est activé et hérité.
  2. Quantifiez : capturez la sortie de zdb -DD et zpool get dedupratio pour le pool.
  3. Corrélez : utilisez iostat -x pendant les fenêtres lentes ; cherchez des IOPS de lecture et un r_await élevé pendant les écritures.
  4. Contenez : mettez dedup=off pour les nouvelles écritures là où ce n’est pas absolument nécessaire.
  5. Choisissez : financez dedup correctement (RAM + métadonnées rapides + validation), ou migrez hors de dedup avec send/receive et retrouvez la prévisibilité.

Si vous ne retenez qu’une chose : le succès de dedup n’est pas « combien d’espace avons-nous économisé ? » C’est « pouvons‑nous encore atteindre nos objectifs de latence quand la DDT a une mauvaise journée ? »

← Précédent
Noyau Debian 13 marqué « tainted » : ce que ça signifie et quand s’en préoccuper
Suivant →
Pilotes comme armes : comment le logiciel peut « changer » votre GPU

Laisser un commentaire