Si tu sistema de almacenamiento se siente rápido al transmitir archivos grandes pero empieza a jadear en cuanto alguien ejecuta
find en un árbol de archivos diminutos, estás frente al enemigo más antiguo del almacenamiento empresarial: metadatos y
E/S de bloques pequeños. ZFS es excelente protegiendo datos, pero no puede cambiar la física: las lecturas aleatorias desde medios lentos siguen siendo
lecturas aleatorias desde medios lentos. La buena noticia es que ZFS te ofrece una puerta de escape: vdevs especiales y
bloques pequeños especiales.
Hecho correctamente, esta es una de esas optimizaciones que se siente como hacer trampa: los recorridos de directorios se aceleran, los
checkouts de git dejan de arrastrarse, la extracción de imágenes de contenedores gana agilidad, y desaparecen esos tickets de “¿por qué ls va lento?”.
Hecho mal, también es una manera espectacular de aprender lo importante que son los metadatos para la supervivencia de tu pool.
Esta guía trata sobre hacerlo bien—en producción, con las limitaciones que aparecen en una llamada de soporte.
Qué significa realmente “bloques pequeños especiales”
En ZFS, “bloques pequeños especiales” es una abreviación para una política: almacenar bloques por debajo de un umbral de tamaño elegido en un
vdev especial—típicamente SSD o NVMe—mientras dejas los datos de archivos grandes en los vdev principales (a menudo HDD).
No es una caché. No es a modo de mejor esfuerzo. Esos bloques viven allí.
El vdev especial ya tiene una función incluso sin umbral: puede contener metadatos. Los metadatos incluyen cosas como
bloques indirectos, dnodes, entradas de directorio y otras estructuras que hacen posibles las operaciones del sistema de archivos.
Cuando configuras special_small_blocks en un dataset, amplías esa función: los bloques de datos de archivos
pequeños también aterrizan en el vdev especial.
La magia no es que el SSD sea “más rápido”. La magia es que las cargas de trabajo con archivos pequeños están dominadas por E/S aleatoria, y
la E/S aleatoria en HDD es básicamente una prueba de simpatía mecánica que perderás. Si puedes empujar esas lecturas a medios de baja latencia,
toda la carga de trabajo cambia de carácter: menos búsquedas, menos bloqueos de IOPS y una cola más corta en el lugar equivocado.
La versión en una línea: los vdevs especiales permiten a ZFS separar “cosas que deben encontrarse rápido” de “cosas que son grandes de leer”.
Broma #1: Piensa en los bloques pequeños especiales como darle a tus metadatos un coche deportivo. Solo recuerda: si destrozas el coche deportivo,
no solo pierdes una reunión—olvidas dónde está la oficina.
Hechos y contexto interesantes (por qué existe esto)
Aquí hay algunos datos históricos y de contexto concretos que importan cuando tomas decisiones de diseño:
-
ZFS comenzó con la suposición de RAM cara y discos más lentos. ARC fue diseñado para ser inteligente
con el caché porque “simplemente añadir RAM” no siempre era la respuesta en despliegues tempranos. -
La “clase de asignación especial para metadatos” llegó más tarde. ZFS temprano no tenía una forma limpia de fijar
metadatos a un tipo de vdev diferente; los vdevs especiales añadieron una clase explícita para ello. -
El rendimiento con archivos pequeños siempre ha moldeado la reputación del sistema de archivos. Los usuarios perdonan
menos una transmisión lenta que una copia de seguridad lenta—pero loslslentos y las compilaciones demoradas causan enojo inmediato. -
Los tamaños de sector de 4K cambiaron las reglas del juego. “Archivos diminutos” todavía se traducen en múltiples
bloques de metadatos, y las lecturas alineadas a 4K significan que pagas un tamaño mínimo de E/S incluso para contenido pequeño. -
El copy-on-write hace que los patrones de escritura aleatoria sean más comunes. ZFS debe escribir nuevos bloques y actualizar
punteros; evita las actualizaciones en sitio, lo cual es excelente para la consistencia y los snapshots pero puede aumentar el churn de metadatos. -
NVMe no solo añadió velocidad; cambió el comportamiento de colas. La brecha de latencia entre NVMe y HDD
significa que mover una pequeña fracción de E/S (metadatos + bloques pequeños) puede cambiar dramáticamente los tiempos de respuesta. -
“Especial” no es “caché”. L2ARC es una caché y puede reconstruirse; la asignación a vdev especial es
autoritativa. Este simple hecho impulsa la mayoría de las decisiones de riesgo operativo. -
Los dnodes son un villano silencioso. Los dnodes son metadatos por objeto y pueden convertirse en un punto caliente para
árboles con millones de archivos; colocar dnodes en el vdev especial puede ser una gran ventaja. -
La contabilidad de espacio importa más de lo que crees. Si el vdev especial se llena, ZFS no “derramará”
metadatos de vuelta a HDD de manera amistosa; puedes terminar con fallos de asignación y opciones operativas feísimas.
Cómo funciona internamente (lo suficiente para tomar buenas decisiones)
The special vdev allocation class
Los pools ZFS se componen de vdevs. Un “vdev especial” es un vdev asignado a una clase de asignación especial. ZFS usa
clases de asignación para decidir dónde van los bloques. Cuando un pool tiene un vdev especial, ZFS puede asignar ciertos tipos de bloques
allí—particularmente metadatos. Si habilitas la colocación de bloques pequeños, los bloques de datos pequeños también van allí.
¿Qué cuenta como “metadatos”? Es más que directorios. Incluye el árbol de bloques indirectos que apuntan a los bloques de contenido de archivos,
y esos bloques indirectos se acceden durante las lecturas. Pónlos en SSD y hasta las lecturas de archivos grandes pueden beneficiarse—especialmente para archivos fragmentados o árboles de bloques profundos.
special_small_blocks: la perilla de política
special_small_blocks es una propiedad por dataset. Si se establece a un tamaño (por ejemplo, 16K), entonces los bloques de datos
de archivos de hasta ese tamaño se asignan al vdev especial.
Tres verdades operativas:
- No es retroactivo. Los bloques existentes se quedan donde están a menos que se reescriban.
- Interactúa con recordsize. Si tu recordsize es 128K y tu carga escribe muchos archivos diminutos, todavía solo almacenas la porción usada, pero el comportamiento de asignación y compresión del bloque puede importar.
-
Puede aumentar rápidamente el consumo del vdev especial. Los datasets de archivos pequeños suelen ser “todo bloques pequeños”.
Ese es el punto, pero también es la trampa.
Dnodes, dnodesize, y “metadatos que se comportan como datos”
ZFS almacena metadatos por archivo en dnodes. Cuando los atributos extendidos y las ACLs son pesadas (piensa: comparticiones corporativas
con auditoría agresiva y clientes Windows), los dnodes pueden hincharse. Si los dnodes no caben en el “bonus buffer”, ZFS puede
derramar metadatos en bloques separados, aumentando lecturas. Ajustes como xattr=sa y
dnodesize=auto pueden reducir el derrame y amplificar el beneficio del vdev especial.
Por qué esto se siente como un “turbo”
Para muchas cargas con archivos pequeños, la ruta crítica es:
- Buscar entradas de directorio (E/S de metadatos).
- Leer atributos de archivo (más E/S de metadatos).
- Leer datos de archivo pequeños (lecturas aleatorias pequeñas).
Si esos pasos ocurren en HDD, estás limitado por búsquedas y profundidad de cola. Si ocurren en NVMe, estás limitado por
CPU, tasas de aciertos del ARC y el modelo de hilos de la aplicación. Es un problema distinto y, por lo general, uno mejor.
Broma #2: Los HDD son geniales para enseñar paciencia. Desafortunadamente, tu pipeline de CI no se apuntó a la lección.
Cargas de trabajo que ganan (y las que no)
Mejores candidatas
Los bloques pequeños especiales brillan cuando tu conjunto de trabajo incluye muchos archivos pequeños y operaciones pesadas en metadatos:
- Árboles de código fuente y artefactos de compilación (millones de archivos diminutos, recorridos repetidos, muchas llamadas stat).
- Capas de imágenes de contenedores (tormentas de descompresión, muchos archivos pequeños, extracción paralela).
- Cargas estilo Maildir (muchos mensajes pequeños como archivos individuales).
- Alojamiento web y contenido CMS (miles de archivos PHP pequeños, plugins, miniaturas, churn de metadatos).
- Comparticiones corporativas con árboles profundos (navegación de directorios intensiva, comprobaciones ACL, clientes Windows).
- Repositorios de backup con muchos fragmentos pequeños (depende de la herramienta; algunos crean muchos objetos diminutos).
Resultados mixtos
Imágenes de VM y bases de datos pueden beneficiarse indirectamente porque metadatos y bloques indirectos son más rápidos,
pero normalmente dominan por E/S aleatoria de tamaño medio a grande y comportamiento de escrituras sync. Si intentas arreglar
latencia de bases de datos, probablemente necesites hablar primero de SLOG, recordsize, ashift y ajuste de la aplicación.
Pobres candidatas
Si tu carga es mayormente lecturas/escrituras secuenciales grandes y ya tienes prefetch sano y recordsize grande,
los bloques pequeños especiales no cambiarán tu vida. Aún puedes querer metadatos en special para operaciones de gestión más ágiles,
pero no esperes milagros para transmisiones secuenciales.
Diseñar un vdev especial: redundancia, dimensionamiento y dominio de fallo
La regla innegociable: redundancia o arrepentimiento
El vdev especial forma parte del pool. Si falla y no tienes redundancia, tu pool no queda “degradado” de forma simpática. Puedes perder el pool porque pueden faltar metadatos. Trata los vdevs especiales como almacenamiento de primera clase, no como aceleradores desechables.
En la práctica, eso significa mirror como mínimo, y en algunos entornos RAIDZ (para la clase special) si puedes tolerar la amplificación de escrituras y quieres eficiencia de capacidad. Los mirrors son comunes porque mantienen la latencia baja y los tiempos de resilver más simples.
Dimensionamiento: la parte que todos subestiman
El dimensionamiento del vdev especial es donde el proyecto “turbo” tiene éxito o se convierte en generador de incidentes trimestrales. Debes estimar:
- Huella de metadatos para datos existentes y futuros.
- Huella de datos de archivos pequeños bajo tu umbral
special_small_blocks. - Crecimiento por snapshots (metadatos y bloques pequeños pueden multiplicarse con el churn).
- Espacio libre para rendimiento y salud del asignador.
Un modelo mental práctico: si almacenas millones de archivos pequeños, los “datos” pueden ser pequeños, pero el
conteo de objetos hace que los metadatos no sean triviales. Un árbol con 50 millones de archivos puede ser “solo” unos terabytes, y
aun así ser una máquina de metadatos.
Otro modelo práctico: el vdev especial es donde vive el cerebro de tu sistema de archivos. No construyas un cerebro con las
piezas más pequeñas que encontraste en el armario de compras.
Elegir un umbral
Los umbrales típicos son 8K, 16K, 32K, a veces 64K. El valor correcto depende de:
- La distribución del tamaño de tus archivos.
- Qué tan rápido puedes aumentar la capacidad especial.
- Si los “archivos pequeños” del dataset son realmente sensibles a la latencia.
Empieza conservador. Puedes aumentar el umbral más tarde; reducirlo no mueve bloques existentes de vuelta.
Compresión y special
La compresión cambia el tamaño efectivo del bloque almacenado. Si tienes compression=zstd, muchos archivos “medianos” pueden comprimirse por debajo de tu umbral y terminar en special. A veces es genial y a veces una sorpresa.
Selección de dispositivo y durabilidad
Los vdevs especiales hacen muchas escrituras aleatorias pequeñas, especialmente con cargas pesadas en metadatos y snapshots. Elige dispositivos
con resistencia decente y protección contra pérdida de energía si tu entorno lo exige. NVMe de consumo puede funcionar en entornos de laboratorio; en producción suele ser una entrada lenta a un ticket de soporte.
Implementación: paso a paso con comandos reales
Los comandos abajo asumen OpenZFS en Linux. Si estás en FreeBSD, los conceptos y la mayoría de comandos son los mismos,
pero las rutas de dispositivos y algunas herramientas difieren. Reemplaza nombres de pool/dataset y identificadores de dispositivos para que coincidan con tu sistema.
Task 1: Confirmar la topología del pool y la salud actual
cr0x@server:~$ sudo zpool status -v
pool: tank
state: ONLINE
scan: scrub repaired 0B in 0 days 02:11:32 with 0 errors on Sun Dec 22 03:15:12 2025
config:
NAME STATE READ WRITE CKSUM
tank ONLINE 0 0 0
raidz2-0 ONLINE 0 0 0
/dev/disk/by-id/... ONLINE 0 0 0
/dev/disk/by-id/... ONLINE 0 0 0
/dev/disk/by-id/... ONLINE 0 0 0
/dev/disk/by-id/... ONLINE 0 0 0
/dev/disk/by-id/... ONLINE 0 0 0
/dev/disk/by-id/... ONLINE 0 0 0
errors: No known data errors
Interpretación: confirma que no hay una clase special existente y que el pool está estable. No añadas complejidad estructural
a un pool que ya está enfermo.
Task 2: Identificar dispositivos SSD/NVMe candidatos por IDs estables
cr0x@server:~$ ls -l /dev/disk/by-id/ | grep -E 'nvme|ssd|ata' | head
lrwxrwxrwx 1 root root 13 Dec 24 09:10 nvme-SAMSUNG_MZVLB1T0HBLR-00000_S4EWNX0R123456 -> ../../nvme0n1
lrwxrwxrwx 1 root root 13 Dec 24 09:10 nvme-SAMSUNG_MZVLB1T0HBLR-00000_S4EWNX0R789012 -> ../../nvme1n1
Interpretación: usa /dev/disk/by-id para evitar sorpresas por renumeración de dispositivos tras reinicios o cambios de HBA.
Task 3: Comprobar ashift y propiedades generales del pool
cr0x@server:~$ sudo zdb -C tank | grep -E 'ashift|name'
name: 'tank'
ashift: 12
Interpretación: ashift=12 implica sectores de 4K, lo cual es bueno. Los vdevs especiales también deben estar alineados apropiadamente;
generalmente no quieres mezclar eso y crear una amplificación de escrituras innecesaria.
Task 4: Añadir un vdev special en espejo (clase metadata)
cr0x@server:~$ sudo zpool add tank special mirror \
/dev/disk/by-id/nvme-SAMSUNG_MZVLB1T0HBLR-00000_S4EWNX0R123456 \
/dev/disk/by-id/nvme-SAMSUNG_MZVLB1T0HBLR-00000_S4EWNX0R789012
cr0x@server:~$ sudo zpool status tank
pool: tank
state: ONLINE
config:
NAME STATE READ WRITE CKSUM
tank ONLINE 0 0 0
raidz2-0 ONLINE 0 0 0
/dev/disk/by-id/... ONLINE 0 0 0
...
special
mirror-1 ONLINE 0 0 0
nvme-SAMSUNG_MZVLB1T0HBLR...123456 ONLINE 0 0 0
nvme-SAMSUNG_MZVLB1T0HBLR...789012 ONLINE 0 0 0
Interpretación: el pool ahora tiene una clase special. Desde este punto, las asignaciones de metadatos pueden aterrizar allí (dependiendo
de flags y tipo de bloque). El destino del pool ahora está ligado a ese espejo. Monitorízalo como si importara.
Task 5: Verificar que la asignación special es reconocida
cr0x@server:~$ sudo zpool list -v tank
NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
tank 54.5T 31.2T 23.3T - - 23% 57% 1.00x ONLINE -
raidz2-0 54.5T 31.2T 23.3T - - 23% 57%
special 1.8T 120G 1.7T - - 2% 6%
Interpretación: puedes ver la capacidad special por separado. Mantén un ojo en esto; “special solo tiene 6% usado” es una frase tranquilizadora que querrás seguir diciendo.
Task 6: Crear un dataset para la carga de archivos pequeños con valores predeterminados sensatos
cr0x@server:~$ sudo zfs create -o atime=off -o compression=zstd -o xattr=sa tank/work
cr0x@server:~$ sudo zfs get -o name,property,value atime,compression,xattr tank/work
NAME PROPERTY VALUE
tank/work atime off
tank/work compression zstd
tank/work xattr sa
Interpretación: desactivar atime reduce escrituras de metadatos para cargas lectora. xattr=sa a menudo reduce el derrame a objetos xattr separados para atributos extendidos de Linux, lo que ayuda en tormentas de archivos pequeños.
Task 7: Habilitar special small blocks en ese dataset
cr0x@server:~$ sudo zfs set special_small_blocks=16K tank/work
cr0x@server:~$ sudo zfs get special_small_blocks tank/work
NAME PROPERTY VALUE SOURCE
tank/work special_small_blocks 16K local
Interpretación: nuevos bloques de datos hasta 16K se asignarán en el vdev special. Esto no reescribirá datos antiguos.
Task 8: Asegurar que los dnodes pueden escalar con cargas pesadas en metadatos
cr0x@server:~$ sudo zfs set dnodesize=auto tank/work
cr0x@server:~$ sudo zfs get dnodesize tank/work
NAME PROPERTY VALUE SOURCE
tank/work dnodesize auto local
Interpretación: dnodesize=auto permite dnodes más grandes cuando es necesario, lo que puede reducir bloques de metadatos extra
y mejorar los patrones de recorrido de directorios y acceso a atributos. Puede aumentar la huella de metadatos—planifica la capacidad.
Task 9: Línea base del comportamiento con archivos pequeños antes y después (micro-benchmark práctico)
cr0x@server:~$ mkdir -p /tank/work/testtree
cr0x@server:~$ /usr/bin/time -f "elapsed=%E" bash -c 'for i in $(seq 1 200000); do echo x > /tank/work/testtree/f_$i; done'
elapsed=0:04:31
cr0x@server:~$ /usr/bin/time -f "elapsed=%E" bash -c 'find /tank/work/testtree -type f -exec stat -c %s {} \; > /dev/null'
elapsed=0:00:46
Interpretación: no tomes los números absolutos al pie de la letra entre sistemas; busca cambios relativos y dónde se desplaza el tiempo (CPU vs E/S). Además: 200k archivos son suficientes para mostrar dolor sin convertir tu sistema de archivos en un proyecto científico a largo plazo.
Task 10: Observar en tiempo real la distribución de E/S (special vs principal)
cr0x@server:~$ sudo zpool iostat -v tank 2 5
capacity operations bandwidth
pool alloc free read write read write
---------- ----- ----- ----- ----- ----- -----
tank 31.3T 23.2T 210 1800 8.3M 145M
raidz2-0 31.2T 23.3T 40 220 7.9M 40M
special 140G 1.6T 170 1580 420K 105M
---------- ----- ----- ----- ----- ----- -----
Interpretación: quieres ver muchas IOPS de escritura/lectura de metadatos/bloques pequeños en el vdev special en lugar de golpear
los vdevs HDD. El ancho de banda en special puede parecer “pequeño” mientras las operaciones son altas—esto es normal.
Task 11: Comprobar propiedades del dataset que comúnmente interactúan con bloques pequeños
cr0x@server:~$ sudo zfs get -o name,property,value recordsize,primarycache,logbias,sync tank/work
NAME PROPERTY VALUE
tank/work recordsize 128K
tank/work primarycache all
tank/work logbias latency
tank/work sync standard
Interpretación: para árboles de archivos pequeños, recordsize usualmente no es tu palanca (importa más para archivos grandes y bases de datos). Pero primarycache puede ajustarse si estás agotando ARC, y sync puede matar el rendimiento si estás haciendo escrituras síncronas sin querer.
Task 12: Encontrar si special se está llenando y por qué
cr0x@server:~$ sudo zfs list -o name,used,available,usedbysnapshots,usedbydataset,usedbychildren -r tank/work
NAME USED AVAIL USEDSNAP USEDDS USEDCHILD
tank/work 1.02T 9.1T 120G 900G -
cr0x@server:~$ sudo zpool list -v tank | sed -n '1,5p'
NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
tank 54.5T 32.2T 22.3T - - 24% 59% 1.00x ONLINE -
raidz2-0 54.5T 31.6T 22.9T - - 24% 58%
special 1.8T 600G 1.2T - - 9% 33%
Interpretación: si el uso de special aumenta de manera desproporcionada respecto al uso del dataset, puede que tengas un dataset con mucho churn de archivos pequeños, promoción inducida por compresión, o snapshots reteniendo bloques pequeños. Rastrear el used-by-snapshots cuidadosamente.
Task 13: Forzar reescritura de bloques antiguos para migrar a special (controlado)
Porque special_small_blocks no es retroactivo, puede que quieras reescribir datos in situ. Puedes hacerlo
copiando dentro del dataset (lo que asigna nuevos bloques bajo la política actual) o usando send/receive a un dataset nuevo.
cr0x@server:~$ sudo zfs snapshot tank/work@rewrite-start
cr0x@server:~$ sudo rsync -aHAX --delete /tank/work/ /tank/work_rewritten/
Interpretación: esto es intensivo en E/S. Hazlo con guardarraíles (limitación de tasa, ventanas fuera de pico) y asegúrate de que special tenga
capacidad para absorber los bloques migrados.
Task 14: Verificar que la clase special se usa realmente para bloques pequeños
cr0x@server:~$ sudo zfs get -r special_small_blocks tank/work
NAME PROPERTY VALUE SOURCE
tank/work special_small_blocks 16K local
cr0x@server:~$ sudo zdb -dddd tank/work | head -n 20
Dataset tank/work [ZPL], ID 123, cr_txg 45678, 1.02T, 720000 objects
Object lvl iblk dblk dsize dnsize lsize %full type
123 1 128K 16K 16K 512 16K 100% ZFS plain file
124 1 128K 16K 16K 512 12K 75% ZFS plain file
Interpretación: la salida de zdb varía por versión, pero buscas evidencia de que los bloques de datos son pequeños y se
están asignando bajo la política actual. Para verificación más profunda, correlaciona con zpool iostat -v
durante ráfagas de carga y observa picos de operaciones en special.
Operación: monitorización, planificación de capacidad y actualizaciones
Qué monitoreas cambia cuando existe special
Antes de vdevs special, la “capacidad del pool” era la métrica destacada. Después de special, tienes dos capacidades que
pueden matarte el día de forma independiente:
- Espacio libre del vdev principal (el clásico).
- Espacio libre del vdev special (el nuevo precipicio).
Special puede llenarse más rápido de lo esperado porque contiene los bloques con más churn: metadatos, archivos diminutos,
y a veces bloques comprimidos que caen por debajo de tu umbral.
Tres mini-historias del mundo corporativo
Mini-historia 1: El incidente causado por una suposición errónea
Una gran organización de ingeniería interna desplegó vdevs special para acelerar una granja de compilación monorepo. Los resultados iniciales fueron
gloriosos: checkouts y escaneos de dependencias se aceleraron, y la cola de CI dejó de amontonarse cada mañana.
La suposición errónea fue sutil: “special es solo metadatos; no crecerá mucho.” Dimensionaron el espejo NVMe special
basándose en una regla empírica de un despliegue antiguo orientado solo a metadatos. Luego configuraron
special_small_blocks=64K para “estar seguros”, porque muchos de sus archivos estaban por debajo de 50KB.
Dos meses después, el vdev special de la granja de compilación estaba en una utilización incómoda. El sistema comenzó a lanzar
advertencias de asignación y stalls intermitentes. Los desarrolladores lo describieron como “el almacenamiento tiene ataques de pánico”, lo cual
es injusto pero no del todo inexacto.
El postmortem no fue sobre ZFS siendo frágil. Fue sobre responsabilidad: nadie tenía una alerta sobre la capacidad de la clase special.
Miraban el espacio libre total del pool y se sentían tranquilos, mientras el cerebro del pool se quedaba discretamente sin espacio.
La solución fue aburrida y efectiva: añadir más capacidad special (en espejo), bajar el umbral para algunos datasets, e
introducir una política de que cualquier dataset que habilite special_small_blocks debe declarar un modelo de crecimiento y un plan de alertas. La segunda solución fue la verdadera ganancia; la primera fue solo pagar la factura.
Mini-historia 2: La optimización que salió mal
Un equipo de servicios de archivos corporativo intentó “optimizar todo” en una ventana de cambios: vdev special añadido, umbral
puesto a 32K, compresión cambiada de lz4 a zstd, y atime modificado—más una migración masiva de datos para reescribir bloques antiguos en special.
El contratiempo no fue que zstd sea malo o que special sea arriesgado. Fue la combinación de mandos y el momento. La migración reescribió volúmenes enormes de datos pequeños y metadatos. La compresión redujo algunos bloques por debajo del umbral de 32K,
lo que hizo que mucha más data aterrizara en special de lo previsto. La retención de snapshots fijó bloques adicionales. La amplificación de escrituras del espejo special se disparó.
El síntoma en producción fue “lentitud aleatoria”. No una caída total—peor. Una mezcla de picos de latencia, listados de directorios largos, y quejas de algunos usuarios en cualquier momento. La monitorización mostraba los dispositivos NVMe con alta latencia durante ráfagas mientras los vdevs HDD parecían casi aburridos.
La recuperación fue humildad operativa: detener la migración de reescritura, revertir el umbral más agresivo en los datasets más ocupados, y volver a ejecutar la migración más tarde en lotes más pequeños con límites de E/S explícitos. También implementaron un SLO de latencia de escritura para dispositivos special. No es una métrica elegante; es la diferencia entre “sabemos que está enfermo” y “nos enteraremos por un VP”.
Mini-historia 3: La práctica aburrida pero correcta que salvó el día
Un equipo distinto ejecutaba un almacén de artefactos respaldado por ZFS para lanzamientos internos. Usaron vdevs special solo para metadatos
al principio y habilitaron special_small_blocks solo en un dataset usado para índices y manifiestos.
Nada heroico: un espejo de SSDs empresariales, umbral conservador (8K), y alertas agresivas sobre uso de special.
Seis meses después, un SSD empezó a arrojar errores de medios. El pool se degradó, pero no pasó nada más—sin drama,
sin correteos. Tenían un runbook: reemplazar el dispositivo, resilverizar, verificar. Lo habían practicado en un entorno de staging con la misma topología. (Sí, staging. Lo que todos dicen tener y nadie financia.)
La verdadera salvación vino de algo aún menos emocionante: tenían scrubs programados y revisaban los informes de scrub.
El recuento de errores del disco subió lentamente durante semanas, y lo cambiaron antes de que se convirtiera en una página a las 3 a.m. El resilver terminó rápido porque special estaba en espejo, pequeño y sano.
La lección no fue “compra mejores SSD”. Fue “trata a los vdevs special como ciudadanos de primera clase”. Si el cerebro de tu pool vive allí, tu madurez operativa también debe vivir allí.
Métricas operativas prácticas
Cosas que he aprendido a vigilar en entornos reales:
- Capacidad del vdev special (absoluta y tasa de tendencia). Las alertas deben dispararse temprano.
- Latencia del vdev special (lectura y escritura) durante ráfagas de carga conocidas.
- Fragmentación del pool y si el comportamiento del asignador cambia tras cambios de política.
- Retención de snapshots y datasets con churn que fijan bloques special.
- Presión en ARC porque el cache de metadatos cambia tus patrones de aciertos/fallos.
Actualizaciones y ciclo de vida: planifica el “cómo añadimos más después”
Puedes añadir mirrors adicionales al vdev special para expandir la capacidad special (como añadir otro mirror vdev a la clase special). No puedes eliminar fácilmente vdevs special de un pool en la mayoría de despliegues productivos prácticos. Esta es una puerta de no retorno para muchos operadores.
Así que tu diseño debería asumir crecimiento. Si piensas “nunca necesitaremos más de 2TB special”, probablemente vas a aprender algo sobre la distribución del tamaño de tus archivos.
Guía de diagnóstico rápido
Cuando alguien dice “los archivos pequeños van lentos” o “el git checkout está arrastrándose”, puedes perder un día en teorías. Aquí hay una
triage práctica en tres pases que tiende a encontrar el cuello de botella rápidamente.
Primero: confirma que el cuello de botella es el almacenamiento y no el cliente
- Comprueba restricciones del lado cliente: saturación de CPU, extracción single-threaded, antivirus escaneando, o latencia de red.
- En el servidor, observa la carga del sistema y la espera de E/S.
cr0x@server:~$ uptime
09:41:22 up 102 days, 3:12, 5 users, load average: 12.11, 11.90, 10.80
cr0x@server:~$ vmstat 1 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
8 2 0 812344 91232 9023112 0 0 420 1800 3200 7000 12 6 55 27 0
Interpretación: un wa alto sugiere latencia de almacenamiento. No es prueba, pero es una pista fuerte.
Segundo: identifica si special está ayudando o perjudicando
- Comprueba la capacidad y salud de special.
- Observa IOPS y latencias por vdev durante la operación lenta.
cr0x@server:~$ sudo zpool status tank | sed -n '1,25p'
pool: tank
state: ONLINE
config:
NAME STATE READ WRITE CKSUM
tank ONLINE 0 0 0
raidz2-0 ONLINE 0 0 0
special
mirror-1 ONLINE 0 0 0
cr0x@server:~$ sudo zpool list -v tank | tail -n 3
raidz2-0 54.5T 31.6T 22.9T - - 24% 58%
special 1.8T 1.4T 420G - - 18% 77%
Interpretación: special al 77% es una señal de peligro. Incluso si hoy el rendimiento está bien, el asignador y el crecimiento futuro no serán amables.
cr0x@server:~$ sudo zpool iostat -v tank 1 10
Interpretación: si special tiene colas enormes/operaciones y latencia creciente mientras los HDD están inactivos, special es tu cuello de botella
(demasiado pequeño, demasiado lento, o se le está pidiendo demasiado).
Tercero: valida la política del dataset y la realidad de la carga
- Confirma que el dataset en cuestión tiene las propiedades esperadas.
- Comprueba si escrituras sync, snapshots o compresión cambiaron el comportamiento.
- Busca sorpresas de “no es retroactivo”: bloques antiguos aún en HDD.
cr0x@server:~$ sudo zfs get -o name,property,value special_small_blocks,compression,atime,xattr,dnodesize -r tank/work
NAME PROPERTY VALUE
tank/work special_small_blocks 16K
tank/work compression zstd
tank/work atime off
tank/work xattr sa
tank/work dnodesize auto
Interpretación: si el dataset no tiene la propiedad establecida, no estás usando la funcionalidad. Si la tiene, pero el rendimiento no cambió,
puede que estés limitado por escrituras sync, CPU, o los datos no se han reescrito.
Errores comunes, síntomas y soluciones
Mistake 1: Añadir un vdev special no redundante
Síntoma: Todo funciona hasta que no; cuando el dispositivo falla, el pool puede ser irrecuperable.
Solución: Siempre mirror (o RAIDZ) los vdevs special. Si ya lo hiciste mal, trátalo como deuda técnica urgente y planea una migración a un pool correctamente diseñado. Arreglarlo in situ no es la clase de emoción que quieres.
Mistake 2: Configurar special_small_blocks demasiado alto “por si acaso”
Síntoma: La capacidad special crece más rápido de lo esperado; el rendimiento puede mejorar inicialmente y luego degradarse a medida que special se acerca a alta utilización.
Solución: Baja el umbral para nuevas escrituras donde sea apropiado, y añade más capacidad special. Recuerda que bajar la propiedad no mueve bloques existentes de vuelta. Para algunos datasets, reconstruir (send/receive o reescritura) es el enfoque limpio.
Mistake 3: Olvidar que la compresión puede mover más datos a special
Síntoma: Tras habilitar zstd o aumentar el nivel de compresión, el uso de special se acelera, especialmente para archivos “medianos” que comprimen bien.
Solución: Reevalúa el umbral y la selección de datasets. Considera un umbral más pequeño (8K/16K) para datasets comprimidos que contienen muchos archivos moderadamente grandes pero altamente compresibles.
Mistake 4: No alertar sobre el uso del vdev special
Síntoma: El pool tiene mucho espacio libre, pero las operaciones fallan o la latencia se dispara; el equipo está confundido porque “el pool solo está al 60%”.
Solución: Alerta por la utilización de la clase special por separado, con un umbral temprano (por ejemplo, advertir al 60–70%, crítico al 80–85%, ajustado a tu tasa de crecimiento y tolerancia al riesgo).
Mistake 5: Suponer que habilitar la propiedad hace que los datos existentes sean más rápidos
Síntoma: Configuraste special_small_blocks, los usuarios no ven mejora y alguien afirma “no funciona”.
Solución: Explica y planifica la reescritura: mueve datos a un nuevo dataset vía send/receive, o reescribe archivos in situ (con cuidado). Valida con zpool iostat -v durante las cargas.
Mistake 6: Sobrecargar special con demasiados datasets a la vez
Síntoma: Los dispositivos special muestran alta latencia de escritura, mientras los vdevs principales parecen infrautilizados; ralentizaciones intermitentes ocurren durante tormentas de metadatos.
Solución: Prioriza datasets que realmente lo necesiten; usa umbrales conservadores; escala la capacidad special antes de ampliar el alcance. Trátalo como un recurso compartido crítico, no como un vertedero para todos los deseos de rendimiento.
Mistake 7: Ignorar snapshots y churn
Síntoma: El uso de special crece incluso cuando el “tamaño de datos” parece estable; eliminar archivos no libera mucho espacio porque los snapshots retienen bloques.
Solución: Audita la retención de snapshots y los datasets con churn. Ajusta la política de snapshots o mueve cargas con mucho churn a pools/datasets separados con retención personalizada.
Listas de verificación / plan paso a paso
Checklist A: Decidir si los bloques pequeños especiales son la herramienta correcta
- Confirma que la carga está dominada por metadatos/archivos pequeños (find lento, stat lento, untar lento).
- Confirma que el cuello de botella es latencia/IOPS de almacenamiento, no red o CPU.
- Mide la distribución de tamaños de archivo y el conteo de objetos (aproximado está bien, pero mide).
- Decide qué datasets realmente lo necesitan (no habilites a todos a la vez).
Checklist B: Diseñar el vdev special de forma segura
- Elige topología redundante (mirror mínimo).
- Elige dispositivos con resistencia adecuada y comportamiento de energía estable para tu entorno.
- Dimensiona para metadatos + bloques pequeños esperados + overhead de snapshots + crecimiento.
- Planifica expansión: deja bahías/slots o carriles PCIe, y presupuesto para añadir otro mirror vdev más tarde.
- Crea alertas específicamente para utilización de special y tasas de error de dispositivos.
Checklist C: Plan de despliegue que no te despertará a las 3 a.m.
- Añade el vdev special y verifica la salud del pool.
- Habilita en un dataset primero con umbral conservador (8K o 16K).
- Mide antes/después en operaciones reales (checkout, untar, indexado, escaneos de directorio).
- Reescribe datos solo si es necesario, y limita las migraciones.
- Amplía el alcance dataset por dataset, vigilando la tendencia de utilización de special.
Checklist D: Operaciones continuas
- Revisión semanal de la tendencia de uso de special y crecimiento de snapshots.
- Scrubs programados y resultados de scrub revisados.
- Comprobaciones de salud de dispositivos (SMART/NVMe logs) integradas con umbrales de paginación.
- Validación trimestral: prueba un reemplazo de dispositivo y procedimiento de resilver (en un entorno seguro).
Tareas prácticas extra (enfocadas en ops)
Si quieres más tareas prácticas más allá de las doce principales, estas son las que suelen devolver valor rápidamente.
Task 15: Comprobar estadísticas de ARC (presión del caché de metadatos)
cr0x@server:~$ sudo arcstat 1 5
time read miss miss% dmis dm% pmis pm% mmis mm% arcsz c
09:52:01 812 102 12 48 47 40 39 14 14 96G 110G
Interpretación: si la tasa de aciertos de ARC cae durante tormentas de directorio, special será golpeado más. Eso está bien si está dimensionado y es rápido. Si no, verás latencia.
Task 16: Comprobación rápida del punto de montaje y mapeo de dataset (evita afinar el dataset equivocado)
cr0x@server:~$ mount | grep tank
tank/work on /tank/work type zfs (rw,xattr,noacl)
cr0x@server:~$ sudo zfs list -o name,mountpoint -r tank | grep -E '^tank/work|/tank/work'
tank/work /tank/work
Interpretación: en entornos reales, “la ruta” y “el dataset” derivan con el tiempo. Afina el dataset que realmente sirve la ruta que usan los usuarios.
Task 17: Comprobar conteo de snapshots y presión de retención
cr0x@server:~$ sudo zfs list -t snapshot -o name,used,creation -S creation | head
NAME USED CREATION
tank/work@daily-2025-12-24 1.2G Wed Dec 24 02:00 2025
tank/work@daily-2025-12-23 1.1G Tue Dec 23 02:00 2025
tank/work@daily-2025-12-22 1.3G Mon Dec 22 02:00 2025
Interpretación: los snapshots no son “gratis”. En datasets con mucho churn, los snapshots fijan bloques pequeños y metadatos.
Preguntas frecuentes
1) ¿Un vdev special es lo mismo que L2ARC?
No. L2ARC es una caché y puede ser descartada y reconstruida. El vdev special almacena bloques reales (metadatos y posiblemente datos de archivos pequeños). Si special se pierde sin redundancia, el pool puede perderse.
2) ¿Necesito bloques pequeños special si ya tengo mucha RAM para el ARC?
Quizá. ARC ayuda si tu conjunto de trabajo cabe y el patrón de acceso es amigable con caché. Pero las cargas de archivos pequeños a menudo sobrepasan ARC o implican escaneos en frío (piensa en runners de CI empezando desde cero). El vdev special mejora la “penalización por fallo de caché” haciendo que los fallos caigan en SSD/NVMe en lugar de HDD.
3) ¿Cuál es un buen valor inicial para special_small_blocks?
8K o 16K es un punto de partida sensato para muchos entornos. Si saltas a 64K, estás aceptando almacenar muchos datos reales en special. Eso puede ser correcto, pero deberías tomar esa decisión con cálculos de dimensionamiento, no por intuición.
4) ¿Puedo activarlo en todo el pool de una vez?
Técnicamente puedes establecerlo ampliamente en datasets, pero operativamente no deberías. Despliega por dataset, mide y vigila la tendencia de utilización de special. Special es un recurso compartido; un dataset ruidoso puede consumirlo.
5) ¿Por qué no mejoró el rendimiento después de establecer la propiedad?
Razones comunes: tus datos no se reescribieron (la propiedad no es retroactiva), tu cuello de botella son escrituras sync o la red,
la carga está limitada por CPU, o special está saturado/lento. Valida con zpool iostat -v durante la carga.
6) ¿Qué pasa si el vdev special se llena?
Cosas malas de forma aburrida: puede fallar la asignación para metadatos y bloques pequeños, y el pool puede volverse inestable o empezar a devolver errores. Trata “special está cerca de lleno” como una condición seria—añade capacidad o reduce los factores de crecimiento antes de que sea un incidente de producción.
7) ¿Puedo eliminar un vdev special más tarde?
En muchos despliegues reales, eliminar vdevs special no es sencillo o no está soportado de la manera que la gente espera. Planifica como si fuera permanente. Si necesitas reversibilidad, considera construir un pool nuevo y migrar.
8) ¿Debería también mover metadatos a SSD usando un diseño “solo metadata” del pool?
Los vdevs special son exactamente ese concepto, integrados en las clases de asignación de ZFS. Si todo tu pool es SSD, los vdevs special pueden no ayudar mucho. Si tu pool es HDD, special puede ser una gran ganancia sin mover todos los datos a SSD.
9) ¿Importa xattr=sa para este tema?
A menudo sí. Cuando los xattrs viven en el área de atributos del sistema, ZFS puede evitar objetos xattr separados en muchos casos,
reduciendo la E/S de metadatos. Eso acelera escaneos de directorio y cargas con muchos atributos y puede reducir la presión sobre
la capacidad e IOPS de special.
10) ¿Debería usar RAIDZ para el vdev special en lugar de mirrors?
Los mirrors son comunes para special porque mantienen la latencia baja y el comportamiento de resilver simple. RAIDZ puede ser viable por eficiencia de capacidad, pero debes sentirte cómodo con la amplificación de escrituras y la dinámica de reconstrucción. Para la mayoría de cargas sensibles a latencia con archivos pequeños, los mirrors special son la opción segura por defecto.
Conclusión
Los vdevs special de ZFS y special_small_blocks son una de las maneras más efectivas de cambiar la sensación de un
sistema de archivos que se ahoga en archivos pequeños. No solo “lo hacen más rápido”—mueven la parte más difícil de la
carga de trabajo (metadatos y E/S aleatoria diminuta) a medios que realmente pueden hacerlo.
Pero esto no es un almuerzo gratis, y no es una caché de la que puedas despreocuparte. El vdev special se convierte en infraestructura
crítica: necesita redundancia, monitorización, planificación de capacidad y disciplina operativa. Si lo tratas como un truco para ganar velocidad, tarde o temprano te tratará como a alguien que disfruta leer logs del kernel a medianoche.
Empieza conservador, mide honestamente y expande con intención. Así consigues el turbo sin romper la junta de la cabeza.