fio (Flexible I/O Tester) es la herramienta de referencia para medir el rendimiento de almacenamiento en Linux. Permite definir con precisión el patrón de acceso (aleatorio o secuencial), el tamaño de bloque, la profundidad de cola y el número de procesos, y devuelve IOPS, ancho de banda y percentiles de latencia. En este tutorial instalarás fio en Ubuntu 24.04, medirás IOPS aleatorias y rendimiento secuencial, evaluarás la latencia de fsync que sufren las bases de datos y guardarás las pruebas en un archivo de trabajo reutilizable.

Requisitos previos

  • Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath.
  • Un usuario no root con privilegios sudo.
  • Al menos 5 GB libres en el sistema de archivos que quieras medir.

Paso 1: Instalar fio

fio está en los repositorios oficiales de Ubuntu:

sudo apt update
sudo apt install fio

Comprueba la versión:

fio --version
fio-3.36

Identifica en qué disco y sistema de archivos está el directorio donde harás las pruebas:

lsblk
df -h ~

Crea un directorio de trabajo en ese sistema de archivos:

mkdir ~/fio-test

Paso 2: Entender los parámetros clave

Todas las pruebas de esta guía combinan los mismos parámetros. Conviene entenderlos antes de lanzar nada:

ParámetroQué controlaValores habituales
--rwPatrón de accesorandread, randwrite, randrw, read, write
--bsTamaño de bloque por operación4k para IOPS, 1M para ancho de banda
--iodepthOperaciones en vuelo por proceso1 para latencia, 32-64 para máximo rendimiento
--numjobsProcesos en paralelo1-4
--ioengineCómo se envían las operacioneslibaio (asíncrono), sync
--direct=1Salta la caché de páginas del kernelSiempre, salvo que quieras medir la caché
--sizeTamaño del archivo de prueba4G o más
--runtime y --time_basedDuración fija de la prueba60 segundos

Sin --direct=1, las lecturas pueden servirse desde la RAM y obtendrás cifras irreales.

Paso 3: Medir IOPS de lectura aleatoria

Las lecturas aleatorias de 4 KB son el patrón más exigente para un disco y el que más se parece a una base de datos o a muchos usuarios leyendo archivos pequeños a la vez:

fio --name=randread --directory=$HOME/fio-test --size=4G \
  --rw=randread --bs=4k --iodepth=64 --numjobs=1 \
  --ioengine=libaio --direct=1 --runtime=60 --time_based --group_reporting

La primera vez, fio crea el archivo de 4 GB (Laying out IO file), lo que tarda unos segundos. Al terminar verás un bloque como este:

randread: (groupid=0, jobs=1): err= 0: pid=4121: Thu Sep 25 10:12:44 2026
  read: IOPS=48.2k, BW=188MiB/s (197MB/s)(11.0GiB/60001msec)
    slat (usec): min=2, max=412, avg= 4.18, stdev= 2.07
    clat (usec): min=112, max=9870, avg=1322.40, stdev=301.55
     lat (usec): min=118, max=9874, avg=1326.71, stdev=301.60
    clat percentiles (usec):
     |  1.00th=[  783],  5.00th=[  914], 10.00th=[  996], 20.00th=[ 1106],
     | 30.00th=[ 1172], 40.00th=[ 1237], 50.00th=[ 1303], 60.00th=[ 1369],
     | 70.00th=[ 1434], 80.00th=[ 1532], 90.00th=[ 1680], 95.00th=[ 1811],
     | 99.00th=[ 2212], 99.50th=[ 2442], 99.90th=[ 3261], 99.95th=[ 3818],
     | 99.99th=[ 6128]

Cómo leerlo:

  • IOPS: operaciones por segundo. Es la cifra principal en pruebas de 4 KB.
  • BW: ancho de banda. En bloques pequeños es simplemente IOPS multiplicado por 4 KB.
  • clat: latencia de finalización de cada operación. slat es el tiempo de envío y lat la suma de ambos.
  • Percentiles: 99.00th=[ 2212] significa que el 99 % de las lecturas tardó 2,2 ms o menos. Los percentiles altos (99,9 % y 99,99 %) muestran los picos que notan las aplicaciones.

Como referencia orientativa, un disco mecánico da del orden de 100-200 IOPS aleatorias, un SSD SATA decenas de miles y un NVMe cientos de miles. En un VPS, el resultado suele estar marcado por los límites de IOPS del plan y no solo por el hardware.

Paso 4: Medir IOPS de escritura aleatoria y cargas mixtas

La escritura aleatoria usa la misma sintaxis cambiando --rw:

fio --name=randwrite --directory=$HOME/fio-test --size=4G \
  --rw=randwrite --bs=4k --iodepth=64 --numjobs=1 \
  --ioengine=libaio --direct=1 --runtime=60 --time_based --group_reporting

La mayoría de aplicaciones reales mezclan lecturas y escrituras. Esta prueba reparte un 70 % de lecturas y un 30 % de escrituras:

fio --name=randrw --directory=$HOME/fio-test --size=4G \
  --rw=randrw --rwmixread=70 --bs=4k --iodepth=64 --numjobs=1 \
  --ioengine=libaio --direct=1 --runtime=60 --time_based --group_reporting

La salida incluye una línea read: y otra write: con sus propias IOPS y latencias.

Paso 5: Medir el rendimiento secuencial

Copias de seguridad, vídeo o importaciones masivas trabajan con bloques grandes en orden. Para medir el ancho de banda máximo usa bloques de 1 MB:

fio --name=seqread --directory=$HOME/fio-test --size=4G \
  --rw=read --bs=1M --iodepth=16 --numjobs=1 \
  --ioengine=libaio --direct=1 --runtime=60 --time_based --group_reporting
  read: IOPS=1742, BW=1742MiB/s (1827MB/s)(102GiB/60003msec)

En pruebas secuenciales la cifra relevante es BW. Para escritura secuencial, cambia --rw=read por --rw=write.

Paso 6: Medir la latencia de fsync para bases de datos

Las bases de datos y sistemas como etcd confirman cada transacción con fsync o fdatasync, y esperan a que el disco confirme que los datos son persistentes. Esa latencia importa más que las IOPS máximas. Esta prueba escribe bloques pequeños y fuerza fdatasync tras cada escritura, con un único proceso y sin cola:

fio --name=fsync-test --directory=$HOME/fio-test --size=256M \
  --rw=write --bs=4k --ioengine=sync --fdatasync=1 --runtime=60 --time_based

Busca en la salida la sección de sincronización:

  fsync/fdatasync/sync_file_range:
    sync (usec): min=210, max=8412, avg=402.18, stdev=190.33
    sync percentiles (usec):
     |  1.00th=[  245],  5.00th=[  265], 10.00th=[  277], 20.00th=[  302],
     | 50.00th=[  359], 90.00th=[  562], 95.00th=[  652], 99.00th=[ 1057],
     | 99.90th=[ 2966], 99.95th=[ 3884], 99.99th=[ 7046]

Un percentil 99 por debajo de 10 ms es el mínimo que se suele recomendar para etcd. Valores de pocos cientos de microsegundos son típicos de un NVMe local.

Paso 7: Crear un archivo de trabajo reutilizable

En lugar de repetir parámetros en la línea de órdenes, fio acepta archivos de trabajo en formato INI. La sección [global] define los valores comunes y cada sección siguiente es una prueba. stonewall hace que cada prueba espere a que termine la anterior, para que no se ejecuten a la vez.

Crea el archivo:

nano ~/disk-benchmark.fio

Añade este contenido:

[global]
directory=${HOME}/fio-test
size=4G
ioengine=libaio
direct=1
runtime=60
time_based
group_reporting

[rand-read-4k]
rw=randread
bs=4k
iodepth=64
stonewall

[rand-write-4k]
rw=randwrite
bs=4k
iodepth=64
stonewall

[mixed-70-30-4k]
rw=randrw
rwmixread=70
bs=4k
iodepth=64
stonewall

[seq-read-1m]
rw=read
bs=1M
iodepth=16
stonewall

[seq-write-1m]
rw=write
bs=1M
iodepth=16
stonewall

fio expande variables de entorno con la sintaxis ${VAR}, por eso ${HOME} apunta a tu directorio personal. Ejecuta el conjunto completo, que tarda unos 5 minutos:

fio ~/disk-benchmark.fio

Cada prueba aparece con su nombre en la salida, seguida de su bloque de IOPS, ancho de banda y latencia.

Paso 8: Guardar los resultados en JSON

Para comparar ejecuciones, por ejemplo antes y después de cambiar de plan o de sistema de archivos, guarda la salida en JSON:

fio --output-format=json --output=$HOME/fio-$(date +%F).json ~/disk-benchmark.fio

Instala jq y extrae las IOPS de lectura y el percentil 99 de latencia (en microsegundos) de cada prueba:

sudo apt install jq
jq -r '.jobs[] | [.jobname, (.read.iops|floor), (.write.iops|floor), ((.read.clat_ns.percentile["99.000000"] // 0)/1000|floor)] | @tsv' ~/fio-$(date +%F).json
rand-read-4k	48213	0	2212
rand-write-4k	0	31877	0
mixed-70-30-4k	25110	10762	3032
seq-read-1m	1742	0	9765
seq-write-1m	0	1508	0

Las columnas son: nombre de la prueba, IOPS de lectura, IOPS de escritura y percentil 99 de lectura en microsegundos.

Paso 9: Limpiar los archivos de prueba

fio no borra los archivos que crea. Elimínalos al terminar para recuperar el espacio:

rm -rf ~/fio-test

Solución de problemas

  • fio: looks like your file system does not support direct=1/buffered=0: el sistema de archivos (por ejemplo tmpfs) no admite O_DIRECT. Usa un directorio en un disco real.
  • Resultados de lectura imposibles, de varios GB/s en un disco modesto: falta --direct=1, o el archivo de prueba es tan pequeño que cabe en la caché del controlador. Usa un --size de varios GB.
  • No space left on device: --size supera el espacio libre. Reduce el tamaño o elige otro directorio.
  • Las IOPS se estancan al subir --iodepth: has alcanzado el límite del dispositivo o del plan. Subir la cola a partir de ahí solo aumenta la latencia.

Conclusión

Ahora puedes medir IOPS aleatorias, ancho de banda secuencial y latencia de fsync con fio, y tienes un archivo de trabajo que repite las mismas pruebas y guarda los resultados en JSON. Como siguientes pasos, puedes medir la base de datos completa con pgbench o sysbench oltp, comparar sistemas de archivos (ext4 frente a XFS) con el mismo archivo de trabajo, o vigilar el disco en producción con iostat -x 1 del paquete sysstat.