Activaste dedup en ZFS porque alguien dijo que era “espacio gratis”. Luego tu pool empezó a comportarse como si cada solicitud de E/S se enviara a una reunión de comité.
Picos de latencia. Escrituras que se arrastran. Los reinicios se sienten como una ruleta. Y la caja que antes iba caliente ahora va ocupada.
El villano rara vez es “dedup” en abstracto. Son las tablas de deduplicación (DDT): dónde viven, cuánto crecen, con qué frecuencia fallas la caché y con qué brutalidad
ZFS acudirá al disco para mantener sus promesas.
DDT en términos sencillos: qué es
La deduplicación de ZFS funciona al notar que dos bloques son idénticos y almacenar solo una copia física. Genial en teoría. En la práctica, para no perder datos,
ZFS debe estar absolutamente seguro de haber visto el bloque antes. Esa certeza viene de una estructura tipo base de datos: la Tabla de deduplicación, o DDT.
La DDT es esencialmente un gran índice indexado por la suma de comprobación (checksum) del bloque (y algo de metadatos extras). Cuando llega una escritura, ZFS calcula la checksum y consulta la DDT:
“¿Ya tengo este bloque en algún sitio?” Si sí, incrementa un contador de referencias y no escribe otra copia. Si no, escribe el nuevo bloque y añade una entrada.
Eso significa que la DDT está en la ruta caliente para cada escritura con dedup activado. No “a veces”. Cada vez. Y porque ZFS es ZFS, mantendrá esa tabla correctamente
incluso si duele. Especialmente si duele.
Lo primero que hay que interiorizar: dedup no es una característica para espolvorear en los conjuntos de datos. Es un compromiso para hacer una consulta de tabla por bloque y asumir
las consecuencias de memoria y E/S para siempre—hasta que reescribas los datos sin dedup.
Por qué DDT duele: el verdadero modelo de costes
1) La DDT quiere RAM como un niño quiere meriendas
Las entradas de DDT son metadatos. Los metadatos quieren estar en memoria porque leerlos repetidamente desde disco es lento y provoca jitter. Cuando el conjunto de trabajo de entradas DDT cabe
en ARC (la caché de ZFS), dedup puede ser “medio decente”. Cuando no cabe, obtienes fallos de caché de la DDT, lo que significa lecturas aleatorias extra, lo que implica latencia, lo que provoca
propietarios de aplicaciones enfadados.
ZFS seguirá funcionando con una DDT que no quepa en RAM. Simplemente irá como si llevara pesas en los tobillos subiendo una cuesta bajo la lluvia.
2) Las consultas a la DDT convierten escrituras secuenciales en lecturas aleatorias
Sin dedup, una carga de trabajo en streaming puede ser en su mayor parte escrituras secuenciales. Con dedup, haces:
calcular checksum → consultar DDT → quizá leer la DDT desde disco → luego escribir (o no). Ese patrón de consulta es frecuentemente aleatorio, y ocurre en tiempo de escritura.
Las lecturas aleatorias en HDD son un impuesto. En SSD son un impuesto menor, pero la factura sigue llegando.
3) “Ahorró 30% de espacio” no es un presupuesto de rendimiento
La métrica de éxito de dedup es el espacio ahorrado. La métrica de éxito en producción es latencia estable bajo carga. Están relacionadas, pero no alineadas.
Puedes tener una excelente ratio de dedup y aun así un rendimiento horrible porque la DDT no cabe, o porque el almacenamiento es demasiado lento para la tasa de consultas.
4) Dedup cambia los modos de fallo
Con dedup, múltiples bloques lógicos apuntan a un bloque físico. Eso es seguro—ZFS es copy-on-write y usa checksums—pero aumenta la importancia de la integridad de los metadatos
y de la capacidad del sistema para acceder a las entradas de la DDT. Aún puedes recuperarte de una falla de dispositivo; simplemente no quieres hacerlo con un ARC hambriento y una DDT que
está reventando al disco en cada grupo de transacciones.
5) El coste “para siempre” oculto
La parte dolorosa no es solo habilitar dedup. Es convivir con ello. Una vez que los datos se escriben con dedup=on, cambiar dedup=off no des-deduplica los bloques existentes.
Has comprometido ese layout en disco a un mundo gobernado por la DDT hasta que reescribas los datos (send/receive a un destino sin dedup, o copiarlos fuera y volverlos a traer).
Una verdad operativa seca: si habilitas dedup a la ligera, luego programarás un “proyecto de migración de datos” que no será otra cosa que pagar un préstamo que no sabías que habías contraído.
Una cita para el camino, porque encaja perfecto:
La esperanza no es una estrategia.
— idea parafraseada atribuida con frecuencia a líderes de operaciones
Hechos & contexto histórico (corto, concreto)
- Dedup se lanzó para resolver un problema real: las primeras granjas de VM y los conjuntos de backups producían bloques duplicados masivos entre imágenes y backups completos.
- La dedup de ZFS es a nivel de bloque, no de archivo: no le importan los nombres de archivos, solo bloques de tamaño fijo (y algunos casos variables).
- La DDT se persiste en disco: no es “solo caché.” ARC mantiene conjuntos de trabajo, pero la tabla canónica vive en el pool.
- Dedup en ZFS es propiedad por dataset: pero la DDT es una realidad por pool. Múltiples datasets con dedup comparten las mismas estructuras DDT a nivel de pool.
- La elección de checksum importa: las versiones modernas de ZFS usan checksums fuertes; dedup depende de ellas, y el miedo a colisiones es una razón por la que no es “barato”.
- La guía temprana fue tosca: muchos administradores aprendieron “1–2GB RAM por TB dedupeado” como regla, pero luego descubrieron que la realidad depende de la carga de trabajo.
- La contabilidad de la DDT mejoró con el tiempo: versiones posteriores de OpenZFS expusieron mejores histogramas y estadísticas para estimar el impacto en memoria con más precisión.
- Los special vdev cambiaron la conversación: colocar metadatos (incluida la DDT) en dispositivos rápidos puede ayudar, pero no es un pase libre y tiene aristas afiladas.
- La compresión le quitó terreno a dedup en muchas empresas: para datos modernos, lz4 suele ofrecer ahorros significativos con mucha menos volatilidad en rendimiento.
Cómo funciona la dedup en realidad (y dónde encaja la DDT)
Dedup es un problema de consultas disfrazado de característica para ahorrar espacio
A alto nivel, el flujo de dedup se ve así:
- Se arma el bloque lógico entrante (tamaño recordsize para sistemas de archivos, volblocksize para zvols).
- Se calcula la checksum (y opcionalmente se aplica compresión; el orden exacto depende de la configuración y la implementación).
- Se consulta la DDT: ¿existe esta checksum, y coincide con los metadatos del bloque?
- Si se encuentra, aumenta refcount; el bloque lógico apunta al bloque físico existente.
- Si no se encuentra, se escribe el bloque y la DDT recibe una nueva entrada.
Lo que representa aproximadamente una entrada de DDT
Una entrada DDT no es solo “checksum → puntero de bloque.” También lleva suficiente información para:
verificar la coincidencia, localizar el bloque físico y rastrear cuántas referencias lógicas apuntan a él.
Por eso la huella en memoria no es trivial. La DDT se parece más a un índice de base de datos que a un simple mapa hash.
Por qué los fallos son catastróficos comparados con fallos de caché normales
Muchas cachés fallan y el sistema sobrevive. El fallo de la DDT es especial porque está en la ruta de escritura y tiende a ser E/S aleatoria. Un fallo de caché de lectura normal puede
retrasar una lectura. Un fallo de DDT añade latencia antes de que la escritura pueda decidir qué hacer.
En un sistema ocupado, eso significa acumulación de colas, mayores tiempos de txg sync y, eventualmente, el síntoma familiar: todo “se cuelga” en ráfagas, luego se recupera, y vuelve a colgarse.
Ese patrón es el clásico “thrash de metadatos” y la DDT es una de las rutas más rápidas hacia allí.
La ratio de dedup no es tu objetivo; el conjunto de trabajo de la DDT sí
La ratio de dedup (lógico vs físico) te dice el beneficio. El conjunto de trabajo de la DDT te dice el coste operativo. Quieres ambos. Rara vez los obtienes juntos.
Chiste #1: Dedup es como comprar una maleta más grande para evitar pagar exceso de equipaje, y luego darte cuenta de que la maleta pesa 20 kilos.
Guía de diagnóstico rápido
Cuando un pool con dedup activado está lento, puedes pasarte días discutiendo sobre “tamaño de ARC”, “SLOG”, “sync”, “red” o “esos discos son viejos”.
O puedes comprobar primero la ruta DDT y decidir rápido si estás en el infierno de consultas.
Primero: confirma que dedup realmente está en juego
- Comprueba la propiedad dedup en datasets y zvols que estén escribiendo.
- Verifica si el pool tiene una DDT no trivial (si dedup se habilitó históricamente).
Segundo: comprueba si la DDT cabe en caché
- Inspecciona el tamaño de la DDT, el histograma y si las entradas DDT faltan con frecuencia en ARC.
- Mira la presión de ARC y el comportamiento de expulsión (eviction).
Tercero: demuestra que está limitado por lecturas aleatorias
- Compara iostat: ¿ves IOPS de lectura elevadas durante escrituras intensas?
- Comprueba latencias: las lecturas aleatorias se disparan cuando la ruta de consulta DDT va al disco.
- Correlaciona con tiempos de txg sync y tiempo CPU en rutas de checksum/compresión.
Cuarto: decide la vía de escape
- Si la DDT no cabe y no puedes añadir RAM o dispositivos rápidos para metadatos, planifica una migración fuera de dedup.
- Si la DDT es manejable, afina tamaños de registro, patrones de carga y considera special vdev / almacenamiento más rápido.
Tareas prácticas: comandos, salidas, lo que significa y qué decides
A continuación hay tareas prácticas que puedes ejecutar en un host típico Linux con OpenZFS. Los comandos son realistas; las salidas son ejemplos representativos. Tus campos y
números exactos variarán según versión y distribución.
Task 1: Encuentra dónde está habilitado dedup (y dónde lo olvidaste)
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
Qué significa: las escrituras a tank/vm y sus hijos hacen consultas a la DDT. tank/home no lo hace.
Decisión: si dedup está “on” en cualquier sitio, inventaría los workloads allí. Si está “on” para datasets de propósito general, asume mala configuración hasta demostrar lo contrario.
Task 2: Verifica si los datos existentes están dedupeados (la propiedad dedup miente sobre la historia)
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
Qué significa: el pool ya contiene bloques dedupeados (existen entradas DDT). Apagar dedup ahora no eliminará estos.
Decisión: si la DDT es significativa y el rendimiento es pobre, estás ante un plan de reescritura/migración, no solo un cambio de propiedad.
Task 3: Sanity check rápido “¿vale la pena dedup?” (ratio vs dolor)
cr0x@server:~$ zpool get dedupratio tank
NAME PROPERTY VALUE SOURCE
tank dedupratio 1.14x -
Qué significa: 1.14x es un ahorro modesto. Puede ser dinero real en petabytes; rara vez merece el caos operativo en pools modestos.
Decisión: si dedupratio está cerca de 1.0x y pagas latencia, planifica salir de dedup a menos que tengas una razón muy específica para mantenerlo.
Task 4: Comprueba tamaño y presión de ARC (¿estás dejando a la DDT sin recursos?)
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
Qué significa: ARC es ~24GiB, target ~32GiB, max ~64GiB; hubo throttling de memoria (presión). La DDT compite con todo lo demás.
Decisión: si la máquina está limitada de memoria o tiene throttling frecuente, dedup es un mal vecino. Añade RAM, reduce carga o sal de dedup.
Task 5: Mide latencia del pool e IOPS de lectura durante escrituras (prueba de olor a fallo DDT)
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
Qué significa: muchas lecturas ocurriendo cuando esperas principalmente escrituras, y r_await es enorme en HDDs. Eso es consistente con consultas DDT que fallan en ARC.
Decisión: si ves presión por lecturas aleatorias ligada a ráfagas de escritura, trata los fallos de DDT como sospecha principal y valida con estadísticas/histogramas DDT.
Task 6: Confirma recordsize/volblocksize y por qué importa para dedup
cr0x@server:~$ zfs get -o name,property,value recordsize tank/vm
NAME PROPERTY VALUE
tank/vm recordsize 128K
Qué significa: bloques más grandes implican menos entradas DDT para el mismo volumen de datos, pero pueden reducir oportunidades de dedup y afectar patrones de E/S aleatoria.
Decisión: para zvols de VM, considera alinear volblocksize con la carga y evita bloques muy pequeños a menos que tengas una razón. Pero no intentes “tunear” para salir de un ARC insuficiente.
Task 7: Comprueba si la compresión puede reemplazar dedup para tu 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
Qué significa: ya obtienes 1.48x con compresión, que a menudo supera a ratios “meh” de dedup en la práctica.
Decisión: si la compresión te da ahorros significativos, esa es la palanca de espacio por defecto. Deja dedup para casos estrechos y validados.
Task 8: Comprueba si existe un special vdev (aceleración de metadata) y si está dimensionado sensatamente
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
Qué significa: hay un mirror special vdev. Los metadatos (y potencialmente bloques pequeños) pueden aterrizar aquí, reduciendo la latencia de consulta DDT cuando ARC falla.
Decisión: si usas dedup, special vdev puede ayudar. Pero trátalo como tier-0: si lo pierdes sin redundancia, puedes perder el pool.
Task 9: Comprueba hacia dónde van los metadatos (política special_small_blocks)
cr0x@server:~$ zfs get -o name,property,value special_small_blocks tank
NAME PROPERTY VALUE
tank special_small_blocks 0
Qué significa: solo los metadatos se están asignando al special vdev, no los bloques pequeños de archivos. La DDT es algo parecido a metadatos, así que puede beneficiarse incluso con 0.
Decisión: no pongas special_small_blocks a ciegas sin entender capacidad y dominio de fallos. Puede desplazar mucha data al special vdev.
Task 10: Comprueba comportamiento de txg sync (cuando el sistema “se atasca”)
cr0x@server:~$ cat /proc/spl/kstat/zfs/txg | head
0 0 0x01 7 448 13056000 2112000
# txgs synced txg ...
Qué significa: las estadísticas txg varían por versión; el punto es correlacionar los “atascos” con la cadencia de sync. Las lecturas aleatorias inducidas por dedup pueden alargar el trabajo de sync y bloquear el progreso.
Decisión: si los intervalos de sync se disparan bajo carga de escritura, sospecha metadatos y fallos de DDT; luego valida con iostat de latencia y presión de ARC.
Task 11: Demuestra que el dataset está pagando la tasa de dedup ahora mismo (observa latencia de escritura con 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
Qué significa: alta latencia en cola (clat max 180ms) es típico cuando las consultas de metadatos van a discos lentos, aunque la media parezca “aceptable”.
Decisión: si la latencia cola es fea y se correlaciona con cargas intensas de dedup, deja de intentar “optimizar la app”. Arregla el camino de almacenamiento o elimina dedup.
Task 12: Comprueba deriva de la propiedad dedup (alguien lo activó “temporalmente”)
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
Qué significa: dedup se está propagando por herencia, que es cómo “un experimento en un dataset” se convierte en “todo el pool ahora paga una tasa de consultas”.
Decisión: rompe la herencia donde no la necesitas. Dedup debe ser opt-in, no algo ambiente.
Task 13: Apaga dedup para nuevas escrituras (sin fingir que arregla los datos existentes)
cr0x@server:~$ sudo zfs set dedup=off tank/vm
Qué significa: los nuevos bloques no serán dedupeados, pero los bloques antiguos siguen dedupeados y todavía dependen de la DDT para lecturas.
Decisión: haz esto pronto como contención de daños, luego planifica la solución real: reescribir datos a un destino sin dedup.
Task 14: Estima “qué tan mala puede ser la reescritura?” comprobando used y 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
Qué significa: los datos lógicos son mayores que los físicos. Si reescribes sin dedup, necesitas espacio para la expansión física (o un destino más grande).
Decisión: planifica capacidad antes de migrar fuera de dedup. Muchos planes de “simplemente copia” mueren cuando el uso físico se dispara.
Task 15: Migra de forma segura con send/receive (la herramienta aburrida que funciona)
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
Qué significa: has creado una copia consistente. Si el destino tiene dedup=off, los nuevos bloques se escribirán sin dedup.
Decisión: esta es la rampa de salida limpia. Usa -u para recibir desmontado, valida y luego cambia con deliberación.
Tres mini-historias corporativas desde el campo
1) Incidente causado por una suposición errónea: “Dedup es por dataset, así que no puede dañar el pool”
Una empresa mediana tenía un clúster de virtualización respaldado por ZFS. El equipo de almacenamiento habilitó dedup en un dataset usado para plantillas de VM, esperando grandes ahorros.
La suposición era reconfortante: es solo un dataset; en el peor de los casos solo ralentiza ese dataset.
Dos semanas después, el helpdesk empezó a recibir tickets de “lentitud aleatoria”. Arranques de VM que se colgaban ocasionalmente. Las ventanas de backup nocturnas se extendieron y luego incumplieron SLAs.
La respuesta inicial fue predecible: añadir más discos al RAIDZ, ajustar el caché del hipervisor, culpar a la red y luego a “vecinos ruidosos”.
El punto de inflexión fue notar IOPS de lectura en aumento durante periodos de escritura intensa. Eso no es normal. Se hizo obvio que las consultas DDT estaban fallando en caché y
forzando lecturas aleatorias a través del pool. Aunque dedup se habilitó por dataset, la DDT vivía en el pool y la ruta de I/O afectaba a todos quienes compartían esos vdevs.
La solución no fue heroica. Deshabilitaron dedup para nuevas escrituras de inmediato, luego planificaron una migración por send/receive a un pool sin dedup durante un fin de semana.
El rendimiento se estabilizó antes de que la migración terminara, porque el conjunto de trabajo activo se movió primero y la presión sobre la DDT bajó.
La conclusión del postmortem fue directa: en almacenamiento compartido, las características “por dataset” aún tienen consecuencias a nivel de pool. Si la ruta caliente de la característica toca metadatos a nivel de pool,
trátala como un cambio de pool, no como un experimento de dataset.
2) Optimización que salió mal: “Activemos dedup para backups, hay muchas duplicaciones”
Otro equipo tenía un target de backup en ZFS y quería reducir el crecimiento de capacidad. Alguien miró los datos de backup, vio repetición y apostó razonablemente:
dedup debería aplastar los redundantes fulls. El entorno tenía CPU de sobra y “suficiente RAM”, y los discos eran una mezcla de HDD y un pequeño SSD para caché.
Durante un mes pareció un éxito. dedupratio subió. La compra de almacenamiento se aplazó. Todos se felicitaron y siguieron con el siguiente incendio.
Luego aumentaron la retención. La DDT creció. ARC no. Las rehidrataciones se ralentizaron. Las pruebas de restore se volvieron desagradables.
El día que se convirtió en incidente, una gran restauración corrió mientras los backups aún ingresaban datos. La latencia se disparó en todo el pool. El monitoreo mostró discos muy ocupados,
pero bajo throughput. Clásica saturación por E/S aleatoria. Las lecturas de metadatos se dispararon porque el conjunto de trabajo activo de la DDT ya no cabía en memoria.
El equipo pensó que optimizaban capacidad; en realidad habían convertido cada escritura en una operación con amplificación de lectura en el peor momento posible.
La solución fue contraintuitiva: pararon dedup, apostaron más por la compresión y cambiaron la canalización de backups para evitar escribir datos idénticos en primer lugar
(cadenas incrementales mejores, chunking diferente y políticas de copia más inteligentes).
La lección: dedup puede parecer genial al principio y luego precipitarse cuando la tabla cruza un umbral de caché. El retroceso no es gradual. Es una función escalón.
3) Práctica aburrida pero correcta que salvó el día: “Mide la DDT antes de habilitar nada”
Un equipo de servicios financieros tuvo la petición: almacenar múltiples versiones de imágenes de VM con crecimiento mínimo. El negocio pidió dedup. El equipo de almacenamiento dijo “tal vez”,
que es la versión educada de “mediremos antes de lamentar”.
Prepararon un pool de prueba pequeño que replicaba el tipo de vdev de producción y ejecutaron una ingest representativa: unos cientos de GB del churn real de imágenes de VM.
Recolectaron histogramas con zdb -DD, observaron comportamiento de ARC y midieron latencia bajo carga concurrente. Repetieron la prueba con solo compresión y con distintos recordsize/volblocksize. Nada sofisticado—solo disciplina.
Los resultados fueron aburridos en el mejor sentido. Dedup ahorró menos de lo esperado porque parte de los datos ya estaban comprimidos y porque los cambios estaban dispersos.
La compresión sola produjo la mayor parte del ahorro con rendimiento estable. Las estimaciones del conjunto de trabajo DDT indicaron que necesitarían más RAM de la que la plataforma podía ofrecer
sin una renovación de hardware.
Rechazaron dedup en producción y documentaron la justificación. Seis meses después, un nuevo ejecutivo preguntó lo mismo. El equipo reabrió el informe,
repitió una validación pequeña sobre datos actuales y otra vez evitaron un fallo autoinducido.
La conclusión: la optimización más valiosa es la que decides no desplegar después de medirla.
Chiste #2: La DDT es una hoja de cálculo que nunca deja de crecer, excepto que vive en tu ruta crítica y no acepta “ocultar columna” como solución de rendimiento.
Errores comunes: síntomas → causa raíz → solución
1) Las escrituras son lentas, pero los discos muestran muchas lecturas
Síntomas: trabajos intensos en escritura disparan IOPS de lectura, alto r_await y latencia en ráfagas.
Causa raíz: consultas DDT que fallan en ARC; las entradas DDT se traen desde disco de forma aleatoria durante las escrituras.
Solución: aumenta RAM/espacio ARC, mueve metadatos a dispositivos más rápidos (special vdev) o migra fuera de dedup. Si no puedes mantener el conjunto de trabajo DDT caliente, dedup no encaja.
2) “Apagamos dedup pero sigue lento”
Síntomas: la propiedad dedup está off, pero el pool sigue comportándose como si fuera pesado en metadatos e impredecible.
Causa raíz: los bloques existentes siguen dedupeados; las lecturas aún dependen de la DDT y su comportamiento de caché.
Solución: reescribe los datos a un destino sin dedup (send/receive), luego retira la copia dedupeada.
3) El pool va bien hasta que no; entonces todo se desploma
Síntomas: meses de rendimiento aceptable y luego picos repentinos, timeouts, txg sync largo y comportamiento errático de aplicaciones.
Causa raíz: la DDT cruza el umbral del conjunto de trabajo de ARC; la tasa de fallos se dispara y las lecturas aleatorias explotan.
Solución: trátalo como un fallo de planificación de capacidad: el crecimiento de la DDT debe presupuestarse como cualquier otra capacidad. Añade RAM/metadatos rápidos o sal de dedup.
4) El reinicio tarda una eternidad, la importación se siente frágil
Síntomas: las importaciones de pool son lentas, el sistema parece colgado tras reiniciar bajo carga.
Causa raíz: conjuntos de metadatos enormes (incluida la DDT) interactuando con caché limitada; el sistema debe “calentar” un conjunto de trabajo mientras atiende E/S.
Solución: reduce la huella de dedup migrando; asegura RAM adecuada; evita mezclar carga de producción intensa con importaciones de caché frío si puedes controlar el orden.
5) Se añadió un special vdev, pero el rendimiento no mejoró
Síntomas: instalaste SSDs rápidos como special vdev, pero el dolor de dedup persiste.
Causa raíz: el conjunto de trabajo DDT sigue siendo demasiado grande; o el special vdev está saturado/demasiado pequeño; o esperabas que arreglara sobrecarga de CPU por checksum/compresión.
Solución: confirma la asignación de metadatos y la latencia del dispositivo. Dimensiona correctamente el special vdev, mórralo y aún así presupuestar RAM. Special vdev es un acelerador, no un milagro.
6) Dedup activado en un dataset que ya tiene bloques comprimidos/encriptados
Síntomas: mejora de dedupratio despreciable, pero aumento notable de latencia.
Causa raíz: las oportunidades de dedup son limitadas cuando los datos ya tienen alta entropía (comprimidos, encriptados o únicos por diseño).
Solución: mantén dedup apagado. Usa compresión, estrategias de backup inteligentes o dedup a nivel de aplicación si hace falta.
7) Dedup activado en shares de archivos generales “porque podría ayudar”
Síntomas: lentitud intermitente en cargas mixtas; correlación poco clara.
Causa raíz: el impuesto de dedup aplicado a datos amplios e impredecibles; churn de DDT con mala localidad.
Solución: restringe dedup a datasets estrechos donde la duplicación esté probada y sea estable. De lo contrario, desactívalo y migra.
Listas de verificación / plan paso a paso
Lista de decisión: ¿deberías habilitar dedup?
- Mide la duplicación en datos reales: no adivines. Si no puedes probar, por defecto di “no”.
- Estima el tamaño y conjunto de trabajo de la DDT: usa
zdb -DDen un pool de prueba o en una muestra; asume crecimiento con la retención. - Presupuesta RAM con margen: necesitas ARC también para lecturas normales. Si dedup va a desplazar todo lo demás, es un no rotundo.
- Valida la latencia bajo concurrencia: prueba con escrituras y lecturas paralelas realistas, no con un benchmark monohilo.
- Decide tu plan de salida por adelantado: si va mal, ¿cómo reescribes los datos y dónde encaja el uso físico extra?
Lista operativa: ya tienes dedup y duele
- Detén la hemorragia: pon
dedup=offpara nuevas escrituras en datasets afectados. - Cuantifica el radio de impacto: recoge
zpool get dedupratio,zdb -DD, estadísticas ARC e iostat de latencia. - Identifica datasets calientes activos: ¿de dónde vienen las escrituras y lecturas durante incidentes?
- Elige tu remedio: añade RAM, acelera metadatos (special vdev) o migra fuera de dedup. No “tunees” alrededor de una DDT sobredimensionada.
- Planifica capacidad para migración: compara
logicalusedconused. Asegura que el destino soporte la expansión física. - Ejecuta con snapshots y send/receive: mantenlo aburrido, verificable y reversible.
- Corte deliberado: recibe desmontado, valida y luego cambia puntos de montaje o consumidores.
- Limpieza post-corte: destruye datasets viejos dedupeados solo cuando estés seguro; verifica que la DDT decrezca con el tiempo conforme se liberan bloques.
Lista preventiva: evita que dedup se propague silenciosamente
- Audita la herencia de la propiedad dedup mensualmente en pools de producción.
- Requiere un registro de cambio para habilitar dedup en cualquier sitio.
- Alerta sobre cambios de dedupratio y IOPS de lectura inusuales durante ventanas de escritura.
- Mantén zonas de aterrizaje “libres de dedup” para cargas generales; haz que los datasets con dedup sean explícitos y raros.
FAQ
¿La dedup de ZFS es “mala”?
No. Es solo cara. Es excelente cuando la duplicación es alta, estable y puedes mantener el conjunto de trabajo DDT caliente (RAM y/o dispositivos rápidos para metadatos).
Es una trampa cuando la habilitas basada en esperanza.
¿Por qué dedup ralentiza tanto las escrituras?
Porque añade una consulta en la ruta de escritura. Si la entrada DDT no está en ARC, ZFS realiza lecturas aleatorias extra para encontrarla, antes de decidir si escribir.
¿Puedo desactivar dedup y recuperar el rendimiento inmediatamente?
Puedes detener que nuevas escrituras sean dedupeadas poniendo dedup=off. Pero los bloques existentes dedupeados permanecen y aún dependen de la DDT para lecturas.
La recuperación real suele requerir reescritura/migración de los datos.
¿Cómo sé si mi DDT cabe en RAM?
Estimas con zdb -DD (histogramas y tamaño “in core”) y luego validas el comportamiento: si los periodos de escritura causan lecturas aleatorias y picos de latencia,
probablemente estás fallando. “Caber” se trata de conjunto de trabajo, no del total.
¿La compresión reduce la efectividad de dedup?
A menudo sí, porque la compresión cambia la representación en disco y los datos comprimidos de alta entropía tienen menos bloques idénticos.
Prácticamente, si la compresión ya te da buenos ahorros, dedup puede no valer la pena.
¿Dedup es bueno para imágenes de VM?
A veces. Las imágenes base y muchos clones pueden deduplicarse bien, pero las cargas VM son sensibles a la latencia y generan escrituras aleatorias.
Si no puedes garantizar localidad de la DDT y metadatos rápidos, puedes convertir “espacio ahorrado” en “deuda de rendimiento”.
¿Puedo poner la DDT en SSD?
Con un special vdev puedes sesgar las asignaciones de metadatos a dispositivos rápidos, lo que puede incluir estructuras como la DDT. Esto puede reducir el dolor de fallos de caché.
Pero aumenta la dependencia de esos dispositivos; deben ser redundantes y dimensionados correctamente.
¿Añadir más discos arreglará el rendimiento de dedup?
Puede ayudar si el cuello de botella son IOPS de lectura aleatoria y estás en HDDs, pero suele ser una solución ineficiente comparada con añadir RAM o mover metadatos a almacenamiento rápido.
Si tu DDT no cabe, más spindles pueden solo hacer el thrash un poco menos terrible.
¿Cuál es la forma más segura de “des-dedupear” un dataset?
Snapshot y zfs send | zfs receive a un dataset destino con dedup=off. Valida y luego corta.
Eso reescribe los bloques y elimina la dependencia de la DDT para esos datos.
¿Existe una regla práctica de RAM por TB con dedup?
Existen reglas empíricas porque la gente necesitaba decir algo en reuniones. La respuesta real depende de la distribución de tamaños de bloque, patrones de duplicación
y tu conjunto de trabajo activo. Si no puedes medirlo, asume que será mayor de lo que quieres.
Conclusión: siguientes pasos que no arruinan fines de semana
Dedup de ZFS no es malvada. Es honesta. Te hace pagar por adelantado—en RAM, en I/O de metadatos y en complejidad operativa—para ahorrar espacio después.
Si no estás preparado para pagar continuamente, la DDT cobrará intereses en forma de latencia.
Pasos prácticos:
- Audit: ejecuta un
zfs get deduprecursivo y localiza dónde está habilitado y heredado dedup. - Cuantifica: captura la salida de
zdb -DDyzpool get dedupratiopara el pool. - Correlaciona: usa
iostat -xdurante ventanas lentas; busca IOPS de lectura y alto read await durante escrituras. - Contén: pon
dedup=offpara nuevas escrituras donde no lo necesites absolutamente. - Elige: o financias dedup correctamente (RAM + metadatos rápidos + validación), o migras fuera con send/receive y recuperas predictibilidad.
Si recuerdas solo una cosa: el éxito de dedup no es “cuánto espacio ahorramos”. Es “¿podemos aún alcanzar los objetivos de latencia cuando la DDT tenga un mal día?”