La deduplicación es una característica que parece dinero gratis hasta que tu pool empieza a moverse como si estuviera bajo el agua.
El modo de fallo más común no es “la dedup no funciona”. Es “la dedup funciona, luego la DDT no cabe en RAM,
y cada escritura aleatoria se convierte en una pequeña búsqueda del tesoro en disco”.
Si estás aquí porque alguien preguntó “¿Podemos simplemente activar la dedup?”, ya vas por delante. La respuesta correcta es,
“Quizá, después de predecir el tamaño de la DDT y demostrar que la máquina puede soportarlo.” Hagámoslo con números que puedas defender
en una revisión de cambios.
DDT: qué es (y por qué duele)
La dedup de ZFS es a nivel de bloque: cuando escribes un bloque, ZFS lo hashea y comprueba si ese contenido ya existe.
Si existe, ZFS almacena una referencia en lugar de otra copia. El índice que lo hace posible es la
Tabla de Deduplicación (DDT). Cada bloque único tiene una entrada. Cada bloque compartido añade referencias.
Esa tabla no es un accesorio bonito. Es una dependencia central de tu camino de escritura. Cuando la dedup está activada, una escritura
se convierte en “hash + búsqueda + quizá asignar + actualizar contadores de referencias”. Si la búsqueda en la DDT es rápida (acierto ARC), la dedup puede ser
tolerable. Si la búsqueda es lenta (falla de DDT en ARC → lectura desde disco), el rendimiento se cae por un precipicio.
Aquí está la verdad operativa: la dedup es mayormente un problema de dimensionamiento de memoria disfrazado de característica de almacenamiento.
También puede ser un problema de dimensionamiento de IOPS, de latencia, y un problema de “explicar a la gerencia por qué las copias de seguridad llegan tarde”.
Pero empieza con la RAM.
Una cosa más que la gente pasa por alto: activar la dedup es por dataset, pero la DDT es efectivamente por pool. Un dataset demasiado entusiasta
puede envenenar el comportamiento de caché de todo el pool al inflar el conjunto de trabajo de la DDT.
Datos interesantes y contexto para usar en reuniones
- La dedup precede a ZFS en espíritu: el almacenamiento direccionado por contenido y los conceptos de dedup por hash se usaban en appliances de backup mucho antes de ser comunes en sistemas de archivos.
- La dedup temprana de ZFS ganó reputación: muchas implementaciones de la primera ola la activaron en pools de propósito general y descubrieron que “funciona” y “rápido” no son sinónimos.
- La DDT rastrea bloques únicos, no el tamaño lógico de datos: dos datasets de 1 TB pueden producir tamaños de DDT muy diferentes según recordsize, compresión y churn.
- La compresión cambia las matemáticas de la dedup: ZFS hashea el bloque almacenado (después de la compresión), así que datos idénticos no comprimidos que se comprimen de forma distinta no deduplicarán como esperas.
- El tamaño de bloque determina el destino: recordsizes más pequeños aumentan el recuento de bloques, lo que incrementa entradas DDT y la necesidad de RAM. Por eso el almacenamiento de VM es una trampa común para la dedup.
- La DDT es metadata, pero no toda la metadata es igual: si añades un special vdev para metadata, la DDT puede aterrizar allí y reducir I/O aleatorio en discos lentos.
- La dedup no es gratis en lecturas: puede amplificar la fragmentación y aumentar la indirección, especialmente tras muchas snapshots y borrados.
- Dedup y cifrado no son amigos por defecto: si los datos están cifrados antes de que ZFS los vea (a nivel de aplicación), el texto plano idéntico se verá distinto y la dedup será casi nula.
La regla empírica que hace despedir gente (y la que no)
La mala regla empírica: “La dedup necesita alrededor de 1–2 GB de RAM por TB.” La escucharás repetida con la confianza
de alguien que nunca recibió una llamada de alarma por ello.
Por qué está mal: asume un cierto recordsize, una cierta relación de duplicación, un cierto patrón de churn, y un
cierto tamaño de entrada DDT en memoria. Cambia cualquiera de esos y tu número “por TB” se convierte en ficción.
La buena regla empírica: dimensiona la RAM para entradas DDT, no para terabytes brutos. Estima el recuento de bloques,
estima el recuento de bloques únicos, multiplica por un coste realista por entrada en memoria, y luego aplica un factor de conjunto de trabajo.
Si no puedes estimar el recuento de bloques, no estás listo para activar la dedup.
Broma #1: Activar dedup sin dimensionar la DDT es como comprar un camión por volumen de carga e ignorar el límite de peso del puente. El puente siempre gana.
Un método defendible: estimar entradas DDT y RAM
Paso 1: Estima cuántos bloques vas a dedupear
Las entradas DDT escalan aproximadamente con el número de bloques únicos escritos en datasets con dedup activada.
Puedes aproximar el recuento de bloques como:
Recuento de bloques ≈ bytes lógicos referenciados / tamaño medio de bloque
“Tamaño medio de bloque” no es necesariamente tu recordsize o volblocksize, porque los bloques reales son
más pequeños en los finales de archivo, la metadata no se cuenta igual, y la compresión cambia el tamaño almacenado. Pero como
número de planificación, empieza con el tamaño de bloque configurado para el tipo de dataset:
- zvols de VM:
volblocksize(a menudo 8K–32K en configuraciones orientadas a rendimiento) - Datasets de archivos:
recordsize(a menudo 128K por defecto)
Paso 2: Estima la fracción única (ratio de dedup)
El ratio de dedup te dice cuánto se duplica la información. Pero para dimensionar la DDT quieres lo contrario:
la fracción que es única.
Ejemplo: si esperas 2.0x dedup, entonces alrededor del 50% de los bloques son únicos. Si esperas 1.1x, entonces alrededor del 91%
son únicos, lo que significa que casi cada bloque necesita su propia entrada DDT y no ahorraste casi nada.
Paso 3: Estima memoria DDT por entrada
El formato de entrada DDT y la sobrecarga en memoria varían según la versión de OpenZFS, banderas de características, y si estás
contando solo la entrada en disco o las estructuras en ARC (que incluyen tablas hash, punteros y comportamiento de expulsión). En la práctica,
los planificadores usan un rango conservador.
Números operativos seguros para planificación:
- Mínimo optimista: ~200 bytes por bloque único (rara vez seguro para planificación de producción)
- Planificación común: ~320 bytes por bloque único
- Planificación paranoica: 400–500 bytes por bloque único (usar para pools VM de alto churn)
Si ya te han quemado antes, usa la planificación paranoica. La RAM es más barata que tu tiempo.
Paso 4: Aplica un factor de conjunto de trabajo
La DDT no necesita estar 100% residente para funcionar, pero el rendimiento depende de con qué frecuencia la ruta de escritura
necesita una entrada de la DDT que no está en ARC. Si tu carga es mayormente secuencial y de solo anexado, a veces puedes
arreglártelas con un conjunto de trabajo parcial de la DDT. Si tu carga es escrituras aleatorias a través de un amplio espacio
de direcciones (hola, virtualización), quieres una gran fracción de la DDT caliente en ARC.
Orientación práctica:
- Imágenes VM, bases de datos, runners CI: objetivo 80–100% de DDT en el conjunto de trabajo ARC
- Destinos de backup, ingestión mayormente secuencial: 30–60% puede ser tolerable si la DDT está en medios rápidos
- Cargas mixtas: asume lo peor a menos que puedas aislar dedup en un pool separado
Paso 5: Añade margen para ARC, metadata y el resto del SO
Incluso si la DDT cabe, todavía necesitas ARC para otra metadata (dnode cache, dbufs), y necesitas RAM para el kernel,
servicios y picos. Si sobrecommites memoria ZFS, este dejará de caché antes de empezar a hacer swap. Entonces tu
pool empieza a comportarse como si te odiara personalmente.
Un ejemplo concreto
Supón que planeas deduplicar 100 TB de datos referenciados en un dataset con recordsize 128K. Bloques aproximados:
100 TB / 128K ≈ 800 millones de bloques. Si el ratio de dedup es 2.0x, bloques únicos ≈ 400 millones.
RAM DDT a 320 bytes/entrada: 400,000,000 × 320 ≈ 128 GB. Eso es solo la DDT. Si necesitas 80% residente en el conjunto de trabajo,
aún quieres ~100 GB de ARC disponible para ella, además de todo lo demás.
Si tu recordsize es 16K en su lugar, el recuento de bloques es 8 veces mayor. Mismos datos, mismo ratio de dedup, ahora el número de planificación
de la DDT parece un pago inicial de una casa pequeña. Por eso el dimensionamiento “por TB” miente.
Formas de carga: cuándo la dedup se comporta y cuándo muerde
Buenos candidatos (a veces)
- Clones VDI con imágenes base idénticas donde la mayoría de bloques realmente coinciden y se mantienen estables.
- Repositorios de backup donde escribes bloques grandes y repetitivos y rara vez reescribes datos antiguos.
- Almacenes de artefactos donde los mismos binarios aterrizan repetidamente y son mayormente inmutables.
Malos candidatos (a menudo)
- Almacenamiento general de VM con bloques pequeños y churn constante: actualizaciones de SO, swap, logs, bases de datos.
- Bases de datos que reescriben páginas con frecuencia. Deduplicarás menos de lo que esperas y pagarás costes de búsqueda para siempre.
- Datos cifrados en origen (cifrado a nivel de aplicación) que destruyen la redundancia.
El churn es el asesino silencioso
La dedup ama los duplicados estables. El churn cambia los hashes. Incluso si tu ratio de dedup es decente en un momento dado, un churn alto
fuerza actualizaciones frecuentes de la DDT, mayor amplificación de escritura y más I/O aleatorio. Las snapshots pueden preservar bloques antiguos,
haciendo que la DDT sea mayor y el pool más fragmentado. Tus “ahorros” se convierten en “deuda de metadata”.
Special vdevs, metadata y dónde vive realmente la DDT
La DDT se almacena en disco como parte de la metadata del pool. Cuando no está en ARC, ZFS debe recuperarla del disco. Si ese disco
son un montón de HDDs, tus búsquedas de dedup se alinearán y esperarán su turno a 10 ms por lectura. Así es como obtienes latencias de varios segundos
en hardware que por lo demás es decente.
Un special vdev puede albergar metadata (y opcionalmente bloques pequeños), lo que puede incluir bloques de la DDT. Poner la
DDT en SSD/NVMe rápido puede reducir dramáticamente la penalización cuando sale del ARC. No elimina la necesidad de
RAM, pero puede convertir lo “catastrófico” en “molesto y medible”.
Orientación con opinión:
- Si estás considerando dedup sobre rust (HDD), presupuestar un special vdev o no hagas dedup.
- Si tienes un special vdev, refléjalo (mirror). Perderlo puede significar perder el pool, dependiendo de la configuración y qué se movió allí.
- No trates el special vdev como un “acelerador mágico” de dedup. Es un reductor de latencia para fallos, no un sustituto de aciertos en ARC.
Tareas prácticas: comandos, salidas y decisiones (12+)
Estas son las cosas que realmente ejecuto antes de que alguien active la dedup. Cada tarea incluye: un comando, lo que significa la salida,
y la decisión que impulsa. Reemplaza nombres de pool/dataset para que coincidan con tu entorno.
Task 1: Confirmar que la dedup está actualmente desactivada (o dónde está activada)
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
Significado: La dedup está deshabilitada en todos los lugares mostrados. Si algunos datasets muestran on, ya tienes una DDT.
Decisión: Si algún dataset está on, debes medir la DDT existente y su comportamiento de caché antes de ampliar su uso.
Task 2: Comprobar flags de características del pool relevantes para dedup
cr0x@server:~$ zpool get -H -o property,value feature@extensible_dataset tank
feature@extensible_dataset active
Significado: Las feature flags afectan formatos en disco y a veces comportamiento. Esto confirma que el pool usa características modernas.
Decisión: Si estás en una versión de pool antigua o en modo de compatibilidad, reconsidera la dedup o planifica una migración primero.
Task 3: Capturar recordsize / volblocksize para estimar el recuento de bloques
cr0x@server:~$ zfs get -o name,property,value recordsize tank/backups
NAME PROPERTY VALUE
tank/backups recordsize 1M
Significado: recordsize 1M significa bloques menos numerosos y más grandes: la DDT crece más lento que en cargas 128K/16K.
Decisión: Las cargas con recordsize grande son más amigables para la dedup desde la perspectiva de dimensionamiento DDT.
Task 4: Para zvols, comprobar volblocksize (aquí es donde empieza el problema)
cr0x@server:~$ zfs get -o name,property,value volblocksize tank/vm/zvol0
NAME PROPERTY VALUE
tank/vm/zvol0 volblocksize 8K
Significado: 8K de bloques significa un recuento de bloques enorme para datos lógicos grandes. Eso infla las entradas DDT rápidamente.
Decisión: Si planeas dedup en zvols de 8K, asume memoria por entrada “paranoica” y requisito de conjunto de trabajo cercano al 100%.
Task 5: Medir bytes referenciados (lo que la dedup debe indexar)
cr0x@server:~$ zfs list -o name,used,refer,logicalused,logicalrefer -p tank/vm
NAME USED REFER LOGICALUSED LOGICALREFER
tank/vm 54975581388800 54975581388800 87960930222080 87960930222080
Significado: Los datos lógicamente referenciados son ~80 TB. El tamaño de la DDT se correlaciona más con bloques lógicos que con bytes físicos.
Decisión: Usa logicalrefer y el tamaño de bloque para estimar el recuento de bloques. Si existen snapshots, considera también el crecimiento de datos referenciados.
Task 6: Comprobar compresión (cambia bloques almacenados y efectividad de dedup)
cr0x@server:~$ zfs get -o name,property,value compression tank/vm
NAME PROPERTY VALUE
tank/vm compression lz4
Significado: LZ4 es bueno. La compresión puede reducir I/O físico, pero la dedup hashea bloques comprimidos.
Decisión: Si la compresión está desactivada, considera activarla antes de dedup. Si la compresión ya da buenos ahorros, el beneficio marginal de dedup puede ser pequeño.
Task 7: Inspeccionar tamaño y presión de ARC (¿puede mantener una DDT grande?)
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
Significado: ARC es ~96G con objetivo ~110G. Tasa de fallos ~8–9% durante la muestra. Esto es decente, pero aún no dice nada sobre la residencia de la DDT.
Decisión: Si ARC ya está constreñido, activar dedup luchará por la caché con todo lo demás. Planifica ampliaciones de RAM o aisla dedup en un pool separado.
Task 8: Comprobar si swap está en uso (una bandera roja para planes de dedup)
cr0x@server:~$ free -h
total used free shared buff/cache available
Mem: 256Gi 142Gi 12Gi 3.0Gi 102Gi 104Gi
Swap: 16Gi 0.0Gi 16Gi
Significado: No hay uso de swap. Bien. Si swap se usa bajo carga normal, ya tienes falta de memoria.
Decisión: Si swap está activo, no actives dedup hasta que arregles la presión de memoria. Dedup + swapping es como formar leyendas.
Task 9: Estimar potencial de duplicación sin activar dedup (muestreo con 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
Significado: Esta muestra cruda muestra archivos grandes duplicados (mismo hash apareciendo varias veces). No es dedup a nivel de bloque, pero es una prueba de olor.
Decisión: Si no ves casi duplicados ni siquiera a nivel de archivo, la dedup a nivel de bloque probablemente no justificará su coste.
Task 10: Si la dedup ya existe, medir tamaño de DDT y aciertos
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
Significado: “in core” es el uso de memoria para estructuras DDT actualmente cacheadas. Si esto está cerca del margen disponible del ARC, vives peligrosamente.
El histograma muestra dónde gana la dedup: buckets con mayor refcount significan bloques compartidos.
Decisión: Si “in core” compite con el headroom del ARC y tu latencia ya es ajustada, añade RAM o mueve la DDT a almacenamiento más rápido (special vdev).
Task 11: Vigilar latencia y encolamiento durante la carga (la dedup amplifica esto)
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
Significado: Await alto y aqu-sz creciente indica que los discos se están encolando. Las fallas de dedup añaden más lecturas aleatorias, incrementando el encolamiento.
Decisión: Si ya estás saturando los discos, la dedup probablemente degradará la latencia a menos que muevas lecturas DDT a medios rápidos y la mantengas caliente en ARC.
Task 12: Confirmar presencia de special vdev y ashift (plan de velocidad de metadata)
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
Significado: Existe un special vdev en espejo. Bien. Esa es tu “vía rápida” para metadata.
Decisión: Si planeas dedup, valida que los bloques DDT sean elegibles para el special vdev y que tenga durabilidad y margen de capacidad.
Task 13: Comprobar special_small_blocks (no muevas datos por accidente)
cr0x@server:~$ zfs get -o name,property,value special_small_blocks tank
NAME PROPERTY VALUE
tank special_small_blocks 0
Significado: Solo la metadata va al special vdev; los pequeños bloques de datos no. Eso es más seguro para la planificación de capacidad.
Decisión: Considera dejarlo en 0 a menos que tengas un objetivo claro y un modelo de capacidad. Mover pequeños bloques a special puede ser genial o catastrófico según tasas de llenado.
Task 14: Modelar recuento de entradas DDT desde tamaño de bloque y datos lógicos
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
Significado: Esta es la salida de “no lo hagas”. 80 TB a bloques de 8K con solo 1.3x dedup implica DDT de varios terabytes en memoria. Eso no es un problema de ajuste.
Decisión: No actives dedup en esta carga. Cambia el diseño: bloques más grandes, otra tecnología de almacenamiento, o acepta el coste de capacidad.
Task 15: Comprobar número de snapshots y potencial de churn
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
Significado: Muchas snapshots pueden fijar bloques antiguos, manteniendo entradas DDT vivas y evitando la reclamación de espacio. Incluso snapshots con “USED 0” pueden representar árboles referenciados enormes.
Decisión: Si la retención es larga y el churn alto, asume que la DDT crece y permanece grande. Considera limitar la dedup a datasets con políticas de snapshot controladas.
Guion de diagnóstico rápido: encontrar el cuello de botella en minutos
Cuando pools con dedup se vuelven lentos, la gente adivina. No lo hagas. Aquí está la ruta más rápida hacia la verdad, en orden.
Primero: ¿La DDT cabe en ARC o está haciendo thrash?
- Ejecuta
zpool status -Dy mira el tamaño “in core”. Si es enorme respecto al ARC disponible, estás en riesgo. - Revisa estadísticas de ARC durante la carga (
arcstat.py): si miss% se dispara cuando las escrituras aumentan, esa es tu señal. - Busca latencia sostenida alta incluso a bajo throughput: síntoma clásico de búsquedas de metadata que bloquean escrituras.
Segundo: ¿Las fallas llegan a medios lentos?
- Ejecuta
iostat -xy observaawait/aqu-szen los vdevs. - Si tienes un special vdev, comprueba si es el que está saturado. Si los HDDs están saturados, la DDT probablemente viene de rust.
- CPU baja + iowait alto + await de disco alto es la firma del dolor por dedup.
Tercero: ¿El sistema está falto de memoria o haciendo swap?
free -hyvmstat 1: cualquier actividad de swap durante la carga normal es una bandera roja.- Si ARC está siendo capado o encogiéndose agresivamente, la dedup empeorará a medida que la DDT se expulse.
Cuarto: ¿La carga simplemente es un mal candidato para dedup?
- Revisa el histograma DDT: si la mayoría de bloques tienen refcnt=1, la dedup es mayormente sobrecarga.
- Comprueba el ratio de dedup desde
zfs get dedup(si ya está activado) y compáralo con tus expectativas. - Si dedup está ~1.0x–1.2x, estás pagando mucho para ahorrar muy poco.
Si estás atascado: captura una instantánea corta de rendimiento bajo carga y decide si estás limitado por memoria (fallos DDT),
por IOPS (colas de disco) o por diseño (baja duplicación). No ajustes a ciegas.
Errores comunes: síntoma → causa raíz → solución
1) Las escrituras se vuelven espigadas y lentas tras activar dedup
Síntoma: La latencia salta, el throughput se colapsa, la CPU se ve bien, los discos muestran lecturas aleatorias.
Causa raíz: El conjunto de trabajo de la DDT no cabe en ARC; las búsquedas de dedup fallan y recuperan bloques DDT desde disco.
Solución: Añadir RAM (solución real), o mover metadata/DDT a un special vdev en espejo (mitigación), o desactivar dedup y reescribir los datos sin dedup (doloroso pero honesto).
2) El ratio de dedup es decepcionante (cerca de 1.0x) pero el rendimiento sigue peor
Síntoma: El pool se ralentizó; el ahorro de espacio apenas cambió.
Causa raíz: La carga tiene baja duplicación real (datos cifrados, comprimidos diferente, churn alto), pero la dedup aún fuerza búsquedas y actualizaciones de contadores.
Solución: Deja de usar dedup en ese dataset. Usa compresión, mejores elecciones de recordsize, o dedup consciente de la aplicación (software de backup) en su lugar.
3) El pool funciona bien semanas y luego empeora “sin razón”
Síntoma: El pool con dedup se degrada con el tiempo; reinicios ayudan temporalmente.
Causa raíz: La DDT crece con nuevos bloques únicos, las snapshots fijan bloques antiguos, la presión de ARC aumenta hasta que las fallas dominan.
Solución: Revisa retención y churn. Añade RAM, acorta la retención de snapshots en datasets con dedup, y asegura que la DDT esté en medios rápidos. Considera dividir cargas en pools separados.
4) El special vdev se llena y el pool entra en pánico operativamente
Síntoma: El special vdev alcanza alta utilización; fallan asignaciones de metadata; el rendimiento cae.
Causa raíz: Special vdev mal dimensionado, o special_small_blocks movió más datos de lo esperado, o el crecimiento de metadata de dedup excedió lo planeado.
Solución: Planifica capacidad del special vdev con margen. Réflectalo. Si ya es demasiado pequeño, migra datos a un nuevo pool; expandir special vdev no siempre es trivial según el layout.
5) “Luego añadimos RAM” se convierte en matemáticas de tiempo de inactividad
Síntoma: La aprobación del cambio asume que la RAM se puede añadir sin impacto, pero las ventanas de mantenimiento son escasas.
Causa raíz: Dedup activada sin un presupuesto duro de capacidad/rendimiento; ahora no puedes revertir fácilmente porque los datos ya están deduplicados.
Solución: Trata la dedup como una decisión de arquitectura. Si debes probar, hazlo en un pool clon o en un dataset limitado con un plan de reversión.
Tres mini-historias corporativas desde el frente
Mini-historia 1: El incidente causado por una suposición equivocada
Una empresa mediana consolidó “almacenamiento diverso” en un único pool ZFS. Alojó imágenes VM, artefactos CI y algunos
directorios de backup. El equipo de almacenamiento vio ISOs duplicadas y plantillas de VM repetidas y decidió que la dedup sería
una ganancia ordenada. La solicitud de cambio decía, esencialmente, “la dedup reduce espacio; se espera impacto de rendimiento mínimo.”
La suposición equivocada fue sutil: dimensionaron la dedup por TB brutos y asumieron una sobrecarga modesta de RAM. No modelaron
el recuento de bloques. El lado VM usaba zvols con bloques pequeños y el patrón de escritura era aleatorio. En horas, las alarmas de latencia
empezaron a sonar en sistemas no relacionados. Se ralentizaron builds. Las consolas VM se congelaban intermitentemente. Nadie pudo correlacionarlo
porque no “cambió” nada grande: no hubo hardware nuevo, ni lanzamiento de aplicación.
El SRE de guardia tiró de telemetría básica: la CPU estaba ociosa, la red tranquila, los discos gritaban. Las lecturas aleatorias se dispararon en
los vdevs HDD, y el await medio subió a decenas de milisegundos. La caja no estaba sin espacio. Estaba sin metadata rápida. Las búsquedas DDT fallaban en ARC y
golpeaban rust.
La solución no fue ingeniosa. Desactivaron dedup para futuras escrituras y comenzaron a migrar los peores casos a un pool sin dedup. Eso tomó tiempo,
porque los bloques ya deduplicados no se “des-deduplican” sin reescribir. La acción del postmortem fue clara: los cambios de dedup requieren una estimación de DDT
basada en recuento de bloques y churn, revisada como un plan de capacidad, no como un interruptor de característica.
Mini-historia 2: La optimización que salió mal
Otra organización intentó optimizar. Sabían que las búsquedas de dedup eran caras, así que añadieron un special vdev en SSDs rápidos y activaron special_small_blocks
para mover también bloques pequeños, razonando que “I/O pequeño aleatorio debería ir a SSD”. En papel, sonaba a ingeniería de rendimiento.
En realidad, sus cargas tenían muchos bloques pequeños. No solo metadata, sino datos reales: logs, caches de paquetes, pequeños artefactos y ruido del sistema de archivos VM.
El special vdev empezó a llenarse más rápido de lo previsto. Al acercarse a una utilización incómoda, el comportamiento de asignación se tensó. La latencia se volvió errática,
y el riesgo operativo aumentó: el special vdev ahora era crítico para mucho más del dataset de lo previsto.
Lo que salió mal no fue que los SSD sean malos. Fue que trataron el special vdev como un cajón desastre de rendimiento. Mezclaron objetivos (acelerar fallos de metadata,
acelerar lecturas de pequeños bloques, acelerar dedup) sin un único modelo de capacidad. Cuando haces eso, no tienes un diseño. Tienes una suscripción a sorpresas.
El plan de recuperación fue aburrido pero doloroso: migrar a un nuevo pool con un special vdev correctamente dimensionado, mantener special_small_blocks conservador,
y tratar la dedup como una herramienta con alcance limitado solo a datasets con duplicación probada. El rendimiento mejoró, pero la verdadera ganancia fue que el
riesgo volvió a ser legible.
Mini-historia 3: La práctica aburrida pero correcta que salvó el día
Un gran equipo de plataforma interna quería dedup para un entorno tipo VDI donde las imágenes base eran idénticas y los ciclos de parcheo eran previsibles. Estaban tentados
a activar dedup ampliamente, pero un ingeniero insistió en un enfoque gradual: crear un pool separado dedicado a candidatos a dedup, recopilar una semana de trazas de carga,
y dimensionar la RAM usando un número conservador bytes-por-entrada con margen.
Empezaron con un piloto: un subconjunto de escritorios y solo los volúmenes derivados de la plantilla. Vigilaron zpool status -D
a diario, siguieron las tasas de acierto ARC durante picos de logins, y midieron la latencia en la capa del hipervisor. También establecieron un criterio de reversión duro:
si la latencia de escritura del percentil 95 superaba un umbral por más de unos minutos, el piloto se detendría y los datos se redirigirían.
No pasó nada dramático. Ese es el punto. La DDT creció como se predijo, ARC se mantuvo sano, y el special vdev manejó las fallas ocasionales sin arrastrar los HDDs.
Los ahorros de dedup fueron reales porque la carga realmente era duplicada y bastante estable.
Cuando la dirección preguntó por qué el despliegue tardó más que activar una propiedad, la respuesta fue simple: “Pagamos por certidumbre.” La práctica aburrida—piloto + medición + dimensionamiento conservador—les salvó de convertir un proyecto de capacidad en uno de respuesta a incidentes.
Listas de verificación / plan paso a paso
Checklist pre-vuelo (antes de cualquier solicitud de cambio)
- Lista los datasets candidatos y confirma el alcance de dedup. No actives a nivel de pool por accidente.
- Registra tamaños de bloque:
recordsizepara archivos,volblocksizepara zvols. - Mide
logicalreferpara cada dataset y patrones de retención de snapshots. - EstimA el ratio de dedup de forma realista (muestreo, o usa comportamiento conocido como plantillas VDI).
- Calcula bloques únicos estimados y RAM DDT (bytes-por-entrada común y paranoico).
- Comprueba el headroom actual de ARC y la presión de memoria del sistema.
- Decide dónde caerán las fallas de la DDT: HDD vs special vdev vs todo flash.
- Escribe criterios de reversión: umbrales de latencia, tasas de fallo, disparadores de impacto de negocio.
Plan piloto (la forma segura de aprender)
- Crea un dataset dedicado (o un pool dedicado si puedes) para candidatos a dedup.
- Activa dedup solo en ese dataset y migra un subconjunto representativo de datos.
- Monitorea el crecimiento de la DDT diariamente via
zpool status -Dy sigue “in core” vs tamaño ARC. - Mide latencia de la aplicación en la capa que notan los usuarios (latencia IO de VM, tiempo de commit DB), no solo estadísticas ZFS.
- Somete a estrés un periodo pico intencionalmente (tormenta de logins, ventana de jobs) y observa el peor comportamiento.
- Expande el alcance solo después de poder predecir el crecimiento de la DDT a lo largo del tiempo, no solo el día uno.
Plan de despliegue a producción (lo que firmaría)
- Asegura que la capacidad de RAM cumple el objetivo conservador del conjunto de trabajo DDT más headroom ARC.
- Asegura que el special vdev (si se usa) está en espejo y dimensionado con amplio headroom para crecimiento de metadata.
- Activa dedup en un conjunto limitado de datasets; no lo dejes “heredar” a todo a menos que disfrutes sorpresas.
- Establece políticas de retención de snapshots explícitas para datasets con dedup para controlar crecimiento a largo plazo de la DDT.
- Programa puntos de validación post-cambio (1 día, 1 semana, 1 mes) con las mismas métricas cada vez.
Broma #2: La dedup es la única función donde ahorrar espacio puede costarte tiempo, dinero y la opinión de varios nuevos compañeros.
Preguntas frecuentes
1) ¿Puedo predecir el tamaño de la DDT con precisión antes de activar la dedup?
Con precisión, no. Puedes predecirlo lo suficientemente bien como para tomar una decisión segura estimando recuento de bloques, fracción única,
y usando bytes-por-entrada conservadores. El enfoque piloto te acerca a lo “preciso” porque mide el comportamiento real en el tiempo.
2) ¿“La DDT debe caber en RAM” es siempre cierto?
Para buen rendimiento en cargas de escritura aleatoria, funcionalmente sí: el conjunto de trabajo caliente debe caber. Si la carga es
mayormente ingestión secuencial y los bloques DDT están en medios rápidos, la residencia parcial puede ser soportable. Pero “soportable”
no es lo mismo que “agradable”.
3) ¿Cuál es un número razonable de bytes por entrada DDT?
Usa 320 bytes como línea base de planificación común y 400–500 bytes para cargas de alto churn y bloques pequeños. Si la respuesta cambia tu decisión,
elige el número mayor y duerme mejor.
4) ¿La compresión ayuda o perjudica a la dedup?
Puede ayudar la capacidad y reducir I/O físico, pero puede reducir la coincidencia de dedup si datos “similares” se comprimen en secuencias de bytes distintas.
Recuerda también: ZFS dedup hashea el bloque almacenado, que típicamente está comprimido.
5) Si activo dedup y me arrepiento, ¿puedo apagarla de forma segura?
Puedes desactivar dedup para futuras escrituras configurando dedup=off en el dataset. Los bloques deduplicados existentes permanecen deduplicados hasta reescribirlos.
Volver a un estado verdaderamente no dedupado suele implicar copiar datos a un dataset no dedup (o nuevo pool) y hacer el corte.
6) ¿Un special vdev resolverá mis problemas de dedup?
Ayuda haciendo que las fallas de DDT/metadata sean menos terribles. No reemplaza la RAM. Si tu tasa de fallos DDT es alta, aún pagas I/O extra y latencia extra;
solo que lo pagas en NVMe en vez de en HDD.
7) ¿Debo activar dedup para almacenamiento de VM?
Usualmente no, a menos que tengas un patrón de duplicación muy específico y estable (como clones VDI) y hayas modelado tamaño de bloque y churn.
Para granjas de VM generales, compresión + buenas plantillas + estrategias de aprovisionamiento fino son más seguras.
8) ¿Qué métricas prueban que la dedup vale la pena?
Dos categorías: capacidad y latencia. Capacidad: ratio de dedup significativo (no apenas por encima de 1.0x) y estable en el tiempo.
Latencia: p95/p99 de IO se mantiene dentro del SLO durante periodos pico de escritura. Si falla cualquiera, la dedup es un mal intercambio.
9) ¿La dedup interactúa con snapshots?
Sí. Las snapshots fijan bloques antiguos, lo que mantiene entradas DDT vivas y puede aumentar la fragmentación. Retención larga más churn alto es receta clásica
para crecimiento de la DDT y empeoramiento de la presión de caché con el tiempo.
10) ¿Cuál es la forma más segura de probar dedup en producción?
No lo “pruebes” ampliamente. Piloto en un dataset aislado (o pool separado), con criterios de reversión explícitos, y mide crecimiento de la DDT y latencia durante picos reales.
Conclusión: próximos pasos que no arruinarán tu fin de semana
La dedup no es una casilla para marcar. Es un compromiso de portar un índice grande y crítico para el rendimiento en memoria y mantenerlo
lo bastante caliente para que tu ruta de escritura no se convierta en terapia de I/O aleatorio.
Pasos prácticos siguientes:
- Inventaria los datasets candidatos y registra sus tamaños de bloque (
recordsize/volblocksize). - Mide
logicalrefery retención de snapshots para entender cuántos bloques indexarás realmente. - Modela RAM DDT con bytes-por-entrada conservadores y un factor de conjunto de trabajo apropiado para la carga.
- Si las matemáticas salen feas, créetelas. Cambia el diseño: evita dedup, aísla la dedup, o muévete a una carga donde la duplicación sea real.
- Si las matemáticas son razonables, corre un piloto con criterios de reversión estrictos de latencia y observa el crecimiento “in core” de la DDT en el tiempo.
Una idea parafraseada de una voz notable en confiabilidad encaja aquí: “La esperanza no es una estrategia,” atribuida en espíritu a pensadores de operaciones como Gene Kranz.
Dimensiona la DDT como dimensionas el riesgo: con evidencia, margen y un plan de reversión.