Tu aplicación está “lenta”. El equipo de bases de datos jura que no cambió nada. La CPU está bien. Los gráficos de red son aburridos.
Y entonces alguien apunta al almacenamiento: “ZFS parece ocupado.” Felicidades, has entrado en la parte del incidente
donde la gente comienza a hacer declaraciones seguras basadas en impresiones.
zpool iostat es donde esas impresiones mueren. También es donde ingenieros perfectamente capaces quedan atrapados,
porque la salida parece simple y está llena de números que parecen confirmar la teoría que ya tenías.
Esto es cómo conviertes esos números en un cuello de botella que puedas nombrar, medir y arreglar.
Qué es ZFS iostat (y qué no es)
ZFS te ofrece múltiples lentes de “iostat”. La que la mayoría de la gente quiere decir es zpool iostat, que informa
por pool y por vdev el rendimiento y las tasas de I/O. Según los flags, también puede mostrar latencia, tamaño de cola y
desgloses por dispositivo. Esta es la visión del trabajo de ZFS, no la visión del dispositivo de bloque, y no necesariamente lo que
tu aplicación cree que solicitó.
Lo que no es: no es un perfilador de rendimiento completo, no te dirá “la base de datos no tiene un índice”, y no
separará mágicamente “carga de trabajo mala” de “diseño de almacenamiento malo”. Pero te dirá dónde el pool está pagando
tiempo: qué vdev, qué dispositivo, lecturas vs escrituras, ancho de banda vs IOPS vs latencia, y si estás limitado por
el disco más lento o por tus propias decisiones de configuración.
Una regla útil: si no puedes explicar un incidente en términos de unas pocas métricas estables (IOPS, ancho de banda, latencia),
no has terminado de depurar; solo has empezado a narrar.
Hechos interesantes y contexto histórico
- ZFS nació en Sun a principios de los 2000 para arreglar la complejidad de sistema de archivos y gestor de volúmenes uniéndolos en un solo sistema, con checksums de extremo a extremo.
- La idea de “pool de almacenamiento” fue radical en un mundo de particiones y hojas de cálculo de LUN; cambió cómo los equipos de operaciones pensaban sobre la gestión de capacidad.
- Los vdevs son la unidad de rendimiento: ZFS stripea a través de vdevs superiores, no a través de discos individuales como muchos suponen casualmente.
- La aritmética de paridad de RAIDZ es por qué las escrituras aleatorias pequeñas dañan más en RAIDZ que en mirrors; la “amplificación de escritura” no es marketing, es aritmética.
- OpenZFS divergió y convergió: múltiples plataformas continuaron ZFS tras Oracle, y características y herramientas evolucionaron de forma desigual entre sistemas operativos durante años.
- SLOG se hizo importante porque las escrituras síncronas son un contrato; ZIL existe para satisfacerlo, y iostat es una de las formas más rápidas de ver si ese contrato es caro.
- Los vdevs especiales fueron introducidos para manejar metadata/bloques pequeños en medios más rápidos; son oro para el rendimiento y deuda operacional.
- Las batallas de ashift ocurrieron porque los discos mentían (o se malinterpretaron) sobre tamaños físicos de sector; el ashift errado puede penalizar el rendimiento permanentemente.
- “L2ARC lo arregla todo” ha sido un mito recurrente desde que los primeros administradores descubrieron caching; no arregla la latencia de escritura y puede robar memoria que necesitabas en otro lado.
Guía de campo: leer zpool iostat con intención
Empieza con la salida por defecto, pero no te quedes ahí
La salida por defecto de zpool iostat es una introducción amable: operaciones de lectura/escritura y ancho de banda por pool y
opcionalmente por vdev. Es buena para “¿está ocupado?” y mala para “¿por qué está lento?” porque los cuellos de botella suelen
tratar sobre la distribución de latencia y un camino lento, no el rendimiento agregado.
Qué significan realmente las columnas
Los nombres de columnas varían ligeramente según la plataforma y la versión de OpenZFS, pero los conceptos son estables:
- r/s, w/s: operaciones de lectura/escritura por segundo tal como ZFS las ve en ese nivel (pool/vdev/dispositivo).
- rkB/s, wkB/s: ancho de banda de lectura/escritura por segundo.
- await (cuando está disponible): tiempo promedio que un I/O pasó esperando + tiempo de servicio (cola + dispositivo).
- r_await, w_await: misma idea, separada por lectura/escritura (muy útil cuando las escrituras síncronas muerden).
- sqd (a veces mostrado): profundidad de cola promedio. Cola alta con latencia creciente es una señal clásica de “dispositivo saturado”.
- util no suele ser una columna de ZFS como en
iostatde Linux; no importes mentalmente las semánticas de Linux. ZFS usa su propia instrumentación.
La frase que te ahorra horas
El rendimiento de ZFS está dominado por el vdev más lento involucrado en la operación, y zpool iostat es la forma más rápida de identificar cuál se está comportando como el integrante más lento del grupo.
Un modelo mental práctico: dónde se va el tiempo
Cuando una aplicación dice “escribe esto”, ZFS lo convierte en una danza: grupos de transacciones (TXGs), copy-on-write (COW),
checksumming, compresión opcional, asignación de metaslab, y luego I/O real al dispositivo. Las lecturas son otra historia:
aciertos en ARC, aciertos en L2ARC, prefetch, reconstrucción RAIDZ y finalmente discos.
zpool iostat no te mostrará el tiempo de commit de TXG directamente, pero sí te mostrará los síntomas:
ráfagas de escrituras, picos de latencia, un único vdev haciendo más trabajo que otros, o un dispositivo SLOG soportando todo el
calor de las escrituras síncronas.
Si necesitas un mapeo rápido de síntoma a “qué capa duele”, aquí va la versión directa:
- Alta w_await, bajo ancho de banda: escrituras pequeñas, escrituras síncronas, coste de paridad RAIDZ, o latencia del dispositivo.
- Alto rkB/s, r/s moderado, r_await en aumento: lecturas grandes saturando el ancho de banda.
- Alto r/s, bajo rkB/s, alto r_await: lecturas aleatorias, caché pobre o contención de metadata.
- Un vdev top-level al tope: asignación desigual, un vdev más pequeño/lleno, o un dispositivo realmente fallando.
Una cita que vale la pena tener en la pared:
La esperanza no es una estrategia.
— Jim Lovell
Tareas prácticas: comandos, salidas, decisiones (12+)
Estas son las cosas que realmente haces a las 02:17 cuando alguien dice “el almacenamiento está lento.” Cada tarea tiene: un comando,
lo que buscas y la decisión que soporta. Ejecuta en orden si eres nuevo; salta si ya tienes una corazonada y cicatrices de pager.
Tarea 1: Obtén una imagen en movimiento, no una instantánea
cr0x@server:~$ zpool iostat -v 1 5
capacity operations bandwidth
pool alloc free read write read write
---------- ----- ----- ----- ----- ----- -----
tank 3.12T 2.88T 420 310 42.1M 18.7M
mirror 1.56T 1.44T 210 155 21.0M 9.4M
sda - - 110 78 10.4M 4.9M
sdb - - 100 77 10.6M 4.5M
mirror 1.56T 1.44T 210 155 21.1M 9.3M
sdc - - 105 77 10.5M 4.6M
sdd - - 105 78 10.6M 4.7M
Significado: Estás obteniendo cinco muestras de 1 segundo con detalle por vdev y disco. Busca desequilibrio
(un disco o mirror haciendo mucho más), y observa la forma de la carga: gran ancho de banda vs muchas operaciones.
Decisión: Si un dispositivo está consistentemente por detrás de su par en un mirror, sospecha salud del dispositivo,
cableado, problemas de controlador o un problema de path. Si ambos lados coinciden pero la latencia del pool es mala, buscas
carga de trabajo o configuración.
Tarea 2: Activa la vista de latencia (si está soportada)
cr0x@server:~$ zpool iostat -v -l 1 5
capacity operations bandwidth total_wait
pool alloc free read write read write read write
-------------------------- ----- ----- ----- ----- ----- ----- ----- -----
tank 3.12T 2.88T 430 320 43.2M 19.1M 4ms 28ms
mirror 1.56T 1.44T 215 160 21.5M 9.6M 3ms 26ms
sda - - 112 81 10.7M 5.0M 3ms 25ms
sdb - - 103 79 10.8M 4.6M 3ms 28ms
mirror 1.56T 1.44T 215 160 21.7M 9.5M 4ms 31ms
sdc - - 109 80 10.8M 4.8M 4ms 32ms
sdd - - 106 80 10.9M 4.7M 4ms 30ms
Significado: “total_wait” (o similar) es tu señal rápida y burda de latencia.
Aquí las escrituras son mucho más lentas que las lecturas, y son lentas en todas partes, no solo en un disco.
Decisión: Si la espera de escritura es alta en todos los vdevs, pregunta: ¿escrituras síncronas? ¿paridad RAIDZ?
¿un SLOG lento? ¿un bloqueo de TXG? Si es solo un vdev/disco, trátalo como un problema de hardware/path hasta que se demuestre lo contrario.
Tarea 3: Delimita al pool culpable cuando tengas múltiples pools
cr0x@server:~$ zpool iostat -v tank 1 3
capacity operations bandwidth
pool alloc free read write read write
---------- ----- ----- ----- ----- ----- -----
tank 3.12T 2.88T 440 330 44.1M 19.8M
mirror 1.56T 1.44T 220 165 22.0M 9.9M
sda - - 112 83 10.8M 5.1M
sdb - - 108 82 11.2M 4.8M
mirror 1.56T 1.44T 220 165 22.1M 9.9M
sdc - - 110 82 11.0M 4.9M
sdd - - 110 83 11.1M 5.0M
Significado: Evita ruido entre pools. Especialmente en máquinas multi-tenant, tu “app lenta”
puede no ser la única que hace I/O.
Decisión: Si el pool ocupado no respalda la queja, no ajustas el almacenamiento;
ajustas expectativas e investigas contención de recursos en otro lugar (o facturación).
Tarea 4: Confirma la salud del pool antes de perseguir fantasmas de rendimiento
cr0x@server:~$ zpool status -v tank
pool: tank
state: ONLINE
scan: scrub repaired 0B in 06:41:12 with 0 errors on Mon Dec 23 03:12:01 2025
config:
NAME STATE READ WRITE CKSUM
tank ONLINE 0 0 0
mirror ONLINE 0 0 0
sda ONLINE 0 0 0
sdb ONLINE 0 0 0
mirror ONLINE 0 0 0
sdc ONLINE 0 0 0
sdd ONLINE 0 0 0
errors: No known data errors
Significado: Si ves errores READ/WRITE/CKSUM en aumento, los “problemas” de rendimiento pueden ser el sistema
reintentando, re-mapeando y sufriendo. ZFS mantendrá tus datos correctos y tu latencia picante.
Decisión: Cualquier contador de errores distinto de cero y con tendencia: deja de optimizar.
Empieza a reemplazar hardware, revisar cables/HBAs y revisar SMART.
Tarea 5: Comprueba si un scrub o resilver te está robando recursos
cr0x@server:~$ zpool status tank
pool: tank
state: ONLINE
scan: scrub in progress since Tue Dec 24 11:02:09 2025
1.43T scanned at 1.21G/s, 820G issued at 694M/s, 3.12T total
0B repaired, 26.18% done, 00:52:14 to go
config:
NAME STATE READ WRITE CKSUM
tank ONLINE 0 0 0
mirror ONLINE 0 0 0
sda ONLINE 0 0 0
sdb ONLINE 0 0 0
mirror ONLINE 0 0 0
sdc ONLINE 0 0 0
sdd ONLINE 0 0 0
errors: No known data errors
Significado: Los scrubs son consumidores legítimos de I/O. También son buena higiene.
Pero durante horas pico, pueden empujar la latencia ya ajustada por encima del límite.
Decisión: Si el sistema es sensible a la latencia, programa scrubs en horas fuera de pico y/o ajusta
la prioridad de scrub. Si no puedes tolerar scrubs, no puedes tolerar operar almacenamiento.
Tarea 6: Ve si estás limitado por IOPS o por ancho de banda
cr0x@server:~$ zpool iostat -v 2 3
capacity operations bandwidth
pool alloc free read write read write
---------- ----- ----- ----- ----- ----- -----
tank 3.12T 2.88T 4200 180 32.1M 6.2M
mirror 1.56T 1.44T 2100 90 16.0M 3.1M
sda - - 1050 45 8.0M 1.6M
sdb - - 1050 45 8.0M 1.5M
mirror 1.56T 1.44T 2100 90 16.1M 3.1M
sdc - - 1050 45 8.1M 1.6M
sdd - - 1050 45 8.0M 1.5M
Significado: 4200 lecturas/seg pero solo 32 MB/s: eso es I/O aleatorio de bloques pequeños. Si la latencia es alta,
añadir más platos (más vdevs) ayuda más que “discos más rápidos” aisladamente.
Decisión: Las cargas limitadas por IOPS quieren mirrors, más vdevs y tamaños de registro sensatos; las cargas limitadas por ancho de banda quieren disposición secuencial, menos penalidades por paridad y a menudo bloques más grandes.
Tarea 7: Valida si las escrituras síncronas son el villano
cr0x@server:~$ zfs get -o name,property,value,source sync tank/db
NAME PROPERTY VALUE SOURCE
tank/db sync standard local
Significado: sync=standard significa que las escrituras síncronas se honran cuando las aplicaciones las solicitan.
Las bases de datos suelen hacerlo.
Decisión: Si la latencia de escritura es terrible y ves muchas escrituras síncronas, tu SLOG (si existe) debe
ser rápido y con protección contra pérdida de energía. Si “arreglas” esto poniendo sync=disabled, al menos sé honesto y llama a eso
“elegir pérdida de datos como característica”.
Tarea 8: Comprueba si hay SLOG y si realmente se usa
cr0x@server:~$ zpool status tank
pool: tank
state: ONLINE
config:
NAME STATE READ WRITE CKSUM
tank ONLINE 0 0 0
mirror ONLINE 0 0 0
sda ONLINE 0 0 0
sdb ONLINE 0 0 0
mirror ONLINE 0 0 0
sdc ONLINE 0 0 0
sdd ONLINE 0 0 0
logs
nvme0n1 ONLINE 0 0 0
errors: No known data errors
Significado: Hay un dispositivo de log separado. Ese es el SLOG (separate intent log).
Solo acelera las escrituras síncronas, no las escrituras regulares.
Decisión: Si no tienes cargas pesadas en sync, un SLOG no ayudará y puede complicar dominios de fallo.
Si las tienes, el SLOG debe tener baja latencia y protección contra pérdida de energía.
Tarea 9: Observa el dispositivo de log bajo carga (¿se dispara?)
cr0x@server:~$ zpool iostat -v 1 5
capacity operations bandwidth
pool alloc free read write read write
---------- ----- ----- ----- ----- ----- -----
tank 3.12T 2.88T 120 1800 3.1M 22.4M
mirror 1.56T 1.44T 60 900 1.5M 11.1M
sda - - 30 450 760K 5.5M
sdb - - 30 450 740K 5.6M
mirror 1.56T 1.44T 60 900 1.6M 11.3M
sdc - - 30 450 780K 5.7M
sdd - - 30 450 820K 5.6M
logs
nvme0n1 - - 0 5200 0 41.6M
Significado: Grandes operaciones de escritura en nvme0n1 bajo “logs” sugieren fuertemente tráfico de escrituras síncronas
siendo comprometido en el SLOG. Si el pool tiene alta espera de escritura y el dispositivo de log muestra muchas ops, tienes una
carga de trabajo dominada por escrituras síncronas.
Decisión: Si las ops del dispositivo de log son altas y la latencia es alta, actualiza/reemplaza el SLOG, verifica PLP, y confirma que no estás
usando accidentalmente un NVMe de consumo barato que se vuelve inútil bajo escrituras síncronas sostenidas.
Tarea 10: Comprueba recordsize del dataset y desajuste con la carga
cr0x@server:~$ zfs get -o name,property,value,source recordsize tank/db
NAME PROPERTY VALUE SOURCE
tank/db recordsize 128K default
Significado: Muchas bases de datos prefieren bloques más pequeños (p. ej., 8K–16K) para evitar leer/escribir datos extra.
Un recordsize de 128K todavía puede funcionar, pero puede amplificar I/O en cargas aleatorias.
Decisión: Si tu iostat muestra altas IOPS con bajo ancho de banda (aleatorio pequeño), considera poner
recordsize más cercano al tamaño de página de la BD (después de confirmar la guía específica de la BD).
Tarea 11: Revisa la compresión (puede reducir I/O o costar CPU)
cr0x@server:~$ zfs get -o name,property,value,source compression tank/db
NAME PROPERTY VALUE SOURCE
tank/db compression lz4 local
Significado: lz4 suele ser una ganancia: menos datos escritos, menos leídos del disco, a menudo más rápido.
Pero en sistemas con CPU saturada o datos ya comprimidos, puede ser neutral o ligeramente peor.
Decisión: Si iostat muestra saturación de disco pero CPU libre, la compresión es tu amiga.
Si la CPU está al máximo y el disco está ocioso, no “optimices almacenamiento” con más compresión.
Tarea 12: Comprueba ashift (alineamiento) porque es para siempre
cr0x@server:~$ zdb -C tank | grep -E 'ashift|vdev_tree' -n | head
118: vdev_tree:
134: ashift: 12
Significado: ashift=12 implica sectores de 4K. Si construiste el pool con ashift=9
en discos de 4K, las escrituras pequeñas pueden convertirse en pesadillas de read-modify-write.
Decisión: Un ashift incorrecto es un error que requiere reconstrucción. Si está mal y el rendimiento importa, planifica una migración.
No hay un botón mágico que reescriba la geometría del pool de forma segura in situ.
Tarea 13: Identifica llenado desigual de vdevs (sesgo de asignación)
cr0x@server:~$ zpool list -v tank
NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH
tank 6.00T 3.12T 2.88T - - 19% 52% 1.00x ONLINE
mirror 3.00T 2.40T 600G - - 28% 80% - ONLINE
mirror 3.00T 720G 2.30T - - 8% 24% - ONLINE
Significado: Un mirror está al 80% y más fragmentado; el otro al 24%. ZFS intenta balancear,
pero la historia del pool, la expansión y el comportamiento de metaslab pueden causar sesgo. El sesgo puede significar que un vdev se convierta en hotspot.
Decisión: Si iostat muestra que el vdev más lleno hace más trabajo o muestra más latencia, la estrategia de reequilibrio importa:
considera añadir vdevs antes, mantén pools por debajo de umbrales de capacidad agresivos y evita planificar “expandir más tarde” como estrategia de rendimiento.
Tarea 14: Correlaciona ZFS iostat con estadísticas de dispositivo a nivel OS
cr0x@server:~$ iostat -x 1 3
Linux 6.6.0 (server) 12/25/2025 _x86_64_ (32 CPU)
Device r/s w/s rkB/s wkB/s await aqu-sz %util
sda 110.2 81.4 10960.0 5092.0 4.2 0.8 42.1
sdb 105.9 79.6 11010.0 4780.0 4.0 0.7 41.7
sdc 109.8 80.2 11030.0 4960.0 4.6 1.0 44.9
sdd 108.8 80.7 11080.0 5030.0 4.4 0.9 44.1
nvme0n1 0.0 5200.3 0.0 41600.0 0.3 0.6 62.0
Significado: ZFS ve trabajo lógico; el SO ve las colas y la utilización reales del dispositivo.
Si ZFS dice que un disco está ocupado pero el SO dice que está inactivo, puede que estés limitado por capas superiores (locks, CPU, TXG).
Si el SO muestra await alto y %util alto, el dispositivo realmente es el limitador.
Decisión: Usa iostat del SO para confirmar si el problema es “planificación de ZFS” vs “el dispositivo no puede seguir”.
Esto te evita reemplazar discos cuando necesitabas ajustar escrituras síncronas, o viceversa.
Tarea 15: Comprueba la presión de ARC y la memoria disponible (porque las lecturas engañan)
cr0x@server:~$ arcstat 1 3
time read miss miss% dmis dm% pmis pm% mmis mm% arcsz c
12:10:01 8420 1120 13 620 55 410 36 90 8 58G 64G
12:10:02 9010 1460 16 780 53 590 40 90 6 58G 64G
12:10:03 8770 1310 14 700 53 520 40 90 7 58G 64G
Significado: Una tasa de miss del 13–16% puede estar bien o ser horrible según la carga.
Si los misses se disparan y los discos se encienden en zpool iostat, tu “latencia de almacenamiento” puede ser en realidad “la caché funcionando como debe”.
Decisión: Si ARC está limitada y la tasa de misses es alta, añade RAM antes de comprar más discos. Si ARC es enorme
pero los misses siguen siendo altos, tu conjunto de trabajo es más grande que la memoria o estás haciendo lecturas en streaming que no cachean bien.
Chiste #1: Si miras iostat lo suficiente, los números no parpadean primero. Tú sí.
Guion rápido de diagnóstico
Cuando necesitas un cuello de botella rápido, haz esto en orden. El objetivo no es un entendimiento perfecto; es una corrección inicial correcta
y una línea de tiempo de incidente nítida.
Primero: ¿es esto un problema de salud que se hace pasar por problema de rendimiento?
- Ejecuta
zpool status -v. Busca errores, vdevs degradados, resilvers lentos o un dispositivo intermitente. - Ejecuta estadísticas de dispositivo a nivel SO (
iostat -x) para ver si un dispositivo está sufriendo.
Si ves errores o reinicios: detente. Reemplaza/arregla el path. Ajustar rendimiento en hardware enfermo es como tunear un coche de carreras mientras la rueda se cae.
Segundo: identifica el alcance y la forma del I/O
zpool iostat -v 1 5: ¿qué pool, qué vdev, qué dispositivo está más caliente?- Mira IOPS vs ancho de banda: muchas ops con poco MB/s gritan I/O aleatorio/pequeño.
- Si está disponible, agrega
-lpara pistas de espera/latencia: ¿las lecturas están bien pero las escrituras horribles?
Tercero: decide si el cuello de botella son escrituras síncronas, paridad RAIDZ o capacidad/fragmentación
- Revisa
zfs get syncen los dataset afectados. - Busca un SLOG en
zpool status, luego observa estadísticas del vdev de logs enzpool iostat -v. - Revisa
zpool list -vpor desequilibrio de llenado de vdevs y pistas de fragmentación.
Cuarto: valida con una métrica de comprobación cruzada
- Usa
iostat -xpara confirmar comportamiento real de colas/await del dispositivo. - Usa
arcstat(o equivalente de la plataforma) para descartar/confirmar que la caché es la razón por la que los discos están ocupados.
Al final de este guion deberías poder decir con la cara seria una de estas frases:
“Estamos saturados en IOPS de lectura aleatoria”, “Las escrituras síncronas están limitadas por el dispositivo de log”,
“Un vdev está sobrecargado”, “Estamos pagando impuestos de fragmentación/capacidad”, o “Un dispositivo/path está fallando y reintentando.”
Tres mini-historias corporativas desde el terreno
Incidente causado por una suposición errónea: “Es mirror, así que es rápido”
Una empresa SaaS mediana migró un clúster PostgreSQL de cara al cliente a un nuevo servidor de almacenamiento con respaldo ZFS.
El pool se construyó como un par de mirrors. Limpio, simple, tranquilizador. Todos dormían mejor.
Semanas después, la latencia empezó a subir en picos durante el tráfico alto. Los gráficos eran enloquecedores: la CPU no estaba al máximo, la red estaba bien.
El on-call ejecutó zpool iostat -v y vio que un mirror mostraba una espera de escritura notablemente mayor que el otro.
La suposición fue inmediata: “ZFS debería balancear entre mirrors; un mirror no puede ser el cuello de botella, es redundante.”
Empezaron a buscar en otra parte.
El problema real fue llenado desigual de vdevs causado por un flujo de trabajo de expansión anterior. Un mirror estaba significativamente más lleno y más
fragmentado. Ese mirror se convirtió en el hotspot de asignación, así que hizo más trabajo, se volvió más lento y el pool pareció más lento.
La salida de zpool list -v había intentado confesarlo todo desde el principio.
La solución no fue exótica: re-evaluaron umbrales de capacidad, cambiaron el plan de expansión para añadir vdevs completos antes, y migraron
datos a un pool recién construido con tamaños de vdev consistentes. El mayor cambio fue cultural: “mirrors” no son “una unidad”.
En ZFS, los vdevs son las unidades que importan.
Una optimización que salió mal: “Deshabilitar sync y listo”
Un equipo de plataforma de datos tenía una canalización de ingestión que escribía muchas transacciones pequeñas. Durante una prueba de carga, la latencia de escritura parecía mala.
Alguien descubrió zfs set sync=disabled y ejecutó un benchmark rápido. La latencia bajó. El throughput subió.
La petición de cambio se escribió sola.
En producción, las primeras semanas fueron geniales. Luego un evento de energía afectó un rack. Los servidores se reiniciaron. La canalización se reinició limpia.
Y apareció una inconsistencia silenciosa en los datos horas después: trabajos downstream vieron duplicados y registros faltantes. El equipo pasó días
“debuggeando Kafka”, luego “debuggeando la base de datos”, y finalmente lo rastrearon hasta escrituras que habían sido reconocidas pero no llegaron de forma segura al disco.
La revisión post-mortem fue incómoda porque nadie había “hecho lo incorrecto” en su marco local.
El benchmark había sido real. La mejora había sido real. La carga había sido real.
El error fue tratar un contrato de durabilidad como una preferencia ajustable.
La solución no fue “nunca tocar sync”. Fue “construir la ruta de escritura correcta.” Añadieron un dispositivo SLOG apropiado con protección contra pérdida de energía,
lo validaron bajo carga vía estadísticas de logs en zpool iostat, y dejaron sync=standard en su lugar.
El rendimiento mejoró, y también el sueño de todos.
La práctica aburrida pero correcta que salvó el día: muestreo base
Un equipo de plataforma interno ejecutaba ZFS en una flota de hosts de virtualización. Tenían una política que a nadie emocionaba:
capturar un muestreo de 60 segundos de zpool iostat -v -l 1 durante horas de negocio “normales” cada semana y guardarlo con metadatos del host.
Sin paneles, sin bombo—solo archivos de texto con marcas de tiempo. De ese tipo que borras por accidente en la limpieza de primavera.
Una tarde, llegaron quejas de latencia de VM. Los hosts parecían “bien” a primera vista. El pool no estaba lleno.
No había errores. Aun así la UI del hipervisor mostraba parones periódicos.
El on-call comparó el muestreo actual de iostat con la línea base y vio la diferencia inmediatamente: la espera de escritura se había duplicado
al mismo nivel de IOPS. No era “más carga”. Era “misma carga, tiempo de servicio más lento.”
Eso acotó la búsqueda a “algo en la ruta de I/O cambió”, no “alguien añadió inquilinos”.
El culpable fue una actualización de firmware en un HBA que cambió el comportamiento de las colas bajo cargas mixtas de lectura/escritura.
Revirtieron el firmware en hosts afectados, confirmaron que la latencia volvió a la línea base, y luego trabajaron con el proveedor
en un build corregido. Las líneas base aburridas los salvaron de una semana de persecuciones fantasmas y lanzarse la culpa.
Chiste #2: La latencia de almacenamiento es como las aprobaciones corporativas—nadie lo nota hasta que de pronto es el problema de todos.
Errores comunes: síntoma → causa raíz → solución
1) “Las IOPS del pool se ven altas, por lo tanto los discos son lentos”
- Síntoma: r/s o w/s altos en
zpool iostat; aumenta la latencia de la app. - Causa raíz: La carga cambió a I/O más pequeño o más aleatorio; el ancho de banda agregado puede ser modesto.
- Solución: Verifica el tamaño de I/O (IOPS vs MB/s), ajusta
recordsizedel dataset, añade vdevs (más platos/mirrors), o mueve datos calientes a SSD/vdev especial.
2) “Un disco en un mirror hace menos, así que está bien”
- Síntoma: En un mirror, un disco muestra menos ops y/o mayor espera.
- Causa raíz: El dispositivo es lento, reintenta por errores, tiene problema de path, o está siendo limitado por el controlador.
- Solución: Revisa contadores en
zpool status, revisa logs del SO, SMART, vuelve a asentar/revisa cables, mueve el disco a otro puerto/HBA, reemplaza el dispositivo si hace falta.
3) “Añadimos un SLOG; las escrituras siguen lentas; el SLOG es inútil”
- Síntoma: SLOG presente; la latencia de escritura permanece alta.
- Causa raíz: La carga no es heavy en sync, o el SLOG es lento/de consumo, o sync está deshabilitado/habilitado inesperadamente.
- Solución: Confirma comportamiento de sync (
zfs get sync), observa actividad del vdev “logs” enzpool iostat -v, usa un dispositivo PLP de baja latencia, y no esperes que SLOG acelere escrituras async.
4) “RAIDZ debería estar bien; es como RAID hardware”
- Síntoma: Escrituras aleatorias pequeñas con alta espera; ancho de banda bajo; CPU está bien.
- Causa raíz: Sobrecoste de paridad RAIDZ y comportamiento read-modify-write en actualizaciones pequeñas.
- Solución: Usa mirrors para cargas sensibles a IOPS, o cambia tamaños de bloque/carga, añade más vdevs, o aísla cargas de escritura aleatoria a pools mirror/SSD.
5) “La latencia es alta, así que el pool está sobrecargado”
- Síntoma: total_wait alto; la app se detiene; pero %util del dispositivo no es alto.
- Causa raíz: El cuello de botella puede estar por encima de los discos: sincronización TXG, contención de CPU, presión de memoria o contención de locks.
- Solución: Corrobora con
iostat -x, observa comportamiento del ARC, revisa steal/interrupts de CPU, y busca bloqueos relacionados con TXG en logs del sistema y tunables de ZFS (específicos de plataforma).
6) “El rendimiento empeoró después de habilitar vdev especial; ZFS está roto”
- Síntoma: Las lecturas se vuelven en ráfagas; operaciones intensivas en metadata se detienen.
- Causa raíz: El vdev especial es de tamaño insuficiente, está saturado o en un dispositivo con mala consistencia de latencia; además, la concentración de metadata aumenta la dependencia en ese dispositivo.
- Solución: Asegura que el vdev especial tenga capacidad y redundancia suficientes, móntalo como una capa crítica y no uses un único dispositivo barato como línea de vida de metadata.
7) “Solo estamos al 80% de uso; la capacidad no puede afectar rendimiento”
- Síntoma: La latencia sube gradualmente a medida que el pool se llena; la asignación se vuelve desigual; aumenta la fragmentación.
- Causa raíz: El espacio libre se vuelve más difícil de asignar eficientemente; la fragmentación de metaslab crece; algunos vdevs se calientan.
- Solución: Mantén los pools cómodamente por debajo de marcas altas para tu carga, añade vdevs antes y evita expansiones de último minuto durante uso pico.
Listas de verificación / plan paso a paso
Paso a paso: de “lento” a “cuello de botella” en 15 minutos
-
Confirma el pool y dataset implicados.
Revisa mountpoints/volúmenes y mapea a pools. No diagnostiques la máquina equivocada. -
Revisa la salud primero.
Ejecutazpool status -v. ¿Errores, dispositivos degradados, resilvers o scrubs? Trátalo como prioridad. -
Captura una corrida corta de iostat.
Ejecutazpool iostat -v 1 10(y-lsi está disponible). Guárdalo. Incidentes sin artefactos se convierten en discusiones. -
Clasifica la forma de la carga.
IOPS-heavy (ops altas, MB/s bajos) vs bandwidth-heavy (ops moderadas, MB/s altas). Decide qué tipo de hardware ayuda. -
Identifica el vdev caliente.
Si un vdev está más caliente, pregunta “por qué”: sesgo de llenado, distinto tipo de disco, distinto path, errores o saturación del dispositivo especial/log. -
Revisa el comportamiento sync.
Ejecutazfs get syncen el/los dataset afectados. Si es sync-heavy, confirma SLOG y su comportamiento en iostat. -
Corrobora en la capa OS.
Ejecutaiostat -x 1 5. Confirma await/cola a nivel dispositivo. Si los dispositivos están ociosos, deja de culpar discos. -
Decide la primera mitigación segura.
Ejemplos: pausar/retimar scrub, mover carga, limitar tasa de un job por lotes, añadir dispositivo de log, añadir vdevs o migrar datasets.
Checklist: qué capturar para un postmortem
- Al menos 60 segundos de muestras de
zpool iostat -v, más vista de latencia si está disponible. - Salida de
zpool status -ven el momento del incidente. - Estadísticas de dispositivo del SO (
iostat -x) para correlación. - Propiedades del dataset:
sync,recordsize,compression,atime, y cualquier configuración de vdev especial. - Si había un scrub/resilver activo y su tasa.
- Una declaración clara: IOPS-bound, bandwidth-bound, sync-bound o single-vdev-bound.
Preguntas frecuentes
1) ¿Cuál es la diferencia entre zpool iostat y iostat de Linux?
zpool iostat informa la vista lógica de ZFS por pool/vdev, incluyendo dispositivos virtuales como logs y vdevs especiales.
iostat de Linux informa el comportamiento de dispositivos de bloque (cola, await, utilización). Usa ambos: ZFS para encontrar el vdev caliente,
iostat del SO para confirmar saturación del dispositivo o problemas de path.
2) ¿Por qué un vdev se convierte en cuello de botella si ZFS “stripea” datos?
ZFS stripea a través de vdevs de nivel superior. Si un vdev está más lleno, más lento, se comporta mal o simplemente tiene menos discos, puede convertirse
en el factor limitante. La velocidad agregada del pool se arrastra hacia el vdev más lento involucrado en la carga.
3) ¿Un SLOG acelera todas las escrituras?
No. Un SLOG acelera las escrituras síncronas (escrituras que deben confirmarse de forma segura antes de devolver). Las escrituras asíncronas se bufferizan y se comprometen con TXGs; SLOG no cambia mucho ese camino.
4) ¿Por qué mis escrituras son más lentas que las lecturas en iostat?
Causas comunes: escrituras síncronas sin un buen SLOG, sobrecosto de paridad RAIDZ para escrituras pequeñas, un pool casi lleno/fragmentado,
o un dispositivo con latencia de escritura inconsistente. Si las lecturas están bien y las escrituras son malas, revisa sync y comportamiento del vdev de logs.
5) ¿Altas IOPS siempre es malo?
Altas IOPS es solo una característica de la carga. Se vuelve malo cuando la latencia sube o el sistema no cumple SLOs.
La pregunta correcta es: ¿estamos limitados por IOPS (aleatorio pequeño) o por ancho de banda (secuencial grande)?
6) ¿Cuántas muestras debo tomar con zpool iostat?
Para triaje, 5–10 segundos a intervalos de 1 segundo está bien. Para diagnosticar jitter, toma al menos 60 segundos y captura lo mismo durante horas “normales” para comparar. Los problemas de almacenamiento adoran esconderse en la varianza.
7) ¿La fragmentación puede explicar picos de latencia?
Puede contribuir, especialmente a medida que los pools se llenan. La fragmentación tiende a aumentar el costo promedio de I/O y la varianza.
Si ves tiempos de espera en aumento con IOPS similares y el pool se está llenando, la fragmentación/impuesto de capacidad es sospechoso real.
8) ¿Cuál es el “arreglo rápido” más seguro durante un incidente?
Lo más seguro es reducir I/O competidor: pausar/reprogramar un scrub, limitar la tasa de jobs por lotes, o mover un tenant ruidoso.
Cambiar ajustes de sync o reconstruir la geometría del pool no es una solución de tiempo de incidente a menos que estés preparado para asumir el riesgo.
9) ¿Por qué zpool iostat muestra bajo ancho de banda pero la app dice que escribe mucho?
Las aplicaciones “escriben” a nivel lógico; ZFS puede comprimir, coalescer, diferir o satisfacer escrituras vía caching y batching de TXG.
El ancho de banda a nivel de dispositivo es lo que realmente llega al disco. Si tu app escribe bloques aleatorios pequeños, el ancho de banda puede parecer pequeño incluso cuando el sistema sufre.
10) ¿Debo confiar en números de latencia promedio?
Confía en ellos como pista, no como veredicto. Los promedios ocultan la latencia de cola, y la latencia de cola es donde viven los usuarios.
Usa muestras a lo largo del tiempo, correlaciona con fases de carga y confirma con métricas de cola a nivel del SO.
Próximos pasos que puedes hacer hoy
Si operas ZFS en producción, haz estas tres cosas antes de que el próximo incidente te las haga a ti:
-
Crea una línea base. Captura semanalmente
zpool iostat -v(y vista de latencia si está disponible) para cada host importante.
Guárdalo en algún lugar aburrido y duradero. -
Documenta tu contrato de escritura previsto. Para cada dataset crítico: ¿se requiere sync? Si sí, ¿cuál es el SLOG y cómo lo validas bajo carga?
Si no, ¿por qué no—y quién firmó ese riesgo? -
Deja de tratar “pool lleno” solo como evento de capacidad. Decide tu línea de agua de rendimiento (a menudo muy por debajo del 90%),
y planifica añadir vdevs mientras el pool aún está cómodo.
Una vez que puedas nombrar el cuello de botella con evidencia—IOPS-bound, bandwidth-bound, sync-bound, o un vdev individual arrastrando el pool—
las soluciones se vuelven directas. No siempre baratas. Pero directas. Ese es el trato.