Enciendes compression=lz4, ejecutas una “prueba” y los números parecen asombrosos. Genial—hasta que tu host de VM más concurrido empieza a tartamudear a las 10 a. m.,
la latencia de tu base de datos se vuelve extrañamente variable, y la única métrica que alguien cita es compressratio de zfs get.
La compresión no “falló”. La prueba falló.
La compresión en ZFS suele ser un almuerzo gratis, pero los sistemas de producción no sirven almuerzos; sirven SLA.
Si quieres ganancias reales—no placebo—necesitas medir las cosas correctas, en el orden correcto, bajo la carga correcta, y luego decidir como un adulto.
Qué significan las “ganancias reales” para la compresión ZFS
“La compresión hace las cosas más pequeñas” es cierto en el mismo sentido en que “hacer ejercicio te hace más saludable” es cierto. Sí, por lo general.
También: depende de lo que hagas, cómo lo midas y en qué ya estés limitado.
En producción, la compresión en ZFS tiene éxito cuando logra uno (o más) de estos resultados bajo tus restricciones reales:
- Menor latencia p95/p99 (porque envías menos bytes a través de un dispositivo lento o un HBA saturado).
- Mayor rendimiento (porque tu almacenamiento está limitado por ancho de banda y la compresión intercambia CPU por menos escrituras).
- Cache más efectiva (ARC/L2ARC retiene más datos lógicos por byte físico cuando los bloques se comprimen bien).
- Menor amplificación de escritura (menos bloques físicos escritos por el mismo cambio lógico).
- Más capacidad utilizable sin cambiar el dominio de fallos o la disposición del pool.
La compresión no tiene éxito cuando la única “victoria” es un número más bonito en zfs get compressratio.
Si tu CPU se vuelve el limitante, si la cola de latencia se amplía, o si tu carga ya no está limitada por I/O, puedes empeorar absolutamente las cosas.
El trabajo de una prueba de compresión no es declarar un códec ganador. El trabajo es responder una pregunta específica:
“Con esta carga, en este hardware, bajo estas restricciones, qué cambia en latencia, rendimiento, CPU y volumen de escrituras cuando cambia la compresión?”
Hechos e historia que realmente importan
Un poco de contexto te evita tuning por culto. Aquí hay hechos concretos que aparecen en la interpretación real de benchmarks:
- ZFS se diseñó para integridad de datos de extremo a extremo (checksums por todas partes), no para “máximos IOPS a cualquier costo”. La compresión vive dentro de ese modelo.
- LZ4 se convirtió en la recomendación por defecto en muchos círculos de OpenZFS porque es lo suficientemente rápido como para que la CPU rara vez sea el limitante en cargas de servidor típicas.
- OpenZFS moderno añadió ZSTD porque la gente quería mejores ratios con niveles configurables; no es “solo un poco más lento”, es un rango.
- La compresión es por bloque y ocurre antes de escribir al disco; los bloques incompresibles se almacenan sin comprimir (usualmente con pequeño overhead).
- El recordsize importa porque la compresión opera sobre bloques de registro; los mismos datos pueden comprimirse distinto a 16K vs 128K.
- Copy-on-write cambia la economía: reescribir pequeñas partes de bloques grandes puede causar más churn; la compresión puede reducir el churn físico si los datos se comprimen.
- ARC cachea los datos post-compresión de forma efectiva: si los datos se comprimen 2:1, ARC puede contener aproximadamente el doble de contenido lógico, cambiando el comportamiento de lectura.
- Dedup y compresión no son aliados por defecto: dedup amplifica metadatos y presión de RAM; añadir pruebas de compresión en pools con dedup puede engañarte.
- Los vdevs especiales cambiaron el comportamiento de metadatos: metadatos y bloques pequeños pueden vivir en medios más rápidos, así que “la compresión mejoró la latencia” puede ser realmente “el vdev especial te salvó”.
Una idea para mantener en la pared: Todo falla, todo el tiempo
— Werner Vogels (idea parafraseada). El ajuste de compresión no es la excepción:
prueba pensando en modos de falla y saturación, no solo en ejecuciones heroicas de laboratorio vacías.
Principios de benchmarking (o cómo no engañarte)
1) Decide qué optimizas: cola de latencia, rendimiento o capacidad
Si al negocio le importa la latencia p99, deja de mostrar gráficos de rendimiento medio. Si al negocio le importa la capacidad, deja de mostrar “IOPS aumentaron”
cuando tu conjunto de datos es incompresible. Elige el objetivo y luego la medición.
2) Separa el comportamiento con caché caliente del caché frío
Los benchmarks ZFS sin control de caché son básicamente un test de Rorschach. ARC puede hacer que “rendimiento de disco” parezca “rendimiento de RAM” por un rato.
Eso no es malo—ARC es real—pero es un sistema diferente a “qué tan rápido van mis vdevs bajo carga sostenida”.
3) Mide el coste de CPU explícitamente
La compresión no es gratis. LZ4 es barato, ZSTD puede ser barato o caro dependiendo del nivel y los datos. Si no mides la saturación de CPU,
atribuirás mal los ralentizaciones a “overhead de ZFS” o “la red”.
4) No hagas pruebas en pools vacíos y declares victoria
El comportamiento de ZFS cambia conforme se llena el pool. Fragmentación, asignación de metaslabs y patrones de escritura evolucionan. Un pool al 20% lleno es distinto a uno al 80%.
Si solo pruebas en vacío, estás probando un mejor caso que nunca volverás a ver.
5) Usa tamaños de I/O y concurrencia realistas
Si tu carga real son lecturas aleatorias de 8K con profundidad de cola 32, no ejecutes escrituras secuenciales de 1M con profundidad 1 y lo llames “estilo base de datos”.
A la gente de almacenamiento le encantan los números; a la realidad no le importan.
6) Controla lo que ZFS puede cambiar legalmente debajo de ti
Recordsize, volblocksize (para zvols), ajustes de sync, atime, almacenamiento de xattr y políticas de vdev especial afectan el resultado.
Una prueba de compresión que cambia cuatro variables más es un ritual supersticioso, no un experimento.
Primer chiste (y sí, es corto): Un benchmark sin controles es como un simulacro de incendio donde olvidas tirar de la alarma.
Todos se sienten preparados, hasta que el edificio está en llamas.
Métricas que importan (y cuáles son vanidad)
Métricas de compresión: útiles, pero limitadas
compressratio: ratio a nivel de dataset entre espacio lógico y físico. Bueno para planificación de capacidad. Malo como predictor de rendimiento.logicalusedvsused: te dice cuánto espacio ahorra la compresión. Sigue sin ser rendimiento.- Ahorros por algoritmo (lz4 vs zstd-N): solo significativos si la forma de tus datos es estable.
Métricas de rendimiento: las que deciden la discusión
- Latencia p95/p99 de lecturas y escrituras (realidad hacia la aplicación).
- Tiempo de servicio del dispositivo (
zpool iostat -vcolumnas de latencia son oro). - Utilización de CPU y tiempo steal (la compresión quema ciclos; la virtualización añade su propio impuesto).
- Bytes escritos/leídos a nivel de vdev (la promesa de “menos bytes” de la compresión es medible aquí).
- Tasa de aciertos ARC en estado estable (la compresión puede aumentar la capacidad efectiva de la caché).
Métricas de vanidad (o “requieren contexto”)
- Pico de throughput en corrida única: a menudo solo calentamiento de caché.
- Latencia promedio: los promedios ocultan dolor en las colas. El dolor en la cola te despierta por la noche.
- IOPS sin tamaño de bloque: sin sentido. 4K IOPS vs 128K IOPS son planetas distintos.
- “Capacidad usada” de
zpool listsola: no revela cuán fragmentado o amplificado por escritura estás.
Tareas prácticas: comandos, salidas y decisiones (12+)
Estas son las tareas que realmente ejecuto cuando alguien dice, “La compresión lo hizo más lento,” o “ZSTD nivel 19 es una victoria gratis.”
Cada tarea incluye: comando, qué significa la salida y la decisión que tomas.
Task 1: Confirmar la configuración de compresión del dataset (y sorpresas heredadas)
cr0x@server:~$ zfs get -o name,property,value,source compression tank/vm
NAME PROPERTY VALUE SOURCE
tank/vm compression zstd local
Significado: La compresión está establecida en zstd localmente, no heredada. Si fuera heredada, verías inherited from ....
Decisión: Si haces benchmarking, asegúrate de que el dataset bajo prueba esté explícitamente configurado con el códec que afirmas, y registra la fuente.
Task 2: Comprobar el ratio realmente alcanzado, no solo “habilitado”
cr0x@server:~$ zfs get -o name,property,value -p used,logicalused,compressratio tank/vm
NAME PROPERTY VALUE
tank/vm used 214748364800
tank/vm logicalused 322122547200
tank/vm compressratio 1.50x
Significado: Lógico ~300G mientras que el uso físico es ~200G, así que la compresión hace trabajo (~1.5x).
Decisión: Si compressratio está ~1.00x, las pruebas de rendimiento no mostrarán ganancias de “menos bytes”; céntrate en overhead de CPU y latencia en su lugar.
Task 3: Comprobar salud y errores del pool antes de culpar a la compresión
cr0x@server:~$ zpool status -xv tank
pool 'tank' is healthy
Significado: Sin errores conocidos. Si ves errores de checksum o vdevs degradados, tu “benchmark de compresión” está probando recuperación ante fallos.
Decisión: Repara la salud del pool primero; probar en un pool degradado es como cronometrar un maratón con un zapato menos.
Task 4: Comprobar el grado de llenado del pool (tu detector de “victoria en pool vacío”)
cr0x@server:~$ zpool list -o name,size,alloc,free,capacity,fragmentation tank
NAME SIZE ALLOC FREE CAP FRAG
tank 40T 28T 12T 70% 34%
Significado: 70% lleno con fragmentación moderada. El rendimiento a 70% puede diferir mucho del 20%.
Decisión: Si tu benchmark se hizo en un pool nuevo, vuelve a ejecutar en un nivel de llenado similar o al menos divulga la diferencia.
Task 5: Observar ancho de banda y latencia a nivel de vdev durante la carga
cr0x@server:~$ zpool iostat -v tank 1
capacity operations bandwidth total_wait disk_wait
pool alloc free read write read write read write read write
-------------------------- ----- ----- ----- ----- ----- ----- ----- ----- ----- -----
tank 28T 12T 2200 1800 320M 290M 12ms 18ms 4ms 10ms
raidz2-0 28T 12T 2200 1800 320M 290M 12ms 18ms 4ms 10ms
sda - - 0 300 0B 48M - - 3ms 11ms
sdb - - 0 300 0B 48M - - 4ms 10ms
sdc - - 0 300 0B 48M - - 4ms 10ms
sdd - - 0 300 0B 48M - - 4ms 10ms
-------------------------- ----- ----- ----- ----- ----- ----- ----- ----- ----- -----
Significado: Observa total_wait y disk_wait. Si disk_wait es alto, el almacenamiento es el cuello de botella.
Si total_wait es alto pero disk_wait es bajo, puede que estés encolando en software (CPU, locks, sync, etc.).
Decisión: Si la compresión reduce el ancho de banda pero la latencia sigue alta, no estás limitado por ancho de banda; persigue CPU, sync o fragmentación.
Task 6: Vigilar la saturación de CPU mientras alternas compresión
cr0x@server:~$ mpstat -P ALL 1 5
Linux 6.6.0 (server) 12/26/2025 _x86_64_ (16 CPU)
12:10:01 AM CPU %usr %nice %sys %iowait %irq %soft %steal %idle
12:10:02 AM all 62.1 0.0 18.4 0.9 0.0 1.1 0.0 17.5
12:10:02 AM 3 96.0 0.0 3.0 0.0 0.0 0.0 0.0 1.0
Significado: Algunos núcleos están casi saturados. La compresión a menudo concentra trabajo; un solo núcleo caliente puede limitar el throughput.
Decisión: Si habilitar un códec más pesado empuja núcleos a saturación, espera picos de latencia. Baja el nivel de ZSTD, reduce la concurrencia o añade margen de CPU.
Task 7: Comprobar comportamiento ARC (¿estás midiendo RAM?)
cr0x@server:~$ arcstat 1 3
time read miss miss% dmis dm% pmis pm% mmis mm% arcsz c
12:10:20 11K 120 1 110 1 10 0 0 0 48G 64G
12:10:21 12K 140 1 130 1 10 0 0 0 48G 64G
Significado: La tasa de miss es pequeña; las lecturas provienen mayormente de ARC. Genial para aplicaciones, terrible si intentas comparar rendimiento de vdevs.
Decisión: Si necesitas números de caché frío, diseña la prueba para exceder ARC o usa datos únicos por ejecución.
Task 8: Verificar que recordsize/volblocksize coincidan con la carga
cr0x@server:~$ zfs get -o name,property,value recordsize tank/vm
NAME PROPERTY VALUE
tank/vm recordsize 128K
Significado: Los archivos tenderán a usar registros de 128K. Para imágenes de VM con I/O aleatorio de 4K, eso puede ser una descoordinación.
Decisión: Para datasets de VM, considera recordsize=16K (o zvol volblocksize=8K/16K) y vuelve a medir—la compresión interactúa con esto.
Task 9: Confirmar comportamiento de sync (benchmark de compresión vs benchmark de sync)
cr0x@server:~$ zfs get -o name,property,value sync tank/vm
NAME PROPERTY VALUE
tank/vm sync standard
Significado: Se respetan las escrituras sync. Si alguien puso sync=disabled, no aceleraron la compresión; quitaron seguridad.
Decisión: Si ves sync=disabled en pruebas de perf, descarta los resultados a menos que la producción también corra así conscientemente.
Task 10: Vigilar TXG y síntomas de throttling de escrituras vía iostat + latencia
cr0x@server:~$ iostat -x 1 3
avg-cpu: %user %nice %system %iowait %steal %idle
55.40 0.00 17.10 1.20 0.00 26.30
Device r/s w/s rMB/s wMB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
nvme0n1 0.0 1200.0 0.0 280.0 478.0 8.2 6.9 0.0 6.9 0.8 92.0
Significado: El dispositivo está muy utilizado, pero el tiempo de servicio es bajo; se está formando cola. La compresión puede reducir wMB/s, lo que puede bajar el encolamiento.
Decisión: Si la compresión reduce el ancho de banda y await baja también, es una ganancia real. Si el ancho de banda baja pero await sube, estás limitado por CPU o por sync.
Task 11: Inspeccionar escrituras lógicas vs físicas por dataset (lo que pagas a los discos)
cr0x@server:~$ zfs get -o name,property,value -p written,logicalwritten tank/vm
NAME PROPERTY VALUE
tank/vm written 53687091200
tank/vm logicalwritten 96636764160
Significado: Has escrito lógicamente ~90G pero físicamente ~50G. La compresión reduce escrituras físicas.
Decisión: En sistemas sensibles a la resistencia de SSD, esto es una de las razones más persuasivas para usar compresión incluso cuando el rendimiento “está bien”.
Task 12: Confirmar ashift y layout (los resultados de benchmark no viajan bien)
cr0x@server:~$ zdb -C tank | grep -E "ashift|vdev_tree" -n | head
12: ashift: 12
45: vdev_tree:
Significado: ashift=12 implica sectores de 4K. Si comparas con otro pool con ashift distinto, no estás comparando compresión; estás comparando alineación.
Decisión: Cuando publiques resultados internos de benchmark, incluye ashift y tipo de vdev. De lo contrario la próxima persona repite tu prueba y “descubre” física distinta.
Task 13: Comparar algoritmos de forma segura en un dataset de prueba
cr0x@server:~$ sudo zfs create -o compression=lz4 -o recordsize=128K tank/bench_lz4
cr0x@server:~$ sudo zfs create -o compression=zstd -o recordsize=128K tank/bench_zstd
cr0x@server:~$ zfs get -o name,compression,recordsize -r tank/bench_lz4 tank/bench_zstd
NAME COMPRESSION RECORDSIZE
tank/bench_lz4 lz4 128K
tank/bench_zstd zstd 128K
Significado: Dos datasets idénticos salvo la compresión. Así es como “benchmarkear la compresión” correctamente.
Decisión: Siempre aisla la variable. Si también cambias recordsize, estás benchmarkeando un paquete de cambios.
Task 14: Medir la latencia real a nivel de aplicación (no solo estadísticas de almacenamiento)
cr0x@server:~$ fio --name=randread --directory=/tank/bench_lz4 --rw=randread --bs=4k --iodepth=32 --numjobs=4 --size=8G --time_based --runtime=60 --group_reporting
randread: (g=0): rw=randread, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=libaio, iodepth=32
...
read: IOPS=128k, BW=500MiB/s (524MB/s)(29.3GiB/60001msec)
lat (usec): min=80, max=5400, avg=245.10, stdev=110.12
clat percentiles (usec):
| 50.00th=[ 230], 90.00th=[ 320], 95.00th=[ 410], 99.00th=[ 980], 99.90th=[ 2900]
Significado: Los percentiles son la historia. Si cambiar a ZSTD eleva el percentil 99.9 mientras el promedio se mantiene similar, te compraste latencia en cola.
Decisión: Usa p99/p99.9 como puerta de aprobación para cargas sensibles a latencia (bases de datos, hosts de VM).
Recetas FIO que modelan cargas reales
FIO no es “la verdad”. Es una mentira controlada que le dices al sistema para ver cómo reacciona. Di la mentira correcta.
El punto es aproximar patrones de acceso: aleatorio vs secuencial, sync vs async, tamaño de bloque, concurrencia, tamaño de working set y comportamiento de sobreescritura.
Receta A: Lecturas/escrituras aleatorias 4K tipo VM (mix)
cr0x@server:~$ fio --name=vm-mix --directory=/tank/bench_zstd --rw=randrw --rwmixread=70 --bs=4k --iodepth=32 --numjobs=8 --size=16G --time_based --runtime=120 --direct=1 --group_reporting
vm-mix: (g=0): rw=randrw, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=libaio, iodepth=32
...
read: IOPS=84.2k, BW=329MiB/s (345MB/s)
write: IOPS=36.1k, BW=141MiB/s (148MB/s)
clat percentiles (usec):
| 99.00th=[ 2200], 99.90th=[ 6500], 99.99th=[17000]
Cómo usarla: Ejecuta el mismo trabajo en bench_lz4 y bench_zstd, compara p99.9 y CPU.
Decisión: Si ZSTD mejora el ancho de banda pero empeora p99.9 materialmente, LZ4 es la opción más segura para mezclas de VM.
Receta B: Estilo base de datos, escrituras aleatorias 8K con presión de fsync
cr0x@server:~$ fio --name=db-sync --directory=/tank/bench_lz4 --rw=randwrite --bs=8k --iodepth=1 --numjobs=4 --size=8G --time_based --runtime=120 --fsync=1 --direct=1 --group_reporting
db-sync: (g=0): rw=randwrite, bs=(R) 8192B-8192B, (W) 8192B-8192B, (T) 8192B-8192B, ioengine=libaio, iodepth=1
...
write: IOPS=4200, BW=32.8MiB/s (34.4MB/s)
clat percentiles (usec):
| 95.00th=[ 1800], 99.00th=[ 4200], 99.90th=[12000]
Cómo usarla: Aquí dominan el sync y la calidad del SLOG. La compresión puede ayudar al reducir bytes, pero la CPU también puede importar.
Decisión: Si la elección del códec mueve poco los números, deja de discutir compresión y mira la ruta de sync (SLOG, latencia, encolamiento).
Receta C: Escrituras secuenciales con bloques grandes (destinos de backup, logs)
cr0x@server:~$ fio --name=seqwrite --directory=/tank/bench_zstd --rw=write --bs=1M --iodepth=8 --numjobs=2 --size=64G --time_based --runtime=120 --direct=1 --group_reporting
seqwrite: (g=0): rw=write, bs=(R) 1024KiB-1024KiB, (W) 1024KiB-1024KiB, (T) 1024KiB-1024KiB, ioengine=libaio, iodepth=8
...
write: IOPS=680, BW=680MiB/s (713MB/s)
cpu : usr=78.2%, sys=8.5%, ctx=12000, majf=0, minf=400
Cómo usarla: Las escrituras secuenciales grandes pueden estar limitadas por ancho de banda. La compresión puede reducir drásticamente bytes si los datos son tipo log/texto.
Decisión: Si el porcentaje de CPU usuario se dispara y el BW deja de aumentar, baja el nivel de ZSTD o usa LZ4. Si BW aumenta y la CPU tiene margen, ZSTD puede valer la pena.
Receta D: Prueba de sanidad de “datos incompresibles” (detectar placebo)
cr0x@server:~$ fio --name=incompressible --directory=/tank/bench_lz4 --rw=write --bs=128k --iodepth=16 --numjobs=4 --size=16G --time_based --runtime=60 --direct=1 --refill_buffers=1 --buffer_compress_percentage=0 --group_reporting
incompressible: (g=0): rw=write, bs=(R) 131072B-131072B, (W) 131072B-131072B, (T) 131072B-131072B, ioengine=libaio, iodepth=16
...
write: IOPS=5200, BW=650MiB/s (682MB/s)
Cómo usarla: Si los datos son incompresibles, la compresión no debería reducir mucho los bytes y solo puede costar CPU.
Decisión: Si ves “aceleración masiva” en escrituras incompresibles tras habilitar compresión, estás midiendo otra cosa (caché, alineación o artefacto de la prueba).
Segundo chiste (último, lo prometo): ZSTD nivel 19 en un hypervisor ocupado es como dar escritorios de pie a todos en la oficina.
La postura mejora; las quejas se vuelven más sonoras.
Guía rápida de diagnóstico
Cuando alguien dice “La compresión cambió el rendimiento”, no tienes tiempo para una recreación de laboratorio de una semana. Triagéalo como un SRE.
El objetivo es identificar rápidamente la clase de cuello de botella: disco, CPU, ruta de sync o ilusión de caché.
Primero: establece si estás limitado por I/O o por CPU
-
Comprueba la saturación de CPU durante la carga. Usa
mpstaty busca núcleos pegados y aumento del tiempo sys.
Si la CPU está al máximo, el nivel de compresión es una perilla real con consecuencias reales. -
Comprueba la utilización y los tiempos de espera de los dispositivos. Usa
zpool iostat -v 1yiostat -x 1.
Si los discos están saturados y la espera sube con el ancho de banda, estás limitado por I/O y la compresión suele ayudar.
Segundo: determina si la prueba es solo ARC
-
Observa los misses de ARC. Si miss% es pequeño durante lecturas, estás mayormente en RAM.
La “aceleración de lectura” de la compresión aquí puede ser efecto de caché, no velocidad de disco. - Aumenta el tamaño del working set. Si tu dataset cabe en ARC, no puedes sacar conclusiones sobre disco.
Tercero: confirma que la ruta de sync/escritura no es el limitante real
-
Revisa la propiedad
syncy el comportamiento sync de la carga. Si estás benchmarkeando una base de datos, el sync domina. - Observa la latencia durante escrituras sync sostenidas. La compresión puede reducir bytes escritos, pero la latencia de SLOG y el comportamiento TXG pueden seguir dominando.
Cuarto: valida que no cambiaste otras variables
- Registra las propiedades del dataset:
recordsize,atime,xattr,primarycache,logbias,redundant_metadata. - Registra propiedades/disposición del pool: ashift, RAIDZ vs mirrors, vdevs especiales, SLOG, L2ARC.
Errores comunes: síntoma → causa raíz → solución
1) “Compresión habilitada, throughput duplicado” (pero solo en la segunda ejecución)
Síntoma: La primera ejecución es más lenta, la segunda vuela, la compresión “gana”.
Causa raíz: ARC calentado. Benchmarqueaste RAM, no discos. La compresión también puede aumentar la eficacia de ARC, exagerando el efecto.
Solución: Usa un working set mayor que ARC, usa nombres de archivo únicos por ejecución y compara el estado estable tras el warmup. Rastrea miss% de ARC.
2) “ZSTD es más lento que LZ4, así que ZSTD es malo”
Síntoma: Colas de latencia más altas y IOPS menores al cambiar a ZSTD.
Causa raíz: Saturación de CPU o cuello de botella en un solo núcleo. Nivel de ZSTD demasiado alto para la concurrencia y margen de CPU disponible.
Solución: Prueba ZSTD en niveles más bajos; mide la CPU. Si no puedes permitir CPU, elige LZ4 y sigue con tu vida.
3) “compressratio es 2.5x, así que deberíamos obtener 2.5x de rendimiento”
Síntoma: Excelentes ratios de compresión, poco o ningún cambio en rendimiento.
Causa raíz: La carga no está limitada por ancho de banda; está limitada por latencia, sync o CPU en otro lugar (checksums, metadatos, locks de la aplicación).
Solución: Usa percentiles de latencia y tiempo de espera de vdev para encontrar el limitante real. La compresión reduce bytes, no necesariamente latencia de IOPS.
4) “Hicimos pruebas con sync=disabled para ver el máximo”
Síntoma: Números de escritura irreales, seguidos de “producción no coincide”.
Causa raíz: Quitaste las garantías de durabilidad y cambiaste la ruta de escritura completamente.
Solución: Benchmarquea con la semántica de sync de producción. Si debes probar sync=disabled, márcalo como “modo inseguro” y no lo uses para decisiones.
5) “La compresión hizo que los snapshots sean costosos”
Síntoma: El crecimiento del espacio de snapshots parece peor después del cambio de compresión.
Causa raíz: Desajuste de recordsize y patrones de reescritura. Recordsize grande + sobrescrituras pequeñas aumenta churn; la compresión cambia el layout físico y puede alterar la fragmentación.
Solución: Alinea recordsize/volblocksize al tamaño de sobreescritura. Para imágenes de VM y bases de datos, bloques más pequeños suelen ser más estables.
6) “Habilitamos ZSTD y ahora scrub es más lento”
Síntoma: El scrub tarda más y compite con la carga.
Causa raíz: El scrub lee y verifica bloques; la descompresión puede añadir overhead de CPU durante el scrub en sistemas ocupados.
Solución: Programa scrubs en ventanas de baja carga, limita el impacto del scrub y evita niveles altos de compresión en sistemas sin margen de CPU.
Tres microhistorias corporativas desde las trincheras de compresión
Microhistoria 1: El incidente causado por una suposición equivocada
Un equipo desplegó un nuevo clúster de VM y estandarizó en ZSTD porque los números de staging se veían bien. Hicieron lo que todos hacen:
copiar una imagen dorada, ejecutar un par de pruebas de copia de archivos y luego celebrar.
La suposición equivocada fue sutil: asumieron que “se comprime bien” equivale a “funciona rápido”. Su imagen dorada era mayormente archivos de OS—altamente compresibles,
mayormente de solo lectura y muy amigables con la caché. Producción era una flota mixta con servicios con muchos logs y algunos middleware parlanchines que hacían escrituras pequeñas constantes.
Lunes por la mañana, el clúster alcanzó carga máxima. Las gráficas de CPU se fueron verticales, pero no de la manera divertida. La latencia subió, luego se volvió variable, luego subió otra vez.
El equipo de hypervisor culpó a la red. La red culpó al almacenamiento. Al almacenamiento le achacaron “vecinos ruidosos”.
Mientras tanto, el pager no le importó el organigrama.
El problema real: ZSTD en un nivel agresivo estaba comprimiendo un flujo de pequeñas escrituras aleatorias. El pool no estaba saturando discos; estaba saturando CPU.
La carga tenía suficiente concurrencia como para que los hilos de compresión se convirtieran en cuello de botella, y la latencia en cola castigó a todos.
La solución fue aburrida: bajar a LZ4 para datasets de VM y reservar ZSTD para datasets de backup/archivo donde el throughput importaba más que la micro-latencia.
También cambiaron su suite de pruebas para incluir un trabajo FIO tipo VM-mix con p99.9 como puerta.
Microhistoria 2: La optimización que salió mal
Otra compañía intentó “optimizar” la compresión desactivándola para su dataset de base de datos. La razón sonaba limpia:
“Las páginas de DB ya están compactas; la compresión es CPU desperdiciada.” Tenían algunas gráficas que probaban que la CPU bajó ligeramente.
Un mes después, los indicadores de desgaste de SSD empezaron a moverse más rápido de lo esperado y las ventanas de mantenimiento nocturno crecieron.
Nada estaba en llamas, pero todo se sentía más pesado—como si el pool hubiera ganado peso.
La investigación encontró que, aunque las páginas de la base de datos no eran dramáticamente compresibles, sí comprimían lo suficiente para reducir escrituras físicas de forma significativa.
Desactivar compresión aumentó los bytes reales escritos, lo que incrementó la amplificación de escritura del dispositivo y empujó el pool más cerca de su techo de ancho de banda.
El “ahorro de CPU” era real, pero no era el recurso limitante.
Peor aún, deshabilitar compresión redujo la capacidad efectiva de ARC para ese dataset. La amplificación de lectura aumentó,
y la base de datos empezó a fallar más en caché durante periodos de churn.
Revirtieron a LZ4, no a ZSTD, porque ofrecía la mayor parte de la reducción de escrituras con costo mínimo de CPU.
La optimización falló porque optimizó el recurso equivocado. El almacenamiento es un sistema; no puedes afinar una parte en aislamiento.
Microhistoria 3: La práctica aburrida pero correcta que salvó el día
Un equipo de plataforma mantenía una regla simple interna: cualquier afirmación de rendimiento necesita un “manifiesto de benchmark”.
No un documento gigante—solo una checklist adjunta al ticket: hardware, disposición del pool, ashift, propiedades del dataset, nivel de llenado, perfil de la carga,
postura de caché caliente/fría y los comandos exactos usados.
Durante una iniciativa de reducción de costos, alguien propuso cambiar todos los datasets a ZSTD en un nivel alto para “ahorrar almacenamiento.”
La solicitud vino con una diapositiva y un número único: “2.1x ratio de compresión.”
El manifiesto les obligó a incluir p99 de latencia bajo carga mixta y utilización de CPU.
El manifiesto también obligó a una prueba en un pool con el mismo nivel de llenado que producción y con la misma disposición de vdevs especiales.
Ahí apareció la sorpresa: el margen de CPU estaba bien en la mitad de los hosts pero ajustado en los más antiguos, que se convertirían en el eslabón débil.
Implementaron un cambio dirigido: ZSTD para datasets de archivo y logs, LZ4 para VM y servicios sensibles a latencia,
además de una nota explícita en la gestión de configuración: “no exceder nivel ZSTD X en clase de host Y”.
No ocurrió nada dramático después. Ese es el punto. La práctica aburrida los salvó de un outage muy emocionante.
Listas de verificación / plan paso a paso
Paso a paso: un benchmark de compresión que puedas defender en una revisión de cambio
- Clona las suposiciones del entorno. Mismo tipo de pool, nivel de llenado similar, clase de hardware similar, misma versión de OS/OpenZFS.
- Crea dos datasets que difieran solo en
compression(y opcionalmente nivel de ZSTD). - Bloquea propiedades: recordsize/volblocksize, sync, atime, xattr, primarycache, logbias (si aplica).
- Elige 2–3 modelos de carga que representen la realidad: mezcla de VM, escrituras DB sync, ingest secuencial/backup.
- Define criterios de éxito antes de ejecutar: umbral de latencia p99.9, throughput mínimo, utilización máxima de CPU y requisito de ganancia de capacidad.
- Ejecuta warmup y luego ventanas de medición en estado estable. Captura p95/p99/p99.9, CPU y espera de vdev.
- Repite corridas (al menos 3) y compara variabilidad. Si la variabilidad es enorme, tu entorno no está lo bastante controlado.
- Registra un manifiesto: comandos exactos, propiedades del dataset, estado del pool, nivel de llenado y cuello de botella observado.
- Toma decisiones por clase de dataset (VM, DB, logs, backups), no “un códec para gobernarlos a todos”.
Checklist operacional: antes de cambiar la compresión en producción
- Confirma margen de CPU en carga pico (no a las 2 a. m.).
- Confirma la compresibilidad de la carga (muestra datos reales, no ceros sintéticos).
- Confirma plan de reversión: cambiar propiedades es fácil; revertir regresiones en un clúster ocupado no lo es.
- Confirma monitorización: p99 de latencia, CPU y paneles de tiempo de espera de dispositivo existen y se vigilan.
- Confirma radio de impacto: cambia primero una clase de dataset o un grupo de hosts.
Preguntas frecuentes
1) ¿Debo habilitar la compresión en ZFS por defecto?
Sí—LZ4 en la mayoría de datasets de propósito general es la elección sensata por defecto. Usualmente ahorra espacio y reduce escrituras físicas con costo mínimo de CPU.
Trata “sin compresión” como una excepción que justificas con mediciones.
2) ¿Es ZSTD siempre mejor que LZ4?
No. ZSTD puede ofrecer mejores ratios, pero puede costar más CPU y empeorar la latencia en cola bajo concurrencia de pequeñas escrituras aleatorias.
Usa ZSTD donde el ahorro de capacidad importe y tengas margen de CPU; mantén LZ4 donde la latencia sea crítica.
3) ¿Por qué compressratio se ve genial pero el rendimiento no mejora?
Porque tu cuello de botella puede no ser ancho de banda. Latencia, semántica de sync, contención de metadatos, CPU o locks de la aplicación pueden dominar.
La compresión reduce bytes; no elimina mágicamente el resto de la pila.
4) ¿Puede la compresión mejorar el rendimiento de lectura?
Sí, especialmente si estás limitado por ancho de banda o por caché. Si los bloques comprimidos significan menos bytes desde disco, las lecturas pueden acelerarse.
Además, ARC puede contener más datos lógicos cuando los bloques se comprimen bien.
5) ¿Necesito recomprimir datos antiguos después de cambiar la propiedad?
La compresión en ZFS se aplica a bloques escritos posteriormente. Los bloques existentes mantienen su estado de compresión hasta que se reescriben.
Si necesitas recomprimir datos antiguos, usualmente los reescribes (por ejemplo, send/receive a un dataset nuevo o copiar internamente).
6) ¿Es válido benchmarkear con dd if=/dev/zero?
Es válido para probar comportamiento pico de compresión en datos trivialmente compresibles, pero es un pésimo proxy para producción.
Puede producir resultados espectaculares pero engañosos.
7) ¿Cómo elijo el nivel de ZSTD?
Empieza con el zstd por defecto (que implica un nivel razonable según la implementación) o elige explícitamente un nivel bajo (p. ej., zstd-3 o zstd-5)
para sistemas sensibles al rendimiento. Aumenta solo si puedes probar margen de CPU y ganas capacidad significativa.
8) ¿Afecta recordsize los resultados de compresión?
Sí. La compresión es por bloque de registro. Un recordsize mayor puede mejorar el ratio en datos secuenciales grandes pero perjudicar cargas con muchas sobreescrituras.
Ajusta recordsize para la carga primero y luego evalúa niveles de compresión.
9) ¿Por qué mis resultados de FIO varían tanto entre ejecuciones?
Causas comunes: cambios en el estado de ARC, scrubs/resilvers en background, otros inquilinos, deriva en el nivel de llenado del pool y escalado de frecuencia de CPU.
Si la variabilidad es alta, no tienes un entorno controlado, así que no tomes decisiones irreversibles basadas en ello.
10) ¿La compresión interactúa con el cifrado?
Sí. La compresión ocurre antes del cifrado en la tubería de ZFS (así que aún puede comprimir), pero el cifrado añade coste de CPU también.
Realiza pruebas con ambos habilitados si la producción usa los dos, porque la contención de CPU es acumulativa.
Siguientes pasos que puedes hacer esta semana
Si quieres resultados de compresión que puedas defender en una revisión de diseño (y no lamentar en una revisión de incidentes), haz esto:
- Crea dos datasets de prueba idénticos salvo la compresión (
lz4vszstden un nivel elegido). - Ejecuta tres perfiles FIO: VM mix (4K randrw), carga sync intensa (8K + fsync), ingest secuencial (1M escrituras).
- Para cada ejecución, captura: latencia p99/p99.9, CPU (mpstat), espera de vdev (zpool iostat), miss% ARC (arcstat) y escrituras físicas vs lógicas.
- Decide por clase de dataset. No estandarices un códec solo porque ganó en una prueba sintética.
- Despliega gradualmente con puertas de monitorización. Si p99.9 se mueve en la dirección equivocada, revierte rápido y sin vergüenza.
La compresión no es una religión. Es un trade-off. Mide el trade-off honestamente y obtendrás el ahorro de espacio y las ganancias de rendimiento prometidas—sin placebo.