Tu pool ZFS “rinde bien” hasta que llega el día en que necesitas transmitir unos terabytes—reproducción de medios, copias de seguridad, escaneos analíticos, rehidratación de object-store—y el rendimiento
se desploma en un triste patrón de sierra. La CPU parece aburrida. Los discos parecen ocupados. La red culpa al almacenamiento, el almacenamiento culpa a la red, y alguien sugiere añadir
más RAM como si fuera agua bendita.
Aquí es donde las lecturas secuenciales en ZFS se ponen interesantes: ZFS puede ser brutalmente rápido en streaming, pero también es un sistema de archivos con opiniones firmes, múltiples cachés,
sumas de verificación por todas partes y un modelo de transacciones que de vez en cuando convierte lo “simple” en “complicado con estilo”. Afinémoslo como si realmente gestionáramos sistemas de producción.
Qué significa “lecturas secuenciales” en ZFS (y qué no)
“Lectura secuencial” es una descripción de carga de trabajo, no una promesa. En discos giratorios, lecturas secuenciales significan largas carreras contiguas en disco, mínimas búsquedas y E/S grandes.
En SSDs, principalmente significa E/S grandes y alta profundidad de cola sin que lecturas aleatorias pequeñas ensucien la tubería.
En ZFS, las lecturas secuenciales están moldeadas por tres capas de realidad:
- Secuencialidad lógica: tu aplicación lee offsets de archivo en orden.
- Distribución en disco: los bloques pueden no ser físicamente contiguos debido a copy-on-write y fragmentación del espacio libre.
- Comportamiento de E/S de ZFS: recordsize, prefetch, verificación de sumas, compresión y cachés deciden cuántas y qué tipo de E/S realmente emites.
Puedes tener una lectura de aplicación perfectamente secuencial que aun así se vuelve “semi-aleatoria” en disco porque el archivo se reescribió durante meses,
snapshots fijaron bloques antiguos y el espacio libre se convirtió en confeti. Eso no es que ZFS sea malo; es que ZFS es honesto con la física.
Tu objetivo para el máximo rendimiento de streaming es hacer que ZFS emita lecturas grandes, mantener la cola lo bastante profunda para saturar los dispositivos,
evitar el churn innecesario de caché y asegurarte de que la CPU no sea el cuello de botella oculto. Los pools más rápidos no son los que tienen más ajustes; son los que tienen menos sorpresas.
Hechos e historia que importan en la práctica
- ZFS nació en Sun Microsystems a mediados de los 2000 con sumas de verificación end-to-end como característica central. Esa verificación no es un “sobrecoste”; es el diseño.
- La propuesta original de ZFS incluía “no fsck”—una gran ventaja operativa. Para lecturas en streaming también significa que la integridad de metadatos se protege constantemente, no ocasionalmente.
- Copy-on-write es por qué los snapshots son baratos—y también por qué los datasets de larga vida pueden fragmentarse. El rendimiento de streaming suele degradarse con el tiempo si reescribes archivos grandes en su lugar.
- “Bloques de 128K” se volvieron culturalmente pegajosos porque recordsize=128K funciona bien para muchas cargas secuenciales. No es mágico; es solo un buen valor por defecto.
- L2ARC se introdujo para extender la caché a SSD cuando la RAM era cara. Ayuda más en lecturas aleatorias que en streaming sostenido porque las lecturas secuenciales tienden a ser “de una sola pasada”.
- zfs send/receive remodeló flujos de trabajo de backup al hacer la replicación consciente de bloques. También es un generador de cargas de lectura secuencial que puede exponer límites de ancho de banda del pool rápidamente.
- OpenZFS unificó múltiples forks así que la guía de afinación ya no es “folclore solo Solaris”, pero los valores por defecto aún difieren entre plataformas y versiones.
- ZFS moderno tiene clases de asignación especiales (special vdev) para separar metadata/bloques pequeños. Eso puede aumentar indirectamente el streaming al reducir la contención de metadatos.
- Los algoritmos de checksum evolucionaron (fletcher4, sha256, etc.). Hashes más fuertes cuestan CPU; hashes rápidos cuestan menos. El streaming puede volverse limitado por CPU antes de que te des cuenta.
La ruta de datos de lectura en streaming: dónde muere el rendimiento
Si quieres máximo rendimiento, necesitas saber qué etapa te limita. Una lectura secuencial en ZFS típicamente fluye así:
- La aplicación emite lecturas (a menudo 4K–1MB dependiendo de la app/librería).
- DMU mapea offsets de archivo a bloques según recordsize y tamaños reales de bloque en disco.
- Prefetch decide si leer por adelantado especulativamente.
- Búsqueda en ARC hit o miss; los misses programan E/S a disco.
- El scheduler de vdev convierte lecturas lógicas en lecturas físicas a través de mirrors/RAIDZ, respetando límites de cola.
- El dispositivo completa las lecturas; los datos se verifican con checksum, se descomprimen si hace falta, luego se colocan en ARC y se copian a espacio de usuario.
El rendimiento de streaming suele morir en uno de cuatro lugares:
- Ancho de banda del dispositivo (simplemente no tienes suficientes discos, lanes o ancho de lectura/escritura en SSDs).
- Tamaño de E/S demasiado pequeño (mucho overhead por byte; “ganas” en IOPS y pierdes MB/s).
- CPU limitada por sumas/descompresión (especialmente con pools NVMe rápidos).
- Fragmentación y geometría RAIDZ (más operaciones de E/S de las esperadas, coalescencia de lectura rota o sobrecoste por paridad).
Broma #1: Si tus “lecturas secuenciales” parecen aleatorias, felicitaciones—has inventado una nueva categoría de benchmark: “almacenamiento interpretativo.”
Guion de diagnóstico rápido (primero/segundo/tercero)
Primero: prueba qué está saturado
- Revisa el throughput y latencia por vdev con
zpool iostat -v. Si un vdev está saturado mientras otros están inactivos, tienes problemas de diseño o encolamiento. - Revisa la CPU (user/system, softirqs) durante el stream. Si la CPU sube con un tope en el throughput, las sumas/compression o el manejo de IRQs pueden estar limitando.
- Revisa la red si esto es remoto. Un “problema de almacenamiento” que se queda en exactamente 9.4 Gbps suele ser un problema de red con disfraz de almacenamiento.
Segundo: valida tamaño de E/S y comportamiento de prefetch
- Mira el recordsize del dataset y si los archivos fueron escritos con él. Los archivos antiguos mantienen sus tamaños de bloque antiguos.
- Observa los tamaños de lectura reales vía
zpool iostatyfio. Si ves muchas lecturas de 4K–16K, estás pagando impuesto por overhead. - Confirma que prefetch no está deshabilitado (parámetros del módulo) y que no lo está derrotando el patrón de accesos (p.ej., muchos lectores con accesos entrelazados).
Tercero: inspecciona salud del pool y contención “en segundo plano”
- Scrub/resilver en progreso puede reducir drásticamente el ancho de banda de streaming. Igual para eliminación masiva de snapshots (liberación intensa de espacio).
- Errores o dispositivos lentos causan reintentos e inflan la latencia; los mirrors suelen elegir el lado “más rápido”, pero si ambos fallan lo verás.
- Espacio y fragmentación: un pool al 85–90% de ocupación rara vez es campeón de streaming. ZFS necesita espacio para maniobrar.
Tareas prácticas: comandos, salidas, decisiones
Estas son las tareas que realmente ejecuto cuando alguien dice “las lecturas secuenciales de ZFS son lentas”. Cada una incluye: el comando, qué significa la salida y qué decides a continuación.
Supuestos: Linux + herramientas OpenZFS; adapta rutas y nombres de pool.
Task 1: Identificar topología del pool y nivel RAID (no puedes afinar lo que no entiendes)
cr0x@server:~$ sudo zpool status -v tank
pool: tank
state: ONLINE
scan: scrub repaired 0B in 02:11:33 with 0 errors on Wed Dec 18 03:21:39 2025
config:
NAME STATE READ WRITE CKSUM
tank ONLINE 0 0 0
raidz2-0 ONLINE 0 0 0
sda ONLINE 0 0 0
sdb ONLINE 0 0 0
sdc ONLINE 0 0 0
sdd ONLINE 0 0 0
sde ONLINE 0 0 0
sdf ONLINE 0 0 0
errors: No known data errors
Meaning: Estás en un único vdev RAIDZ2 de seis discos. Las lecturas en streaming estarán limitadas por el ancho de banda agregado de ese vdev y por el comportamiento de RAIDZ.
Un vdev grande es una única “carril” desde la perspectiva de ZFS.
Decision: Si necesitas más throughput, añade vdevs (más carriles) o cambia a mirrors para mejor paralelismo. La afinación no creará ancho de banda de la nada.
Task 2: Ver throughput real, IOPS y latencia por vdev y disco
cr0x@server:~$ sudo zpool iostat -v tank 1 5
capacity operations bandwidth
pool alloc free read write read write
------------------------------------------ ----- ----- ----- ----- ----- -----
tank 8.11T 2.77T 480 5 820M 1.20M
raidz2-0 8.11T 2.77T 480 5 820M 1.20M
sda - - 85 1 135M 256K
sdb - - 76 1 130M 256K
sdc - - 79 1 138M 256K
sdd - - 80 1 140M 256K
sde - - 78 1 139M 256K
sdf - - 82 0 138M 0K
------------------------------------------ ----- ----- ----- ----- ----- -----
Meaning: Estás obteniendo ~820 MB/s de lecturas, distribuido de forma uniforme. Eso es plausible cercano al techo secuencial de estos discos.
Si esperabas 2–3 GB/s, la topología explica la diferencia.
Decision: Si el pool se ve equilibrado y el ancho de banda es “razonable”, deja de culpar a ZFS y empieza a planificar capacidad. Si un disco va más lento, investígalo.
Task 3: Confirmar ajustes del dataset que afectan lecturas en streaming
cr0x@server:~$ sudo zfs get -o name,property,value -s local,received recordsize,compression,atime,primarycache,secondarycache tank/media
NAME PROPERTY VALUE
tank/media recordsize 1M
tank/media compression lz4
tank/media atime off
tank/media primarycache all
tank/media secondarycache all
Meaning: recordsize es 1M (bueno para archivos grandes de streaming), compression es lz4 (normalmente bueno), atime está off (bien; evita tráfico de escritura al leer).
Decision: Si recordsize es 128K pero transmites archivos enormes, considera 1M para ese dataset antes de ingresar datos. Si ya tienes datos, necesitarás reescribir/replicar para beneficiarte.
Task 4: Verificar qué tamaños de bloque tienen realmente los archivos (no lo que desearías)
cr0x@server:~$ sudo zdb -ddddd tank/media | head -n 20
Dataset tank/media [ZPL], ID 52, cr_txg 11423, 2.31T, 98123 objects
Object lvl iblk dblk dsize dnsize lsize %full type
17 1 128K 1M 1.00M 512B 4.00M 100.00 ZFS plain file
18 1 128K 128K 128.0K 512B 512.0K 100.00 ZFS plain file
Meaning: Algunos archivos usan bloques de 1M, otros de 128K. recordsize es un límite superior; los datos existentes pueden ser más pequeños según cómo se escribieron.
Decision: Si tus objetos clave de streaming no usan bloques grandes, planifica una reescritura: zfs send|recv, rsync con reflinks desactivados o reingest a nivel de aplicación.
Task 5: Medir ancho de banda secuencial bruto a nivel de archivo (evita “la app es rara”)
cr0x@server:~$ fio --name=seqread --filename=/tank/media/bigfile.bin --rw=read --bs=1M --iodepth=32 --direct=1 --numjobs=1 --time_based --runtime=30
seqread: (g=0): rw=read, bs=(R) 1024KiB-1024KiB, (W) 1024KiB-1024KiB, (T) 1024KiB-1024KiB, ioengine=libaio, iodepth=32
fio-3.36
Starting 1 process
seqread: Laying out IO file (1 file / 0MiB)
Jobs: 1 (f=1): [R(1)][100.0%][r=1050MiB/s][r=1050 IOPS][eta 00m:00s]
seqread: (groupid=0, jobs=1): err= 0: pid=21944: Thu Dec 26 11:08:14 2025
read: IOPS=1048, BW=1048MiB/s (1099MB/s)(30.7GiB/30005msec)
clat (usec): min=310, max=2200, avg=720.12, stdev=120.44
Meaning: Con I/O directo, bloques de 1M y iodepth razonable, obtienes ~1.0 GiB/s. Eso concuerda con el throughput observado en el vdev.
Decision: Si fio es rápido pero la aplicación es lenta, el cuello de botella es el comportamiento de la app (lecturas pequeñas, mono-hilo, puntos sync). Si fio es lento, sigue investigando en almacenamiento.
Task 6: Verificar tamaño de ARC y ratios de acierto (el streaming suele ser miss-heavy, y eso está bien)
cr0x@server:~$ arcstat 1 3
time read miss miss% dmis dm% pmis pm% mmis mm% arcsz c
11:09:01 820M 790M 96 790M 96 0 0 0 0 42G 64G
11:09:02 835M 810M 97 810M 97 0 0 0 0 42G 64G
11:09:03 810M 785M 96 785M 96 0 0 0 0 42G 64G
Meaning: La tasa de misses en ARC es alta durante un stream. Eso es normal: streaming es “leer y seguir”. arcsz es 42G con objetivo c=64G, así que ARC está sano.
Decision: No entres en pánico ni “aumentes ARC” solo porque los misses son altos. ARC ayuda en relecturas; el throughput en streaming suele estar limitado por los dispositivos.
Task 7: Comprobar si prefetch funciona o está deshabilitado a nivel de módulo
cr0x@server:~$ cat /sys/module/zfs/parameters/zfs_prefetch_disable
0
Meaning: 0 significa que prefetch está habilitado globalmente.
Decision: Si está a 1 y tu carga es mayormente secuencial, vuelve a ponerlo a 0 (y averigua quién lo deshabilitó y por qué).
Task 8: Revisar saturación a nivel de dispositivo y comportamiento de colas
cr0x@server:~$ iostat -x 1 3
Device r/s rkB/s rrqm/s %rrqm r_await rareq-sz aqu-sz %util
sda 85.0 138240.0 0.0 0.00 8.40 1626.4 0.71 98.0
sdb 76.0 133120.0 0.0 0.00 8.10 1751.6 0.62 97.2
sdc 79.0 141312.0 0.0 0.00 8.55 1788.8 0.68 98.5
sdd 80.0 143360.0 0.0 0.00 8.60 1792.0 0.69 98.7
sde 78.0 142336.0 0.0 0.00 8.45 1824.8 0.66 98.3
sdf 82.0 141312.0 0.0 0.00 8.50 1723.3 0.70 98.6
Meaning: %util cerca de 100% y ~8–9 ms de await indican que los discos están saturados. rareq-sz es grande (~1.6–1.8MB), lo cual es bueno para streaming.
Decision: Si los dispositivos están saturados, afinar por encima de la capa de vdev no ayudará. Añade spindles, añade vdevs o muévete a SSD/NVMe.
Task 9: Confirmar que no estás compitiendo con un scrub/resilver (asesinos silenciosos de throughput)
cr0x@server:~$ sudo zpool status tank | sed -n '1,12p'
pool: tank
state: ONLINE
scan: scrub in progress since Thu Dec 26 10:41:12 2025
2.11T scanned at 1.20G/s, 1.02T issued at 595M/s, 8.11T total
0B repaired, 12.59% done, 03:56:18 to go
Meaning: El scrub está consumiendo ~595 MB/s de ancho de banda emitido. Tus lecturas en streaming compiten por los mismos discos.
Decision: Si esto ocurre en una ventana de streaming de producción, pausa/cancela el scrub o prográmalo adecuadamente. Los scrubs son buenos; los scrubs en picos no lo son.
Task 10: Revisar ashift y alineación de sectores (mala alineación es para siempre)
cr0x@server:~$ sudo zdb -C tank | grep -E 'ashift|vdev_tree' -n | head -n 8
45: vdev_tree:
61: ashift: 12
Meaning: ashift=12 significa sectores de 4K, correcto para la mayoría de HDD/SSD modernos. Si ves ashift=9 en discos 4K, el rendimiento puede sufrir mucho.
Decision: Si ashift está mal, la solución es reconstruir el vdev correctamente. No hay “tune” que borre el dolor de la mala alineación.
Task 11: Evaluar fragmentación y presión de espacio (las lecturas secuenciales odian un pool muy lleno)
cr0x@server:~$ sudo zpool list -o name,size,alloc,free,capacity,fragmentation tank
NAME SIZE ALLOC FREE CAPACITY FRAG
tank 10.9T 8.11T 2.77T 74% 33%
Meaning: 74% lleno está bien; 33% de fragmentación es moderado. Si la capacidad está al 90% y la frag es alta, lo “secuencial” tenderá a volverse aleatorio.
Decision: Si estás por encima de ~80–85% en un pool que debe transmitir, planifica expansión o migración. Si la fragmentación es alta, considera reescribir datasets o restaurar desde replicación para reempaquetar bloques.
Task 12: Revisar primarycache/secondarycache por dataset (deja de cachear lo que no vas a releer)
cr0x@server:~$ sudo zfs get -o name,property,value primarycache,secondarycache tank/backup
NAME PROPERTY VALUE
tank/backup primarycache metadata
tank/backup secondarycache none
Meaning: Este dataset cachea solo metadata y evita L2ARC. Eso suele ser correcto para escaneos de backup de una sola pasada y grandes lecturas secuenciales.
Decision: Si las cargas de streaming están expulsando caché útil para otros servicios, pon los datasets de streaming a primarycache=metadata. No trates a ARC como un buffet libre.
Task 13: Validar que readahead no te sabotee en la capa de bloque de Linux
cr0x@server:~$ lsblk -o NAME,TYPE,RA,MOUNTPOINT | grep -E 'tank|zd|sd[a-z]'
sda disk 256
sdb disk 256
sdc disk 256
sdd disk 256
sde disk 256
sdf disk 256
Meaning: El readahead aquí es 256 (KiB en muchas distros). ZFS hace su propio prefetch; el readahead de Linux normalmente importa más para sistemas no ZFS.
Aun así, valores extraños (como multi-megabytes por disco) pueden provocar E/S desperdiciada.
Decision: Si alguien afinó readahead a lo loco y es enorme, devuélvelo a valores sensatos. Arregla una capa a la vez.
Task 14: Confirmar que tu stream no es en realidad una carga de lecturas pequeñas
cr0x@server:~$ sudo zpool iostat -r tank 1 2
capacity operations bandwidth
pool alloc free read write read write
---------- ----- ----- ----- ----- ----- -----
tank 8.11T 2.77T 8500 3 420M 1.00M
---------- ----- ----- ----- ----- ----- -----
tank 8.11T 2.77T 8600 2 430M 1.10M
---------- ----- ----- ----- ----- ----- -----
Meaning: 8.5K operaciones de lectura para ~420 MB/s implica tamaño medio de E/S alrededor de ~50 KB, no ideal para “streaming”.
Decision: Si tu tamaño de E/S es pequeño, arregla la aplicación para leer en tamaños mayores/profundidad de cola; fija recordsize=1M para futuras escrituras; y verifica que prefetch no se esté viendo alterado por muchos lectores entrelazados.
Palancas de afinación que realmente mueven el rendimiento
1) recordsize: configúralo para los archivos que vas a almacenar, no para los que ya almacenaste
recordsize establece el tamaño máximo de bloque que ZFS usará para archivos en un dataset. Para lecturas secuenciales, bloques más grandes suelen significar menos operaciones de E/S,
menos cómputo de checksums por byte, menos búsquedas de metadatos y mejor eficiencia del dispositivo.
Qué hacer:
- Medios, backups, objetos grandes, escaneos analíticos:
recordsize=1Msuele ser el punto de partida correcto. - Bases de datos e imágenes de VM: no aumentes recordsize a ciegas. Esos suelen tener patrones mixtos/aleatorios; optimízalos por separado.
La trampa: recordsize no reescribe retroactivamente archivos existentes. Si tu “dataset de streaming” se creó con 128K y se pobló durante un año, cambiar a 1M hoy solo ayuda a nuevas escrituras.
Si necesitas el beneficio ahora, reescribe los datos.
2) atime: desactívalo para lecturas en streaming a menos que disfrutes escrituras innecesarias
Las actualizaciones de atime en lecturas causan escrituras. Las escrituras causan trabajo de TXG y potencialmente actividad adicional en disco.
Para un dataset de solo streaming, atime=off es una victoria aburrida.
3) primarycache / secondarycache: deja de envenenar ARC con streams de una sola pasada
ARC es precioso porque está en RAM y es caliente. Las lecturas secuenciales de datasets grandes pueden expulsar caché realmente útil para otros servicios (metadatos, archivos pequeños calientes,
blobs de configuración leídos con frecuencia, etc.).
Para datasets que son mayormente lecturas secuenciales de una sola pasada (verificación de backups, rehidratación archivística, analítica por lotes), pon:
primarycache=metadata. A menudo también secondarycache=none si tienes L2ARC y no quieres quemarlo en junk.
Esto no necesariamente aumentará el throughput del stream; previene daños colaterales. En sistemas reales, eso es la mitad de la batalla.
4) Compresión: lz4 suele ser “gratis”, hasta que no lo es
compression=lz4 suele mejorar el rendimiento de lectura en streaming porque lees menos bytes de disco y gastas algo de CPU descomprimiendo.
Esa es una gran compensación en pools HDD y muchos pools SSD.
Pero en NVMe rápido la compresión puede mover el cuello de botella a la CPU. Verás dispositivos subutilizados y hilos de CPU al 100% descomprimiendo y verificando checksums.
Si tu pipeline está limitado por CPU, considera CPUs más rápidas, verifica que no uses un checksum costoso y compara con/ sin compresión para tus datos.
5) No confundas “más ARC” con “más throughput”
ARC ayuda cuando vuelves a leer datos. El throughput en streaming generalmente está limitado por el ancho de banda de almacenamiento y por cuán eficazmente ZFS emite E/S.
Aumentar ARC puede ayudar si tu stream se repite o si el cache de metadatos es el limitador oculto, pero para lecturas de una sola pasada a menudo solo cambia quién se queda con la RAM.
6) Paralelismo: un vdev es una sola carril
ZFS escala el throughput principalmente añadiendo vdevs. Los mirrors generalmente ofrecen mejor paralelismo de lectura y geometría más simple.
RAIDZ puede ser eficiente en ancho de banda y capacidad, pero un solo vdev RAIDZ sigue siendo un único vdev, y eso limita la concurrencia.
7) Special vdev: la metadata pertenece a almacenamiento rápido (a veces)
Si tu trabajo de “lectura secuencial” aún choca con metadata (muchos archivos, muchas exploraciones de directorio, archivos secundarios pequeños), mover metadata y bloques pequeños a un special vdev
puede reducir bloqueos en cabeza de línea en HDDs. Esto trata más sobre “tiempo hasta el primer byte” y streaming consistente en cargas con muchos archivos que sobre MB/s puros en un archivo enorme.
Broma #2: L2ARC para streaming es como llevar maleta a un viaje de una noche—técnicamente impresionante, prácticamente innecesario.
Diseño de vdev y matemáticas de ancho de banda defendibles en una reunión
La afinación para lecturas secuenciales se vuelve existencial cuando la gente pregunta: “¿Por qué este pool de 12 discos no puede hacer 4 GB/s?” Porque la topología importa más que el optimismo.
Mirrors: la opción aburrida de alto throughput
Los mirrors leen de cualquiera de los lados. Con múltiples vdevs mirror, ZFS puede repartir lecturas entre vdevs y discos. Para lecturas grandes en streaming, los mirrors suelen ofrecer:
- alta concurrencia (muchos vdevs)
- rendimiento predecible bajo carga mixta
- comportamiento simple de reconstrucción (resilver claro)
La contrapartida es la eficiencia de capacidad. Los ingenieros de almacenamiento pueden discutirlo durante horas, lo cual es una forma reconocida de cardio en algunas organizaciones.
RAIDZ: eficiente en capacidad, pero sensible a la geometría
RAIDZ puede streamar rápido, especialmente con múltiples vdevs RAIDZ, pero hay dos realidades prácticas:
- Cuello de botella en vdev único: Un vdev RAIDZ único es un solo vdev. Si tu pool tiene un RAIDZ2, estás limitado por la envolvente de rendimiento de ese vdev.
- Las lecturas pequeñas duelen más: RAIDZ tiene que reconstruir desde paridad para ciertos patrones de acceso, y bloques pequeños pueden crear amplificación de lectura.
Para máximo throughput secuencial, prefiere múltiples vdevs con un tamaño que encaje con tus restricciones operativas. Si no puedes añadir vdevs porque el chasis está lleno,
tus opciones de “afinación” se reducen a no empeorar las cosas.
Vdevs anchos: tentadores en hojas de cálculo, problemáticos en producción
Vdevs RAIDZ muy anchos lucen bien en capacidad/€ por TB. También incrementan tiempos de rebuild, ampliando ventanas de exposición, y pueden crear acantilados de rendimiento cuando un disco va lento.
Si tu negocio central es throughput de streaming y operaciones predecibles, los vdevs anchos son una negociación con la entropía.
Profundidad de cola y concurrencia: streaming es una tubería, no una sola petición
Muchas aplicaciones leen secuencialmente pero de forma mono-hilo con buffers pequeños. Eso puede dejar el pool subutilizado porque discos/SSDs quieren múltiples E/S pendientes.
Cuando fio con iodepth=32 alcanza 1.5 GB/s pero tu aplicación alcanza 400 MB/s, la aplicación se está auto-limitando.
Arréglalo aumentando el tamaño de lectura de la aplicación, habilitando I/O asíncrono, añadiendo hilos lectores o usando herramientas que lean bloques grandes eficientemente.
ZFS puede prefetch, pero no es sustituto de una aplicación que se niega a encolar trabajo.
Compresión, sumas de verificación y CPU: aliados hasta que no lo son
ZFS hace sumas de verificación end-to-end. Eso no es opcional. La buena noticia: la verificación de checksum suele ser lo bastante rápida en CPUs modernas.
La mala noticia: “lo bastante” acaba justo cuando mejoras a NVMe rápido y mantienes la misma CPU de hace tres años.
Cómo saber si estás limitado por CPU en lecturas
- zpool iostat muestra dispositivos no saturados pero el throughput está plafonado.
- mpstat/top muestra uno o más núcleos de CPU al máximo en tiempo kernel/system.
- fio con I/O directo no aumenta throughput al incrementar iodepth más allá de cierto punto.
Compromisos de compresión
lz4 es la recomendación por defecto por buenas razones: bajo coste de CPU, a menudo mayor throughput y mejora la caché efectiva.
Si tus datos son incomprimibles (video ya comprimido), lz4 no ayudará mucho; tampoco suele perjudicar demasiado, pero mide.
Compresiones más fuertes (zstd en niveles altos) pueden reducir E/S de disco a costa de CPU. Eso puede ayudar pools HDD para ciertos datasets, pero para throughput puro en streaming
es fácil pasarse y mover el cuello de botella a la CPU. “Más compresión” no es lo mismo que “más rendimiento”.
Elección de algoritmo de checksum
La mayoría usa fletcher4 (rápido) o sha256 (más fuerte, más CPU). Para throughput de lectura, checksums más rápidos pueden importar en sistemas con mucho NVMe.
Si estás en un entorno regulado, la elección puede no ser tuya. Si no, elige pragmáticamente: mantén la integridad lo suficientemente fuerte y el rendimiento predecible.
Una frase que funciona en operaciones: “La esperanza no es una estrategia.”
— General Gordon R. Sullivan.
No arreglas el throughput de lectura con esperanza; lo arreglas con mediciones y cambios controlados.
Prefetch, ARC y por qué L2ARC no salvará tu stream
Prefetch: qué hace para lecturas secuenciales
El prefetch de ZFS intenta detectar acceso secuencial y emitir E/S de lectura por adelantado para que los siguientes bloques ya estén en vuelo.
Para una lectura de un archivo grande de forma secuencial, prefetch suele ser beneficioso. Para muchos lectores secuenciales entrelazados, puede volverse ruidoso pero aun así útil.
El modo de fallo clásico: alguien deshabilita prefetch para “arreglar latencia de lecturas aleatorias” en un dataset de base de datos, luego olvida que es global o que lo cambió,
y ahora los streams de medios o las restauraciones de backups son más lentas. Esto sucede más a menudo de lo que nadie admitirá.
ARC: qué cachear para sistemas de streaming
ARC es una caché combinada de datos+metadata. Para cargas de streaming, cachear los propios datos del stream normalmente aporta poco beneficio a menos que los vuelvas a leer.
Lo que sí importa: la metadata. Si tienes una carga que abre muchos archivos, realiza muchas stat, recorre directorios o lee índices secundarios,
caché de metadata puede mejorar la “rampa” y suavizar el rendimiento.
L2ARC: cuándo ayuda
L2ARC es una caché de segundo nivel, normalmente en SSD. Ayuda cuando tienes un conjunto de trabajo mayor que la RAM que se vuelve a leer.
Las lecturas de streaming de archivos enormes rara vez se vuelven a leer pronto para justificar L2ARC; además puede añadir overhead porque ZFS debe gestionar encabezados de L2ARC y alimentar la caché.
Si tu sistema es un servicio de archivos multi-tenant y los trabajos de streaming están expulsando datos calientes de otros tenants, L2ARC puede ayudar a esos otros tenants,
pero no necesariamente hará más rápido el trabajo de streaming. Es una ganancia, pero sé honesto sobre qué ganancia compras.
Scrub/resilver y otros trabajos “en segundo plano” que no lo son
Las lecturas en streaming compiten con todo lo que quiera ancho de banda de disco: scrub, resilver, eliminación de snapshots, churn intenso de metadata y a veces incluso pruebas SMART largas
si el controlador y el firmware del disco las manejan mal.
Reglas operativas que mantienen predecible el streaming
- Programación de scrubs: ejecuta scrubs cuando puedas permitirte el impacto en ancho de banda. Si no encuentras ventana, tu planificación de capacidad te está mintiendo.
- Postura de resilver: trata el modo degradado como un incidente. El throughput de streaming durante un resilver suele reducirse, y eso es esperado.
- Eliminación de snapshots: destruir snapshots masivos puede causar intensa actividad de espacio libre. No lo hagas a las 10:00 un lunes.
Tres micro-historias corporativas desde el campo
Incidente por una suposición errónea: “Es secuencial, así que debe ser contiguo”
Un equipo de pipeline de medios tenía un NAS con ZFS sirviendo archivos de video grandes. Durante meses fue bien: los editores extraían material, los nodos de render leían clips,
y el throughput se mantuvo cómodamente alto. Luego llegó un nuevo show y todo se sintió “pegajoso”. La reproducción tartamudeaba. Los transcodificados nocturnos no cumplían plazos matutinos.
La suposición era simple: “Los archivos son grandes y se leen secuencialmente, así que los discos hacen lecturas secuenciales.” La verdad era más fea.
El dataset había estado bajo churn constante: ingest, ediciones parciales, re-exportaciones y snapshots frecuentes por “seguridad”.
Los archivos se reescribían a nivel de aplicación, pero copy-on-write de ZFS convirtió cada reescritura en nuevas asignaciones de bloque.
Al inspeccionar zpool list, el pool estaba en altos 80% de capacidad y la fragmentación era suficiente para importar.
zpool iostat mostró muchas lecturas de tamaño medio y mayor latencia de la esperada para esos discos.
La carga era secuencial en la mente de la aplicación pero no físicamente en disco.
La solución no fue un sysctl mágico. Ampliaron el pool añadiendo vdevs y luego replicaron el dataset a uno nuevo con el recordsize previsto.
Esa reescritura efectivamente “desfragmentó” los datos. El throughput volvió. La lección: los patrones secuenciales no garantizan accesos secuenciales en disco,
especialmente tras meses de snapshots y reescrituras.
Optimización que salió mal: “Deshabilitar prefetch para reducir contaminación de caché”
Un grupo de plataforma corría una flota mixta: bases de datos, object storage y un servicio de backup en almacenamiento ZFS compartido.
Alguien leyó que prefetch puede perjudicar la latencia de lecturas aleatorias, especialmente para bases de datos que hacen su propio caching.
Deshabilitaron prefetch a nivel de módulo para “estabilizar la BD”.
El equipo de BD reportó alguna mejora en picos. Todos celebraron en silencio y siguieron.
Dos semanas después, una prueba de restauración falló: throughput muy bajo. Otro equipo que hacía escaneos analíticos vio lo mismo.
Los síntomas eran clásicos: el pool no emitía grandes pipelines de lectura suaves; avanzaba a trompicones con lecturas bajo demanda.
El informe fue incómodo porque nadie había conectado la naturaleza global de la opción de prefetch con la variedad de cargas.
La “optimización” solucionó un dolor inyectando otro en otro lugar.
Volvimos a habilitar prefetch y abordamos el comportamiento de la BD de la forma correcta: elecciones por dataset, ajustes de caché en la aplicación y aislar cargas cuando haga falta.
La moraleja: “ajustar globalmente” es un eufemismo para “radio de impacto global”. Cuando cambias el comportamiento del sistema de archivos, lo cambias para todos.
Práctica aburrida pero correcta que salvó el día: “Medimos el plan de rebuild”
Un proyecto de renovación de almacenamiento migró una plataforma de backup/restore a servidores nuevos con NICs más rápidos y más SSDs.
El equipo hizo algo poco fashion: escribieron el throughput esperado por vdev, por host y por ruta de red, luego probaron cada capa independientemente con herramientas simples.
No fue glamoroso, pero fue medible.
Durante la semana de cutover, un nodo rindió menos. El pánico intentó empezar un pequeño incendio: “ZFS es lento” y “quizá el firmware nuevo está mal.”
Pero como existían números base, el equipo comparó inmediatamente zpool iostat -v de ese nodo contra los demás.
Un SSD mostraba latencia ligeramente más alta y menor ancho de banda. No catastrófico, pero suficiente para capar el nodo.
Cambiaron el dispositivo sospechoso, resilverizaron y el throughput volvió a la línea base.
Nada de tuning heroico. Nada de ruleta de sysctl nocturna. Solo validación aburrida y un envelope de rendimiento acordado.
La ganancia no fue solo velocidad; fue confianza. Cuando sabes qué es “normal”, puedes tratar las desviaciones como hechos, no como sensaciones.
Errores comunes: síntoma → causa raíz → arreglo
1) Síntoma: Throughput está muy por debajo de las especificaciones del disco, y iostat muestra tamaños de lectura pequeños
Causa raíz: La aplicación lee en trozos pequeños (4K–64K), derrotando el streaming eficiente; o los archivos están almacenados en bloques pequeños por el recordsize en el momento de la escritura.
Arreglo: Aumenta el tamaño de lectura/profundidad de cola de la aplicación; pon recordsize=1M para el dataset y reescribe datos para beneficiarte; verifica que prefetch esté habilitado.
2) Síntoma: Un disco en un vdev muestra await mayor y menor ancho de banda, el pool hace sierra
Causa raíz: Un dispositivo lento o fallando, problemas de cableado/controladora, o comportamiento SMR bajo carga sostenida.
Arreglo: Confirma con zpool iostat -v y iostat -x; reemplaza el dispositivo; valida firmware/controladora; evita SMR en niveles de rendimiento.
3) Síntoma: Streaming va bien hasta que empieza un scrub, luego todo cae
Causa raíz: El scrub compite por ancho de banda; la programación ignora ventanas de uso pico.
Arreglo: Reprograma scrubs; considera políticas por pool; pausa temporalmente durante streams críticos; planifica más colchón.
4) Síntoma: Hit rate de ARC es bajo durante el stream y alguien lo llama “problema de caché”
Causa raíz: Streaming produce muchos misses por naturaleza; ARC no está fallando.
Arreglo: Deja de afinar basándote solo en miss%; enfócate en saturación de discos y tamaño de E/S; protege ARC con primarycache=metadata donde corresponda.
5) Síntoma: Pool NVMe no satura dispositivos, la CPU está alta y el throughput se estabiliza
Causa raíz: Limitación por CPU en verificación de checksum y/o descompresión; posible lectura mono-hilo.
Arreglo: Mide con compresión activada/desactivada; asegúrate de suficiente paralelismo (jobs/iodepth); considera CPU más rápida, estrategias de pinning NUMA en la aplicación y evita niveles de compresión pesados.
6) Síntoma: “Cambiamos recordsize pero nada mejoró”
Causa raíz: Los archivos existentes conservan sus tamaños de bloque; recordsize no es retroactivo.
Arreglo: Reescribe datos: replicación a dataset nuevo, reingest o reescritura por la aplicación. Valida con zdb.
7) Síntoma: El pool se hizo más lento en meses sin cambios de hardware
Causa raíz: Fragmentación y alta ocupación por CoW + snapshots + churn de reescritura.
Arreglo: Mantén pools de rendimiento por debajo de ~80–85% si te importa el ancho de banda consistente; añade vdevs; migra/replica para reempaquetar; revisa retención de snapshots.
Listas de verificación / plan paso a paso
Paso a paso: afina un dataset para lecturas de streaming máximas (datos nuevos)
- Crea un dataset dedicado para objetos de streaming; no lo compartas con bases de datos/VMs.
- Establece recordsize (típicamente
1Mpara archivos grandes):zfs set recordsize=1M tank/media. - Apaga atime:
zfs set atime=off tank/media. - Usa compresión lz4 salvo que hayas probado que perjudica:
zfs set compression=lz4 tank/media. - Decide política de caché: para cargas de una sola pasada,
primarycache=metadata; para bibliotecas compartidas calientes, mantieneall. - Ingiere datos una sola vez (evita churn de reescritura); valida tamaños de bloque con
zdb. - Benchmarca con fio usando tamaños de bloque e iodepth realistas; captura
zpool iostat -vdurante la prueba.
Paso a paso: recuperar throughput de streaming en un dataset “lento” existente
- Ejecútalo guion de diagnóstico rápido y determina si estás device-bound, CPU-bound o I/O-size-bound.
- Revisa capacidad y fragmentación del pool; si estás muy lleno, expande o migra primero.
- Confirma tamaños de bloque reales de los archivos importantes; no asumas que cambiar recordsize lo arregló.
- Detén la contención de fondo durante las pruebas (scrub/resilver/eliminación de snapshots).
- Reescribe el dataset si los tamaños de bloque/fragmentación son el problema: replica a un dataset fresco con ajustes correctos.
- Vuelve a probar con fio; compara métricas por vdev y uso de CPU antes/después.
Lista operativa: evitar que el rendimiento de streaming se degrade
- Mantén pools de rendimiento por debajo de ~80–85% si te importa ancho de banda consistente.
- Programa scrubs fuera de ventanas de streaming; trata la superposición de scrub como reducción de rendimiento planificada.
- Evita mezclar cargas con metas de afinación incompatibles en el mismo dataset.
- Baselínea el throughput tras cambios; mantén un procedimiento de benchmark “conocido bueno”.
- Cuando cambies parámetros globales del módulo, documenta y revisa como harías con reglas de firewall.
Preguntas frecuentes
1) ¿Debo poner recordsize=1M en todas partes para mejores lecturas secuenciales?
No. Ponlo en datasets que almacenen archivos grandes aptos para streaming. Bases de datos, imágenes de VM y cargas mixtas aleatorias suelen empeorar con bloques sobredimensionados.
2) ¿Por qué no mejoraron mis archivos existentes al cambiar recordsize?
Porque recordsize no reescribe bloques. Los archivos existentes conservan los tamaños de bloque con los que fueron escritos. Necesitas reescribir o replicar para beneficiarte.
3) ¿Prefetch de ZFS siempre es bueno para lecturas secuenciales?
Usualmente sí para streams secuenciales únicos. Puede ser menos efectivo con muchos lectores entrelazados. Deshabilitarlo globalmente rara vez es buena idea a menos que hayas probado un beneficio específico.
4) ¿L2ARC aumenta el throughput de streaming?
Típicamente no para streams de una pasada. L2ARC ayuda cuando los datos se vuelven a leer y el working set no cabe en RAM. Para streaming suele ser overhead con poco retorno.
5) ¿Por qué el miss% de ARC es tan alto durante el streaming? ¿Es malo?
Un miss% alto es normal para cargas de lectura única. Lo que importa es si discos/SSDs están saturados y si los tamaños de lectura son lo bastante grandes.
6) RAIDZ o mirrors para lecturas secuenciales máximas?
Los mirrors tienden a ofrecer mejor paralelismo y lecturas más predecibles bajo carga mixta. RAIDZ también puede streamar bien, especialmente con múltiples vdevs, pero un RAIDZ ancho único es limitante de throughput.
7) ¿La compresión puede mejorar el throughput de lecturas secuenciales?
Sí. lz4 a menudo mejora throughput al reducir bytes leídos de disco. En NVMe rápido o sistemas limitados por CPU, la compresión puede convertirse en cuello de botella; mide para tus datos.
8) ¿Cómo sé si estoy limitado por CPU en lecturas ZFS?
Si los dispositivos no están saturados, el throughput se estabiliza y la CPU está ocupada (a menudo en tiempo kernel/system), probablemente estés limitado por CPU en checksum/descompresión o por lectores mono-hilo.
Confírmalo con pruebas fio y métricas de CPU del sistema.
9) Mi pool está al 90% de ocupación. ¿La afinación puede salvar el rendimiento de streaming?
La afinación puede reducir el daño, pero no restaurará las leyes físicas. Alta ocupación aumenta la dificultad de asignación y la fragmentación. Expande, añade vdevs o migra.
10) ¿Por qué un scrub afecta tanto mis lecturas en streaming?
El scrub es básicamente una carga de lectura estructurada e implacable a través de todo el pool. Compite directamente con tus lecturas de streaming por el mismo ancho de banda de dispositivo.
Siguientes pasos
Si quieres máximo rendimiento de streaming en ZFS y quieres que sea fiable—no solo el día del benchmark—haz esto en orden:
- Mide el cuello de botella:
zpool iostat -v,iostat -x, métricas de CPU y una prueba directa confiode lectura secuencial. - Arregla las palancas grandes: cantidad/topología de vdevs, recordsize de datasets para datos nuevos, tamaño de lectura/profundidad de cola de la aplicación.
- Protege el resto del sistema: políticas de caché (
primarycache=metadata), programación de scrubs y evita ajustes globales con radio de impacto sorpresa. - Reescribe cuando sea necesario: si los tamaños de bloque y la fragmentación son el problema, la replicación a un dataset fresco suele ser el “defrag” más limpio.
Luego anota qué significa “bueno” para tu pool: MB/s esperados por vdev, latencia típica y qué actividad de fondo es aceptable. El futuro-tú lo agradecerá,
incluso si el presente-tú piensa que la documentación es algo que hacen otras personas.