Linux I/O Scheduler Tuning: mq-deadline, none, BFQ
16 min de lectura - 1 de junio de 2026

Cómo elegir y ajustar el programador de E/S de Linux adecuado para cargas de trabajo NVMe, SATA y HDD, con comandos sysfs, reglas udev y pasos de evaluación comparativa fio.
Ajuste del programador de E/S de Linux: mq-deadline, none y BFQ
El programador de E/S de Linux decide el orden en el que las solicitudes de lectura y escritura llegan a tu dispositivo de almacenamiento, y la elección correcta depende casi por completo de tu hardware. Utiliza none para NVMe, mq-deadline para SSD y HDD SATA que ejecutan cargas de trabajo mixtas, y bfq cuando necesites evitar que un proceso acabe con los recursos de los demás. Esta guía explica cómo funcionan los tres programadores principales, cómo elegir el más adecuado para tu carga de trabajo y cómo ajustarlos y verificar el resultado.
Si quieres un tutorial práctico antes de leer, este vídeo explica los conceptos básicos para cambiar y probar programadores desde el terminal.
En qué se diferencian mq-deadline, none y BFQ
Cada programador gestiona las solicitudes con una estrategia diferente. Saber en qué se diferencian es lo que te permite elegir deliberadamente en lugar de ejecutar lo que el kernel haya seleccionado al arrancar.
mq-deadline
El mq-deadline programador se asegura de que ninguna solicitud espere indefinidamente. Mantiene colas ordenadas separadas para lecturas y escrituras, ordenándolas por dirección de bloque lógico (LBA) para reducir el tiempo de búsqueda, y aplica plazos: 500 ms para lecturas y 5 segundos para escrituras por defecto. Cuando una solicitud alcanza su plazo, salta al principio de la cola.
Las lecturas tienen prioridad sobre las escrituras, ya que las lecturas suelen bloquear la aplicación, mientras que las escrituras se gestionan de forma asíncrona. Para evitar que las escrituras se vean totalmente bloqueadas, el programador atiende un lote de escrituras vencidas tras un número determinado de lecturas. El resultado es una baja latencia constante, lo que lo hace muy adecuado para servidores de bases de datos y cualquier carga de trabajo que combine lecturas y escrituras.
ninguna
El none programador no hace prácticamente nada. Pasa las solicitudes directamente al dispositivo en orden de «primero en entrar, primero en salir», sin reordenar, fusionar ni priorizar. Esto se adapta a las unidades NVMe modernas, que gestionan sus propias colas internas y pueden rastrear decenas de miles de solicitudes en curso a la vez. Eliminar la capa de programación de software proporciona la ruta más corta posible desde la aplicación hasta el dispositivo, que es exactamente lo que necesitan las cargas de trabajo NVMe de alto rendimiento.
El inconveniente es que esto solo funciona cuando el hardware puede programar de forma inteligente por sí mismo. En discos duros (HDD) o SSD SATA con colas poco profundas, omitir la reordenación por software suele empeorar el rendimiento, en lugar de mejorarlo.
BFQ
BFQ (Budget Fair Queuing) da prioridad a la equidad. En lugar de intervalos de tiempo, asigna a cada proceso un presupuesto medido en sectores de disco. Las lecturas secuenciales de gran tamaño reciben presupuestos mayores para mantener el rendimiento, mientras que las tareas sensibles a la latencia reciben presupuestos más reducidos para que se atiendan rápidamente, y un bucle de retroalimentación ajusta los presupuestos sobre la marcha.
BFQ mantiene la capacidad de respuesta de las tareas interactivas incluso bajo una carga pesada, de modo que la reproducción de vídeo o una consulta a la base de datos se mantienen fluidas mientras se ejecuta una transferencia de archivos de gran tamaño en segundo plano. Esa equidad tiene un coste en CPU. Su sobrecarga por solicitud es de aproximadamente 1,9 microsegundos, unas tres veces la de mq-deadline, y en un núcleo ARM más lento esa sobrecarga limita el rendimiento muy por debajo de lo que el mismo programador alcanza en un chip x86 rápido. En servidores donde el rendimiento bruto y la eficiencia de la CPU son lo más importante, esa compensación es difícil de justificar.
| Programador | Algoritmo | Sobrecarga de CPU | Mejor hardware | Objetivo principal |
|---|---|---|---|---|
mq-deadline | LBA ordenados con plazos | Bajo (~0,7 µs/solicitud) | SSD SATA, discos duros, discos virtuales | Latencia baja predecible |
none | FIFO, sin reordenación | Insignificante | SSD NVMe | Rendimiento máximo |
bfq | Presupuestos de reparto proporcional | Moderado (~1,9 µs/solicitud) | HDD, sistemas compartidos y de escritorio | Equidad y capacidad de respuesta |
Elegir un programador adecuado para su carga de trabajo
Hay dos factores que determinan el programador adecuado: tu hardware de almacenamiento y el patrón de acceso de tu aplicación. Empieza por el hardware. Si el dispositivo ya reordena las solicitudes, como una unidad NVMe con firmware compatible, la programación por software solo añade sobrecarga, por lo que none gana. En los discos duros giratorios, donde predomina el tiempo de búsqueda, la reordenación por software reduce la latencia, por lo que mq-deadline o bfq son las mejores opciones. Los SSD SATA se sitúan en un término medio: son más rápidos que los discos duros, pero carecen de las colas profundas de NVMe, por lo que mq-deadline encaja.
La misma lógica se aplica cuando ya hay otro elemento programando por ti. Las máquinas virtuales invitadas en virtio-blk dependen del host para programar la E/S, y los controladores RAID de hardware con caché de reescritura optimizan su propio orden. En ambos casos none se evita pagar dos veces por el trabajo.
El patrón de acceso es el segundo factor. Una base de datos que realiza miles de lecturas aleatorias de 4K por segundo no tiene nada en común con un trabajo de entrenamiento que transmite grandes bloques secuenciales desde una matriz NVMe, y ambas requieren programadores diferentes. La tabla siguiente asigna las cargas de trabajo comunes a un punto de partida.
| Carga de trabajo | Almacenamiento | Programador | Motivo |
|---|---|---|---|
| Entrenamiento de IA/ML | SSD NVMe | none | Alto rendimiento secuencial; el firmware gestiona las colas |
| Base de datos OLTP | SSD NVMe | none | E/S aleatoria de baja latencia; evita la sobrecarga del software |
| Base de datos OLTP | SSD SATA | mq-deadline | Evita la saturación de escritura; latencia de cola predecible |
| Almacén de datos / OLAP | NVMe / SSD rápido | none | Colas paralelas profundas; rendimiento máximo |
| Alojamiento web general | SSD SATA / HDD | mq-deadline | Respuesta constante para E/S mixta de archivos pequeños |
| Alojamiento compartido / multitenant | HDD / SSD | bfq | Equidad entre inquilinos; evita la monopolización de E/S |
| Máquina virtual invitada | virtio-blk | none | El host ya realiza la programación; la doble programación desperdicia CPU |
| Copia de seguridad / archivo | HDD | mq-deadline | Rendimiento secuencial con prevención de agotamiento |
Hay una excepción que merece la pena destacar. Incluso en NVMe, si la latencia de cola en p99 o p999 es la métrica que te interesa, como en los sistemas financieros, mq-deadline puede superar none imponiendo plazos estrictos y evitando las solicitudes retrasadas ocasionales.
Modificación y ajuste de los parámetros del programador
Tanto el cambio de programadores como el ajuste de sus parámetros se realizan a través de sysfs, sin necesidad de reiniciar el sistema para probar un cambio.
Cambio del programador activo
Comprueba qué está disponible para un dispositivo, donde el valor entre paréntesis es el activo:
cat /sys/block/sda/queue/schedulerCambie a un programador diferente en tiempo de ejecución. Esto surte efecto inmediatamente, pero no se mantiene tras un reinicio:
echo bfq | sudo tee /sys/block/sda/queue/schedulerSi bfq no aparece en la lista, cargue primero el módulo:
sudo modprobe bfqPara que la elección sea permanente, utilice una regla udev en lugar del antiguo elevator= , que ya no cambia el programador en RHEL 9 y versiones similares. Esta regla establece mq-deadline para todos los discos SCSI no rotativos en /etc/udev/rules.d/60-io-scheduler.rules:
ACTION=="add|change", SUBSYSTEM=="block", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="0", ATTR{queue/scheduler}="mq-deadline"Recargue y aplíquela sin reiniciar:
sudo udevadm control --reload-rules && sudo udevadm triggerEn sistemas basados en RHEL, los perfiles de TuneD realizan la misma tarea mediante perfiles para todo el sistema en lugar de reglas por dispositivo.
Parámetros que vale la pena ajustar
Cada programador expone sus parámetros ajustables en /sys/block/<device>/queue/iosched/. En mq-deadline, los plazos son las principales palancas. Las bases de datos sensibles a la latencia en SSD SATA se benefician de plazos más cortos:
echo 100 | sudo tee /sys/block/sda/queue/iosched/read_expire
echo 1000 | sudo tee /sys/block/sda/queue/iosched/write_expireEn bfq en sistemas de alto rendimiento, desactivar la heurística de latencia aumenta el rendimiento:
echo 0 | sudo tee /sys/block/sda/queue/iosched/low_latency
echo 0 | sudo tee /sys/block/sda/queue/iosched/slice_idle| Programador | Parámetro | Predeterminado | Objetivo de ajuste |
|---|---|---|---|
mq-deadline | read_expire | 500 ms | Menor para una respuesta de lectura más rápida |
mq-deadline | write_expire | 5000 ms | Reduzca este valor para reducir la latencia de escritura |
mq-deadline | writes_starved | 3 | Aumentar para cargas con gran volumen de lectura |
mq-deadline | fifo_batch | 16 | Establecer en 1 para una latencia mínima |
bfq | low_latency | 1 | Establecer en 0 para obtener el máximo rendimiento |
bfq | slice_idle | 8 ms | Establezca en 0 para SSD o RAID |
bfq | strict_guarantees | 0 | Establecer en 1 para un reparto estricto del ancho de banda |
Para el alojamiento compartido, BFQ combina bien con cgroups v2. Asignar io.weight valores te permite asignar a un proceso de base de datos, por ejemplo, diez veces más ancho de banda de E/S que a una tarea de copia de seguridad, de modo que el trabajo en segundo plano no sature el tráfico interactivo. Independientemente de los cambios que realices, el mayor coste por solicitud de BFQ se acumula en sistemas con alta carga de CPU y muchos IOPS, así que realiza pruebas de rendimiento antes de aplicar los cambios.
Verificación del rendimiento tras el ajuste
Captura siempre una referencia antes de cambiar nada. Sin ella, no hay forma de saber si un ajuste ha servido de algo.
fio es la herramienta estándar para ello. Reproduce patrones de carga de trabajo específicos mediante el tamaño de bloque, la profundidad de la cola y la configuración del motor de E/S. Utilícelo siempre --direct=1 para que omita la caché de páginas y mida directamente el programador y el dispositivo, en lugar de las lecturas en caché. Adapta la prueba a la carga de trabajo real:
| Carga de trabajo | Parámetros de fio |
|---|---|
| Base de datos OLTP | --rw=randread --bs=4k --iodepth=32 --direct=1 |
| Almacén de datos | --rw=read --bs=1m --iodepth=32 --direct=1 |
| Registro de reescritura anticipada / registro de rehacer | --rw=write --bs=4k --iodepth=1 --direct=1 |
| Almacenamiento de objetos | --rw=randrw --bs=64k --iodepth=64 --direct=1 |
Ejecute la misma prueba con iodepth valores del 1 al 256 para encontrar el punto de saturación del dispositivo, la profundidad en la que las IOPS dejan de aumentar y la latencia se dispara. Para la monitorización en tiempo real tras un cambio, iostat -x 1 informa de las métricas que importan: r_await y w_await para la latencia de finalización de lectura y escritura, aqu-sz para la profundidad media de la cola, y %util para la utilización del dispositivo. Cuando %util se sitúa cerca del 100 %, el hardware es el límite y ningún cambio en el programador servirá de ayuda.
Para separar el coste del software del coste del hardware, ejecute blktrace con btt. Divide la latencia en Q2D, el tiempo que se pasa en la cola del software, y D2C, el tiempo que tarda el dispositivo en atender la solicitud. Si predomina Q2D, el programador es su cuello de botella. Si predomina D2C, lo es el hardware.
Una cosa a tener en cuenta al leer los resultados: la elección del programador determina principalmente la cola de la distribución de la latencia, no la mediana. Cambiar de none a mq-deadline en NVMe podría aumentar la latencia mediana en unos pocos microsegundos, al tiempo que reduce a la mitad la latencia p99 y p999. Para los servicios orientados al usuario sujetos a SLA, esa compensación casi siempre merece la pena, por lo que medir la latencia de la cola, y no el rendimiento medio, es el objetivo del ejercicio.
Elegir el programador adecuado
El ajuste del programador consiste en adaptar el algoritmo al hardware y al patrón de acceso, y luego verificarlo mediante mediciones. La versión resumida:
- NVMe: utilice
noney deja que el firmware se encargue de las colas. - SSD SATA y HDD con E/S mixta: utilice
mq-deadlinepara obtener una latencia predecible. - Hosts compartidos o multitenant: utilice
bfqpara evitar que una carga de trabajo agote los recursos del resto. - Latencia de cola, no mediana: los cambios del programador se ven en p99 y p999, así que eso es lo que hay que medir.
- Hazlo persistente: utiliza reglas udev o TuneD, nunca el parámetro
elevator=.
Sacar el máximo partido a cualquier programador empieza por un hardware que pueda seguirle el ritmo. Si necesitas servidores con tecnología NVMe diseñados para cargas de trabajo de alto rendimiento y baja latencia, explora las opciones de VPS de FDC.
Por qué es importante tener un VPS potente y sin contador
Un VPS sin contador proporciona un ancho de banda fijo a una velocidad de puerto fija. En qué se diferencia de los planes con contador, cuándo compensa y qué hay que comprobar antes de comprarlo.
7 min de lectura - 9 de mayo de 2025
Gestión de memoria en Linux: Swap, OOM Killer y Cgroups
12 min de lectura - 31 de mayo de 2026