Lecturas secuenciales en ZFS: Afinación para el máximo rendimiento de streaming

¿Te fue útil?

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í:

  1. La aplicación emite lecturas (a menudo 4K–1MB dependiendo de la app/librería).
  2. DMU mapea offsets de archivo a bloques según recordsize y tamaños reales de bloque en disco.
  3. Prefetch decide si leer por adelantado especulativamente.
  4. Búsqueda en ARC hit o miss; los misses programan E/S a disco.
  5. El scheduler de vdev convierte lecturas lógicas en lecturas físicas a través de mirrors/RAIDZ, respetando límites de cola.
  6. 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 iostat y fio. 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=1M suele 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)

  1. Crea un dataset dedicado para objetos de streaming; no lo compartas con bases de datos/VMs.
  2. Establece recordsize (típicamente 1M para archivos grandes): zfs set recordsize=1M tank/media.
  3. Apaga atime: zfs set atime=off tank/media.
  4. Usa compresión lz4 salvo que hayas probado que perjudica: zfs set compression=lz4 tank/media.
  5. Decide política de caché: para cargas de una sola pasada, primarycache=metadata; para bibliotecas compartidas calientes, mantiene all.
  6. Ingiere datos una sola vez (evita churn de reescritura); valida tamaños de bloque con zdb.
  7. Benchmarca con fio usando tamaños de bloque e iodepth realistas; captura zpool iostat -v durante la prueba.

Paso a paso: recuperar throughput de streaming en un dataset “lento” existente

  1. Ejecútalo guion de diagnóstico rápido y determina si estás device-bound, CPU-bound o I/O-size-bound.
  2. Revisa capacidad y fragmentación del pool; si estás muy lleno, expande o migra primero.
  3. Confirma tamaños de bloque reales de los archivos importantes; no asumas que cambiar recordsize lo arregló.
  4. Detén la contención de fondo durante las pruebas (scrub/resilver/eliminación de snapshots).
  5. Reescribe el dataset si los tamaños de bloque/fragmentación son el problema: replica a un dataset fresco con ajustes correctos.
  6. 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:

  1. Mide el cuello de botella: zpool iostat -v, iostat -x, métricas de CPU y una prueba directa con fio de lectura secuencial.
  2. 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.
  3. Protege el resto del sistema: políticas de caché (primarycache=metadata), programación de scrubs y evita ajustes globales con radio de impacto sorpresa.
  4. 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.

← Anterior
MySQL vs MariaDB: 504 de WordPress — ¿quién colapsa primero ante un pico de tráfico
Siguiente →
Shellshock: cuando una variable de entorno se convirtió en noticia mundial

Deja un comentario