Puedes comprar discos más rápidos, lanzar NVMe al problema y aun así ver las gráficas de latencia como un sismógrafo.
Luego cambias dos propiedades de ZFS—recordsize y compression—y de repente el mismo hardware parece haber recibido un ascenso.
O las ajustas mal y tus CPUs empiezan a hacer danza interpretativa mientras los discos duermen la siesta. Aquí es donde dejamos de adivinar y hacemos las cuentas.
Qué hace realmente recordsize (y qué no hace)
recordsize es el tamaño máximo de un bloque de datos que ZFS usará para un archivo en un dataset de sistema de archivos.
No es “el tamaño de bloque” por obligación. Es un techo, una pista y—dependiendo de tu carga—un volante de dirección del rendimiento.
Recordsize es un límite, no un mandato
Para archivos ordinarios, ZFS usa registros de tamaño variable hasta recordsize. Un archivo con escrituras de 4K no se convierte mágicamente en bloques de 128K
solo porque el recordsize del dataset sea 128K. ZFS almacenará bloques pequeños cuando el patrón de escritura lo requiera.
Pero cuando la carga produce escrituras secuenciales grandes, recordsize se convierte en tu realidad en disco.
Por esto recordsize es más visible en:
- lecturas/escrituras de gran streaming (backups, stores de objetos, multimedia, logs que se añaden en grandes trozos),
- datasets con archivos grandes y beneficios de read-ahead,
- cargas donde aparecen penalizaciones por read-modify-write (sobrescrituras aleatorias dentro de bloques grandes).
Recordsize no se aplica a zvols de la misma manera
Si sirves iSCSI/LUNs o discos de VM vía zvols, la perilla es volblocksize, fijada en la creación del zvol.
Ese es el tamaño de bloque fijo expuesto al consumidor. Trátalo como un contrato de API.
El villano oculto: escrituras parciales de bloque
El modo doloroso de “recordsize demasiado grande” no es que ZFS no pueda hacer I/O pequeño; es que las sobrescrituras de trozos pequeños dentro de un registro grande
pueden desencadenar read-modify-write (RMW). ZFS debe leer el bloque viejo, modificar parte de él y escribir un nuevo bloque completo (copy-on-write).
Eso cuesta I/O extra y mayor latencia—especialmente cuando el working set no cabe en ARC y haces escrituras síncronas.
Piensa en recordsize como elegir la unidad de “tamaño de transacción de almacenamiento”. Bloques mayores reducen overhead de metadatos y pueden aumentar el throughput en streaming.
Bloques mayores también aumentan el radio de impacto de una sobrescritura pequeña.
Broma #1: Configurar recordsize=1M para una base de datos con escrituras aleatorias es como llevar un camión de mudanza para entregar una llave de casa. Llegará, pero nadie estará contento.
La compresión cambia la economía
La compresión de ZFS no es solo una función de espacio. Es una función de rendimiento porque intercambia ciclos de CPU por I/O físico reducido.
Ese intercambio puede ser fantástico o terrible dependiendo de cuál sea tu cuello de botella.
El efecto central: bytes lógicos vs bytes físicos
Cada dataset ZFS tiene dos realidades:
- Lógico: lo que la aplicación cree que escribió/leyó.
- Físico: lo que realmente llegó al disco tras la compresión (y potencialmente después de efectos de padding, paridad RAIDZ, etc.).
Cuando la compresión es efectiva, reduces lecturas/escrituras físicas. Eso significa:
- menor consumo de ancho de banda de disco,
- potencialmente menos IOPS si la misma solicitud lógica se traduce en menos trabajo físico,
- ARC más efectivo (porque los bloques comprimidos en caché representan más datos lógicos por byte de RAM).
LZ4 suele ser el predeterminado por una razón
En producción, compression=lz4 es la configuración “segura y rápida” para la mayoría de los datos: configuraciones, logs, payloads textuales, muchas bases de datos, imágenes de VM.
Es lo suficientemente rápido como para que CPU rara vez sea el factor limitante a menos que estés a un throughput muy alto o con núcleos poco potentes.
Compresores más agresivos (niveles de zstd, por ejemplo) pueden ganar mucho en espacio y a veces en reducción de I/O, pero el coste en CPU se vuelve real.
Ese coste de CPU aparece en la latencia en momentos inconvenientes: ráfagas de compactación, ventanas de backup y “¿por qué la API está lenta solo durante la replicación?”.
La compresión también interactúa con recordsize
La ratio de compresión es sensible al tamaño de bloque. Registros mayores a menudo comprimen mejor porque el compresor ve más redundancia.
Por eso aumentar recordsize puede mejorar ratios de compresión en datos tipo log o columnales.
Pero otra vez: si tu carga sobrescribe pequeñas partes de bloques grandes, puedes pagar RMW más el coste de compresión en cada reescritura.
La aritmética de la combinación: IOPS, ancho de banda y CPU
Recordsize y compresión no solo “optimizan.” Deciden qué recurso será tu factor limitante:
IOPS de disco, ancho de banda de disco o CPU.
Comienza con los tres regímenes de cuello de botella
- Limitado por IOPS: la latencia está dominada por el número de operaciones (I/O aleatorio, cargas con muchos metadatos, escrituras síncronas).
- Limitado por ancho de banda: no puedes pasar más bytes por discos/red (streaming, escaneos, backups).
- Limitado por CPU: compresión/descompresión, checksums, cifrado o mucha contabilidad de ZFS consumen los núcleos.
Tu objetivo de afinamiento no es “máximo rendimiento.” Es “mover el cuello de botella al recurso más barato.”
La CPU suele ser más barata que las IOPS en flash, y enormemente más barata que las IOPS en discos giratorios. Pero la CPU no es gratis cuando los presupuestos de latencia son estrictos.
Recordsize cambia la aritmética de IOPS
Supongamos que tienes una carga leyendo 1 GiB secuencialmente.
Si tu recordsize es 128K, eso son alrededor de 8192 bloques. Si es 1M, son unos 1024 bloques.
Menos bloques significa menos búsquedas de metadatos, menos validaciones de checksum, menos operaciones de I/O y mejor comportamiento de prefetch.
Para lecturas en streaming, bloques más grandes suelen ganar.
Ahora cambia a sobrescrituras aleatorias de páginas de 8K (hola bases de datos).
Si esas páginas viven dentro de registros de 128K, una actualización aleatoria de página puede traducirse en:
- leer el 128K antiguo (si no está en ARC),
- modificar 8K dentro de él,
- escribir un nuevo 128K en otro lugar (copy-on-write),
- actualizar metadatos.
Eso es más que “una escritura de 8K”. Es un patrón de I/O amplificado, además de fragmentación con el tiempo.
Con recordsize=16K (o 8K dependiendo del tamaño de página de la BD), reduces la amplificación RMW.
Puedes aumentar overhead de metadatos, pero las bases de datos ya viven en ese mundo.
La compresión cambia la aritmética de ancho de banda (y a veces de IOPS)
Si tu ratio de compresión es 2:1, una “lectura de 100 MB” se convierte en “50 MB lectura física + descompresión”.
Si tus discos estaban saturados, acabas de liberar la mitad del ancho de banda. La latencia baja, el throughput sube, todos parecen inteligentes.
Si tus discos no estaban saturados y tus CPUs ya estaban ocupadas, te acabas de comprar un nuevo problema.
Aritmética de CPU: la descompresión está en el camino de lectura
Las escrituras pagan el coste de compresión. Las lecturas pagan el coste de descompresión. En muchos sistemas, las lecturas dominan la latencia en cola.
Eso significa que el comportamiento del descompresor importa más de lo que crees.
LZ4 suele ser rápido; los compresores de mayor nivel pueden no serlo.
ARC y compresión: “más caché” sin comprar RAM
ZFS guarda bloques comprimidos en ARC (los detalles de implementación varían por versión y flags de características, pero el efecto práctico se mantiene:
los datos comprimidos hacen la caché más efectiva).
Mejor compresión puede significar más datos lógicos en caché por GB de RAM.
Esta es una razón por la que compression=lz4 es un “sí” por defecto para datasets de propósito general.
Cuando la combinación es mágica
El escenario de mejor caso:
- recordsize relativamente grande para el patrón de acceso de la carga,
- compresión barata (lz4) y efectiva (mucho redundancia),
- los discos son el recurso caro y las CPUs tienen holgura.
El resultado son menos bytes físicos, menos paradas de disco y mayores tasas de aciertos en caché.
Cuando la combinación es una trampa
El escenario trampa:
- recordsize mayor que la granularidad de sobrescritura,
- la carga hace actualizaciones aleatorias,
- la compresión añade coste de CPU a cada reescritura de un registro grande,
- escrituras síncronas + dispositivo SLOG pequeño (o sin SLOG) magnifica la latencia.
El síntoma suele ser “el disco no está ocupado pero la latencia es terrible.” Eso es contención en CPU o en la ruta síncrona, no ancho de banda bruto.
Guía por cargas de trabajo: qué ajustar según cada caso
Existen valores por defecto porque son seguros, no porque sean óptimos. Tu trabajo es ser opinable de forma segura.
Compartición de archivos general, directorios personales, configuraciones
- recordsize: deja el valor por defecto (a menudo 128K) a menos que tengas una razón.
- compression:
lz4. - Por qué: cargas mixtas se benefician de un recordsize medio; la compresión reduce I/O físico para archivos con mucho texto.
Imágenes de VM en datasets de sistema de archivos (qcow2/raw files)
- recordsize: comúnmente 16K o 32K; a veces 64K si predominan lecturas secuenciales.
- compression:
lz4(a menudo ayuda más de lo que la gente espera). - Por qué: I/O de VM tiende a ser algo aleatorio y en bloques más pequeños; reduce la amplificación RMW.
zvols para discos de VM / iSCSI
- volblocksize: coincide con la expectativa del guest/filesystem (a menudo 8K o 16K; a veces 4K).
- compression:
lz4salvo que la CPU esté ajustada. - Por qué: volblocksize es fijo; equivocarse es un impuesto permanente a menos que migres.
PostgreSQL / MySQL / bases de datos en datasets de sistema de archivos
- recordsize: alinea con el tamaño de página de la BD o un múltiplo que no cause churn (a menudo 8K o 16K).
- compression:
lz4suele estar bien; pruebazstdsolo si tienes CPU y buscas espacio. - Por qué: las bases de datos hacen sobrescrituras aleatorias pequeñas; registros grandes conducen a RMW y fragmentación.
Backups, archivos, medios, blobs tipo objeto
- recordsize: 512K o 1M puede tener sentido si las lecturas/escrituras son en streaming.
- compression:
zstd(nivel moderado) puede valer la pena, olz4si quieres CPU predecible. - Por qué: I/O secuencial recompensa bloques grandes; la ratio de compresión mejora con ventanas mayores.
Logs (append-heavy)
- recordsize: el valor por defecto suele estar bien; si los logs son grandes y mayormente se leen secuencialmente después, considera 256K.
- compression:
lz4casi siempre gana. - Por qué: los logs comprimen extremadamente bien; reducir escrituras físicas también reduce desgaste en SSDs.
Cargas de archivos pequeños (maildirs, árboles de código, cachés de paquetes)
- recordsize: no es la perilla principal; los archivos pequeños ya usan bloques pequeños.
- compression:
lz4ayuda y puede acelerar lecturas al reducir I/O de disco. - Considera: un vdev especial para metadatos/bloques pequeños si te tomas en serio la latencia.
Datos interesantes y contexto histórico
- ZFS se construyó para integridad de datos de extremo a extremo: checksums en cada bloque son nucleares, no un añadido.
- Los primeros valores por defecto de ZFS apuntaban a discos grandes y cargas mixtas: recordsize 128K se volvió un término medio pragmático para streaming y uso general.
- La compresión en ZFS ha sido “transparente” por mucho tiempo: las aplicaciones no necesitan saber; el sistema de archivos decide la representación en disco.
- LZ4 se volvió la opción por defecto porque es barato: fue diseñado para velocidad, así que a menudo mejora rendimiento al reducir I/O sin disparar CPU.
- Copy-on-write cambia el coste de las sobrescrituras: ZFS nunca sobrescribe en el lugar; por eso las cargas con muchas sobrescrituras son sensibles al dimensionamiento de bloques.
- La amplificación de escritura en RAIDZ es real: las escrituras aleatorias pequeñas pueden convertirse en grandes ciclos read-modify-write a nivel de stripe de paridad, lo que complica las decisiones de recordsize.
- ARC popularizó “RAM como almacenamiento”: ZFS cachea agresivamente, y la compresión efectivamente aumenta la capacidad de caché para datos compresibles.
- zvols se crearon para consumidores de bloque: pero su volblocksize fijo hace que los errores sean más difíciles de deshacer que recordsize en sistemas de archivos.
Tres microhistorias corporativas desde el campo
Incidente: la suposición equivocada (“recordsize no importa para bases de datos, ¿verdad?”)
Una empresa mediana ejecutaba una plataforma de analítica de clientes en ZFS. La base de datos principal vivía en un dataset clonado de un objetivo de backup.
Nadie se dio cuenta, porque montaba bien, el rendimiento parecía “aceptable” y las gráficas estaban tranquilas—hasta fin de mes.
Fin de mes implicaba actualizaciones pesadas, churn de índices y trabajos de mantenimiento. La latencia subió y luego se disparó. Los hilos de la aplicación se acumularon.
El equipo de almacenamiento miró la utilización de disco: no estaba saturado. Las gráficas de CPU en los hosts de BD: tampoco saturadas. Así que culparon a la red.
Afinaron buffers TCP como si fuera 2009.
El problema real fue aburrido: el dataset tenía recordsize=1M, heredado del perfil de backup.
La base de datos escribía páginas de 8K. Bajo churn, ZFS tuvo que hacer read-modify-write de registros grandes y churn de metadatos.
La pool no estaba “ocupada” en términos de ancho de banda; estaba ocupada haciendo el tipo de trabajo equivocado.
Lo arreglaron moviendo la base de datos a un nuevo dataset con recordsize=16K y manteniendo compression=lz4.
La migración fue la parte dolorosa. La recuperación de rendimiento fue inmediata y las gráficas dejaron de gritar.
Lección: “Se montó, por lo tanto está bien” es cómo terminas depurando física a las 2 a.m.
Optimización que salió mal: la fase de entusiasmo por zstd
Otra organización tenía un problema de coste de almacenamiento: pools SSD rápidos se llenaban con imágenes de VM y artefactos de build.
Alguien propuso compresión más fuerte. Activaron compression=zstd a un nivel agresivo en un dataset muy activo.
Los ahorros de espacio fueron grandes. El ticket se cerró con un suspiro satisfecho.
Dos semanas después, su sistema CI empezó a incumplir SLAs de build. No consistentemente—solo lo suficiente para ser enloquecedor.
El cluster no estaba sin CPU en general, pero un subconjunto de nodos mostró iowait elevado y más CPU de sistema en horas pico.
El array de almacenamiento no estaba saturado. La red estaba bien. Así que empezó la gira de culpas: actualizaciones de kernel, afinamiento del scheduler, “tal vez sea DNS”.
El culpable real: la CPU dedicada a comprimir y descomprimir artefactos calientes durante cargas bursty.
La compresión fuerte mejoró la capacidad pero aumentó la latencia en cola y amplificó la contención en los nodos de cómputo más ocupados.
El sistema no falló; simplemente se volvió molesto e impredecible—el peor tipo de regresión en producción.
Revirtieron a lz4 en el dataset caliente y dejaron zstd solo para archivos de artefactos fríos y de solo-escritura.
El resultado correcto no fue “nunca usar zstd.” Fue “úsalo donde el patrón de acceso encaje con el presupuesto de CPU.”
Práctica aburrida pero correcta que salvó el día: datasets por carga
Una firma de servicios financieros tenía una regla clara: cada carga obtiene su propio dataset, aunque parezca papeleo.
Bases de datos, logs, imágenes de VM, backups—datasets separados, propiedades explícitas y un README corto en el punto de montaje.
Los ingenieros nuevos se quejaban. Los mayores sonreían con educación y seguían haciéndolo.
Un día apareció una regresión de rendimiento después de una actualización de plataforma. Los arranques masivos de VM eran más lentos y un subconjunto de invitados tenía latencia de escritura alta.
Porque la organización tenía datasets separados, pudieron comparar propiedades rápidamente y ver qué cargas se veían afectadas.
El dataset de VM tenía un recordsize afinado para ese entorno y la compresión era consistente.
El problema resultó estar en otra parte (una ruta de escritura síncrona combinada con un dispositivo SLOG que se portaba mal),
pero aislar datasets evitó un intento desordenado de “afinamiento global” que habría roto sus bases de datos.
Arreglaron el problema del SLOG y todo lo demás permaneció estable porque nunca se tocó.
Lección: la corrección suele ser simplemente separación disciplinada. No es glamoroso. Es cómo evitas afinar una carga a costa de sabotear tres más.
Tareas prácticas: comandos, salidas, decisiones (12+)
Estos son movimientos operativos reales. Cada tarea incluye un comando, una salida de ejemplo, lo que significa y qué decisión tomar después.
Ajusta nombres de pool/dataset para que coincidan con tu entorno.
Task 1: Listar propiedades del dataset que importan (recordsize, compression)
cr0x@server:~$ zfs get -o name,property,value -s local,inherited recordsize,compression rpool/data
NAME PROPERTY VALUE
rpool/data recordsize 128K
rpool/data compression lz4
Significado: Ves las configuraciones efectivas. Si están heredadas, rastrea de dónde vienen.
Decisión: Si este dataset aloja una BD o imágenes de VM, 128K podría estar mal. No cambies todavía—mide primero.
Task 2: Comprobar si un dataset está realmente comprimiendo datos
cr0x@server:~$ zfs get -o name,property,value compressratio,logicalused,used rpool/data
NAME PROPERTY VALUE
rpool/data compressratio 1.85x
rpool/data logicalused 1.62T
rpool/data used 906G
Significado: La compresión está funcionando. Logical es lo que las apps escribieron; used es el espacio físico consumido.
Decisión: Si compressratio está cerca de 1.00x en datos calientes, la compresión podría ser CPU desperdiciada. Considera dejar lz4 de todos modos a menos que la CPU esté ajustada.
Task 3: Encontrar propiedades heredadas rápidamente (¿quién lo configuró?)
cr0x@server:~$ zfs get -o name,property,value,source recordsize,compression -r rpool
NAME PROPERTY VALUE SOURCE
rpool recordsize 128K default
rpool compression off default
rpool/data recordsize 128K inherited from rpool
rpool/data compression lz4 local
rpool/data/db recordsize 1M local
rpool/data/db compression lz4 inherited from rpool/data
Significado: rpool/data/db tiene un recordsize local de 1M. Eso es una señal de alarma para muchas bases de datos.
Decisión: Confirma el tipo de carga. Si es una BD con páginas de 8K, planifica migración a un dataset con tamaño correcto.
Task 4: Ver I/O en tiempo real por dataset para encontrar al vecino ruidoso
cr0x@server:~$ zpool iostat -v rpool 2
capacity operations bandwidth
pool alloc free read write read write
rpool 2.10T 1.40T 180 950 12.3M 88.1M
mirror 2.10T 1.40T 180 950 12.3M 88.1M
nvme0n1 - - 90 480 6.2M 44.0M
nvme1n1 - - 90 470 6.1M 44.1M
Significado: Muchas ops de escritura frente a ops de lectura modestas sugiere carga orientada a escritura. No es suficiente por sí solo—correlaciona con latencia.
Decisión: Si el ancho de banda es bajo pero las ops son altas y la latencia es mala, sospecha escrituras aleatorias pequeñas y/o problemas en la ruta síncrona.
Task 5: Comprobar salud del pool y errores antes de afinar rendimiento
cr0x@server:~$ zpool status -v rpool
pool: rpool
state: ONLINE
status: Some supported features are not enabled on the pool.
action: Enable all features using 'zpool upgrade'. Once this is done,
the pool may no longer be accessible by software that does not support the features.
scan: scrub repaired 0B in 00:12:41 with 0 errors on Tue Dec 24 03:12:02 2025
config:
NAME STATE READ WRITE CKSUM
rpool ONLINE 0 0 0
mirror ONLINE 0 0 0
nvme0n1 ONLINE 0 0 0
nvme1n1 ONLINE 0 0 0
errors: No known data errors
Significado: Sin errores, scrub limpio. Bien—los problemas de rendimiento probablemente sean por configuración o carga.
Decisión: Procede con diagnóstico de carga. No “afines” un pool que está fallando silenciosamente en el medio.
Task 6: Medir el coste de CPU de la compresión vía tiempo de sistema bajo carga
cr0x@server:~$ mpstat -P ALL 1 3
Linux 6.5.0 (server) 12/26/2025 _x86_64_ (16 CPU)
12:40:01 AM CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle
12:40:02 AM CPU all 18.1 0.0 9.7 2.4 0.0 0.3 0.0 0.0 0.0 69.5
12:40:03 AM all 20.2 0.0 13.5 1.9 0.0 0.2 0.0 0.0 0.0 64.2
12:40:04 AM all 19.6 0.0 14.1 1.8 0.0 0.2 0.0 0.0 0.0 64.3
Significado: %sys elevado sugiere trabajo del kernel (ZFS incluido). iowait bajo sugiere que los discos no son el cuello de botella.
Decisión: Si el rendimiento es pobre con iowait bajo, investiga rutas limitadas por CPU (nivel de compresión, checksumming, escrituras síncronas, contención).
Task 7: Observar efectividad de ARC y presión de memoria
cr0x@server:~$ arcstat 1 3
time read miss miss% dmis dm% pmis pm% mmis mm% arcsz c
12:41:10 820 110 13 28 3 72 9 10 1 28.5G 31.8G
12:41:11 790 95 12 22 3 63 8 10 1 28.6G 31.8G
12:41:12 815 120 15 35 4 74 9 11 1 28.6G 31.8G
Significado: La tasa de misses no es terrible. El tamaño de ARC está cerca del objetivo. La compresión puede hacer que ARC “guarde más datos lógicos”, pero los misses aún duelen.
Decisión: Si miss% es alto en una carga de lectura, la compresión puede ayudar (menos lecturas físicas), pero también podrías necesitar más RAM o mejor localidad.
Task 8: Comprobar por dataset escritos lógicos vs físicos (¿la compresión reduce la carga de escritura?)
cr0x@server:~$ zfs get -o name,property,value -r logicalused,used,compressratio rpool/data/vm
NAME PROPERTY VALUE
rpool/data/vm logicalused 800G
rpool/data/vm used 610G
rpool/data/vm compressratio 1.31x
Significado: Compresión moderada. Las imágenes de VM a menudo comprimen un poco, a veces mucho dependiendo del OS y bloques cero.
Decisión: Mantén lz4. Si la CPU está alta y compressratio está cerca de 1.00x, considera dejarla activada solo si no persigues latencia en cola.
Task 9: Confirmar los tamaños reales de bloque escritos (no solo recordsize)
cr0x@server:~$ zdb -bbbbb rpool/data/db | head -n 12
Dataset rpool/data/db [ZPL], ID 236, cr_txg 41292, 1.12G, 2948 objects
Indirect blocks:
0 L0 16K 1.23G 11% 1.00x 79.3M
Significado: Esto muestra la distribución real de bloques. Aquí ves predominio de bloques L0 de 16K, a pesar de que el recordsize pudiera ser mayor.
Decisión: Si esperabas bloques de 128K para una carga en streaming pero ves muchos de 8K/16K, la carga no es streaming (o está fragmentada). Afina acorde.
Task 10: Identificar si las escrituras síncronas te están matando
cr0x@server:~$ zfs get -o name,property,value sync,logbias rpool/data/db
NAME PROPERTY VALUE
rpool/data/db sync standard
rpool/data/db logbias latency
Significado: Sync está en standard (seguro). logbias latency prefiere uso de SLOG cuando está presente.
Decisión: Si tienes muchas fsync y no buen SLOG, espera dolor de rendimiento. No “arregles” poniendo sync=disabled a menos que disfrutes explicar pérdida de datos.
Task 11: Comprobar si existe un vdev especial (aceleración de metadatos/bloques pequeños)
cr0x@server:~$ zpool status rpool | sed -n '1,40p'
pool: rpool
state: ONLINE
config:
NAME STATE READ WRITE CKSUM
rpool ONLINE 0 0 0
mirror ONLINE 0 0 0
sda ONLINE 0 0 0
sdb ONLINE 0 0 0
special
mirror ONLINE 0 0 0
nvme0n1 ONLINE 0 0 0
nvme1n1 ONLINE 0 0 0
Significado: Hay un mirror special. Metadatos y opcionalmente bloques pequeños pueden aterrizar allí.
Decisión: Si latencia de archivos pequeños y metadatos es tu problema, special vdev puede ser importante. Pero también es un compromiso de fiabilidad—si lo pierdes puedes perder la pool.
Task 12: Verificar setting special_small_blocks (¿los datos pequeños van realmente al special?)
cr0x@server:~$ zfs get -o name,property,value special_small_blocks rpool/data
NAME PROPERTY VALUE
rpool/data special_small_blocks 0
Significado: Solo los metadatos van al special, no los bloques pequeños de datos.
Decisión: Si quieres bloques pequeños en special, fija special_small_blocks=16K o similar—después de pensar bien sobre capacidad y dominio de fallo.
Task 13: Probar un cambio de recordsize de forma segura en un dataset nuevo
cr0x@server:~$ zfs create -o recordsize=16K -o compression=lz4 rpool/data/db16k
cr0x@server:~$ zfs get -o name,property,value recordsize,compression rpool/data/db16k
NAME PROPERTY VALUE
rpool/data/db16k recordsize 16K
rpool/data/db16k compression lz4
Significado: Creaste un lugar para migrar/probar sin mutar el dataset existente en sitio.
Decisión: Migra una porción representativa de datos y haz benchmark. Evita las heroicidades de “cambiar en producción”.
Task 14: Mover datos con send/receive para preservar snapshots y reducir downtime
cr0x@server:~$ zfs snapshot rpool/data/db@pre-migrate
cr0x@server:~$ zfs send -R rpool/data/db@pre-migrate | zfs receive -u rpool/data/db16k
cr0x@server:~$ zfs list -t snapshot -o name,used -r rpool/data/db16k | head
NAME USED
rpool/data/db16k@pre-migrate 0B
Significado: El dataset y el snapshot existen en el destino. -u lo mantiene desmontado hasta que estés listo.
Decisión: Haz el corte intercambiando puntos de montaje o actualizando la configuración del servicio durante una ventana de mantenimiento.
Task 15: Validar que realmente reduces I/O físico después de activar compresión
cr0x@server:~$ zfs get -o name,property,value written,logicalused rpool/data/logs
NAME PROPERTY VALUE
rpool/data/logs written 145G
rpool/data/logs logicalused 312G
Significado: Logical used es mucho mayor que lo escrito físicamente, lo que sugiere que la compresión está reduciendo tráfico de escritura.
Decisión: Para logs append-heavy, mantén la compresión activada. Reduce desgaste en disco y a menudo mejora la velocidad de lectura en investigaciones.
Guion rápido de diagnóstico
Este es el flujo de trabajo para cuando “alguien está gritando en Slack”. El objetivo es identificar si estás limitado por IOPS, ancho de banda, CPU o por la ruta síncrona—rápido.
Primero: confirma que no es un pool fallo o una scrub/resilver en curso
- Ejecuta
zpool status. Si ves errores, vdevs degradados o un resilver en curso, para de afinar y empieza a arreglar hardware o terminar la recuperación. - Comprueba si hay un scrub en ejecución. Los scrubs pueden ser educados o agresivos según tunables y carga.
Segundo: decide si el cuello de botella es disco o CPU
- Señales de limitación por disco: iowait alto, utilización del dispositivo alta, ancho de banda cercano al límite del dispositivo, latencia que crece con el throughput.
- Señales de limitación por CPU: iowait bajo pero CPU de sistema alto, el throughput se estanca temprano, picos de latencia sin saturar discos.
Tercero: prueba si las escrituras síncronas son el verdadero villano
- Revisa la propiedad
syncdel dataset y si la carga usa fsync/O_DSYNC. - Si tienes un SLOG, valida que esté sano y rápido. Si no lo tienes, acepta que cargas pesadas en fsync estarán limitadas por la latencia del vdev principal.
Cuarto: comprueba recordsize/volblocksize frente al patrón de I/O
- Para BD/overwrite aleatorio, recordsize demasiado grande puede causar amplificación RMW.
- Para cargas en streaming, recordsize demasiado pequeño desperdicia IOPS y CPU en metadatos/checksums.
Quinto: verifica que la compresión esté ayudando, no solo “activada”
- Mira
compressratioy uso lógico vs físico. - Si la ratio es pobre y la CPU está alta, considera lz4 en lugar de algoritmos más pesados, o apaga la compresión para datos ya comprimidos.
Errores comunes: síntoma → causa raíz → arreglo
1) “Los discos están ociosos pero la latencia es alta”
- Síntoma: ancho de banda bajo, iowait bajo, pero picos de latencia en la aplicación.
- Causa raíz: ruta limitada por CPU (compresión demasiado agresiva, coste de checksum/cifrado) o contención en escrituras síncronas.
- Arreglo: degradar compresión a
lz4, asegurar holgura de CPU, validar SLOG para cargas síncronas y evitar registros sobredimensionados para patrones de sobrescritura.
2) “Las escrituras de la base de datos se volvieron más lentas tras aumentar recordsize para throughput”
- Síntoma: mayor latencia de escritura, picos impredecibles durante mantenimiento/vacuum/compactación.
- Causa raíz: amplificación RMW por sobrescribir páginas pequeñas dentro de registros grandes; la fragmentación empeora con el tiempo.
- Arreglo: migra a un dataset con recordsize alineado al tamaño de página de la BD (a menudo 8K/16K). Mantén
compression=lz4salvo que la CPU sea un problema.
3) “Compresión activada, pero la pool sigue llenándose y el rendimiento no mejoró”
- Síntoma:
compressratio~1.00x, sin reducción significativa de escrituras físicas. - Causa raíz: los datos ya están comprimidos/encriptados (media, zip, muchas imágenes de VM con sistemas de archivos cifrados).
- Arreglo: deja lz4 si la CPU es barata y quieres ganancias ocasionales; de lo contrario apaga compresión en ese dataset y deja de pagar la tasa de CPU por nada.
4) “Las lecturas secuenciales son más lentas de lo esperado en discos rápidos”
- Síntoma: throughput por debajo de la capacidad hardware durante escaneos/backups.
- Causa raíz: recordsize demasiado pequeño, causando IOPS excesivos y overhead; o datos fragmentados en bloques pequeños por patrón histórico de escritura.
- Arreglo: usa un dataset de registros grandes para datos de streaming (256K–1M). Para datos existentes, considera reescribir (send/recv a un nuevo dataset) para reblockear.
5) “Activar compresión fuerte ahorró espacio pero rompió SLAs”
- Síntoma: ganancias de espacio, pero latencia en cola y regresiones bajo carga pico.
- Causa raíz: contención de CPU por compresión/descompresión pesada en la ruta caliente.
- Arreglo: usa lz4 para datos calientes; reserva zstd para datasets fríos y menos sensibles a latencia; mide con concurrencia de producción.
6) “Cambiamos recordsize y no pasó nada”
- Síntoma: ninguna diferencia observable después de fijar recordsize.
- Causa raíz: recordsize afecta escrituras nuevas. Los bloques existentes mantienen su tamaño hasta que se reescriben.
- Arreglo: reescribe datos vía replicación/migración (send/recv) o reescritura a nivel de aplicación; valida con
zdbla distribución de bloques.
Broma #2: Si cambias recordsize en un dataset y esperas que los bloques antiguos se reconfiguren solos, felicidades—has inventado yoga de almacenamiento.
Listas de verificación / plan paso a paso
Paso a paso: elegir recordsize + compresión de forma segura
- Clasifica la carga: overwrite aleatorio (BD), lectura aleatoria (VM), secuencial (backup/media), mixto (home dirs).
- Encuentra la unidad de I/O: tamaño de página BD, tamaño de bloque VM, tamaño típico de objeto, tamaño de chunk de log.
- Elige un recordsize inicial:
- BD: 8K–16K (alinear páginas).
- Archivos VM: 16K–64K.
- Streaming: 256K–1M.
- Mixto: 128K.
- Activa compression lz4 salvo que tengas una razón concreta relacionada con CPU/latencia.
- Crea un dataset nuevo con esas propiedades; no mutes el antiguo si puedes evitarlo.
- Migra una muestra representativa y haz benchmark con concurrencia parecida a producción.
- Valida resultados:
compressratiomejoró?- ¿Mejoraron los percentiles de latencia?
- ¿La CPU mantiene holgura?
- Despliega gradualmente: migra servicio por servicio, no “toda la pool de una vez”.
Checklist: antes de tocar perillas en producción
- Pool sano (
zpool status). - Scrub reciente completado sin errores.
- Sabes si la carga es heavy en sync.
- Puedes revertir (snapshots + plan de migración probado).
- Tienes métricas base (percentiles de latencia, CPU, IOPS, ancho de banda).
- No estás mezclando cargas no relacionadas en un solo dataset con propiedades de “talla única que no sirve a nadie”.
Checklist: política de compresión por tipo de dato
- Textos/logs/configs: lz4 activado.
- Bases de datos: lz4 activado (normalmente), recordsize alineado.
- Medios (ya comprimidos): opcional; a menudo desactivado a menos que hayas medido ganancias.
- Datos cifrados en reposo dentro de archivos: la compresión no ayudará; no esperes milagros.
- Archivos fríos: considera zstd si el presupuesto de CPU está separado del presupuesto de latencia.
Una idea de fiabilidad (parafraseada) que vale la pena conservar
Idea parafraseada: planifica para el fallo, porque todo falla eventualmente—diseña para que los fallos sean sobrevivibles.
— Werner Vogels
Preguntas frecuentes
1) ¿Debo activar siempre compression=lz4?
Para la mayoría de los datasets, sí. A menudo reduce I/O físico y mejora la cache efectiva sin coste notable de CPU.
Excepciones: datos ya comprimidos/cifrados en sistemas con CPU escasa y presupuestos de latencia estrictos.
2) Si cambio recordsize, ¿reescribirá los bloques existentes?
No. Recordsize se aplica a escrituras nuevas. Los bloques existentes mantienen su tamaño hasta que se reescriben.
Para “reblockear” normalmente migras datos a un dataset nuevo (send/recv) o reescribes archivos en la capa de aplicación.
3) ¿Qué recordsize debo usar para PostgreSQL?
Puntos de partida comunes son 8K o 16K (las páginas de PostgreSQL suelen ser 8K). 16K puede estar bien si ves algo de comportamiento secuencial.
La respuesta correcta es: alinea con el comportamiento de las páginas y haz benchmark de tu carga, especialmente en tablas con muchas actualizaciones.
4) ¿Y MySQL/InnoDB?
InnoDB suele usar páginas de 16K por defecto. Un recordsize de 16K es un punto de partida sensato para datasets con muchas sobrescrituras.
Si haces escaneos secuenciales grandes y mayormente append, podrías tolerar más grande. Mide antes de ser creativo.
5) ¿Por qué recordsize grande perjudica escrituras aleatorias si ZFS puede almacenar bloques pequeños?
El problema no son las escrituras pequeñas iniciales—son las sobrescrituras parciales y el comportamiento copy-on-write que crean ciclos read-modify-write y fragmentación.
Los registros grandes aumentan la cantidad de datos reescritos por un cambio pequeño.
6) ¿Vale la pena zstd?
A veces. Puede ofrecer mejor compresión que lz4, especialmente en datos fríos o de solo-escritura.
En datasets calientes y sensibles a latencia, puede empeorar la situación por la contención de CPU y la latencia en cola.
7) ¿La compresión ayuda a IOPS o solo al ancho de banda?
Principalmente reduce ancho de banda (bytes). Pero reducir bytes puede indirectamente reducir presión de IOPS si las operaciones completan más rápido,
y mejora la densidad de caché, lo que puede reducir lecturas físicas (y por tanto IOPS) cuando los aciertos de ARC mejoran.
8) Recordsize vs volblocksize: ¿qué ajusto para almacenamiento de VM?
Si almacenas discos de VM como archivos en un dataset, ajusta recordsize. Si usas zvols, ajusta volblocksize al crear.
No los confundas; volblocksize es más difícil de cambiar después.
9) ¿Puedo fijar distintos valores de recordsize dentro del mismo dataset?
No por directorio usando propiedades ZFS estándar. Es por dataset. El patrón práctico es: crea múltiples datasets con propiedades diferentes
y móntalos donde la aplicación espere distinto comportamiento de I/O.
10) ¿Cómo sé si estoy limitado por CPU por la compresión?
Típicamente verás iowait bajo, CPU de sistema elevado y throughput que se estanca antes de que los discos se saturen.
Confirma comparando rendimiento con lz4 vs compresión más pesada en un dataset de prueba, usando concurrencia parecida a producción.
Conclusión: siguientes pasos que puedes hacer hoy
- Inventaría tus datasets: lista
recordsize,compressiony fuentes de herencia. Encuentra el caso de “perfil de backup ejecutando producción” accidental. - Elige una carga que sea sensible a latencia o costosa (base de datos, almacenamiento de VM, caché de build). Dale un dataset dedicado.
- Fija valores sensatos:
- Datos calientes generales:
compression=lz4,recordsize=128K. - Bases de datos:
compression=lz4,recordsize=8Ko16K. - Backups en streaming:
recordsize=512Ko1M, compresión según presupuesto de CPU.
- Datos calientes generales:
- Migra, no mutes: crea un dataset nuevo con las propiedades correctas y mueve datos usando send/recv. Así evitas regresiones sorpresa.
- Vuelve a medir: lógico vs físico, percentiles de latencia, holgura de CPU y la pregunta “¿los discos están realmente ocupados?”.
Recordsize y compresión no son “trucos de afinamiento”. Son decisiones de arquitectura expresadas como dos propiedades.
Tómalas deliberadamente, por carga, y tu presupuesto de hardware llegará más lejos—sin convertir tus CPUs en pasantes no remunerados.