none, mq-deadline, bfq and kyber compared in practice
The disk I/O scheduler on Linux decides in what order read and write requests get passed to the block device, long before MySQL itself notices anything. On NVMe storage, the wrong choice can cost more latency than any MySQL configuration change could ever save.
Table of Contents
- 1. Why the I/O scheduler matters for databases
- 2. The multi queue block layer and available schedulers
- 3. none: the scheduler without reordering
- 4. mq-deadline: fairness with latency guarantees
- 5. bfq and kyber: when they still make sense
- 6. Determining and changing the active scheduler
- 7. Persistent configuration with udev rules
- 8. Making the difference measurable with fio
- 9. Schedulers compared directly
- 10. Summary
- 11. FAQ
1. Why the I/O scheduler matters for databases
Every write and read operation that MySQL sends to disk or SSD passes through the Linux block layer before reaching the actual storage device, and there the disk I/O scheduler decides in what order and with what priority these requests are actually executed. On rotating disks, this reordering was historically enormously important to minimize expensive head movements. On modern NVMe and SSD storage, this logic partially reverses, because the underlying medium no longer has any mechanical head movement at all.
For a MySQL server writing InnoDB redo logs, binlogs and data files in parallel, an unsuitable disk I/O scheduler can introduce extra latency that at first glance looks like a database problem but actually originates in the block layer. Especially for transaction commits waiting on fsync(), unnecessary scheduler latency adds directly to the perceived response time of Magento checkouts.
This article shows which schedulers the modern Linux kernel offers, which one is the right choice for NVMe based database servers, and how to make that decision with real benchmarks instead of gut feeling.
2. The multi queue block layer and available schedulers
Since Linux kernel 4.x, the so called multi queue block layer (blk-mq) has replaced the older single queue schedulers such as cfq and deadline. The multi queue approach distributes I/O requests across multiple hardware queues in parallel, which on modern NVMe devices with many parallel queues exploits the actual strength of the medium. For this new layer, current distributions typically offer four options: none, mq-deadline, bfq and kyber.
Which disk I/O scheduler is currently active for a block device is shown by the virtual file at /sys/block/[device]/queue/scheduler, where the active scheduler is marked in square brackets. This file can also be written at runtime to switch the scheduler without a reboot, though only temporarily until the next boot unless a persistent rule is configured.
# Show the currently active scheduler for an NVMe device
cat /sys/block/nvme0n1/queue/scheduler
# none [mq-deadline] bfq kyber
# Switch the scheduler at runtime (temporary until reboot)
echo none > /sys/block/nvme0n1/queue/scheduler
# List all block devices with their current scheduler
for dev in /sys/block/*/queue/scheduler; do
echo "$dev: $(cat "$dev")"
done
3. none: the scheduler without reordering
The none scheduler passes I/O requests directly to the block device without any reordering, with minimal CPU overhead in the kernel. For NVMe SSDs, which internally already have highly parallel command queues and their own controller with wear leveling logic, this decision is usually the right one: the device can organize incoming requests more efficiently itself than an additional software scheduler in the kernel ever could.
For a dedicated MySQL server on NVMe storage that does not run other competing I/O intensive services on the same device, none in practice regularly delivers the lowest latency for random read and write access, exactly the access pattern InnoDB generates for redo logs and buffer pool flushes. Skipping reordering does not mean skipping fairness, because modern NVMe controllers already have enough internal parallelism to efficiently serve multiple simultaneous requests.
#!/usr/bin/env bash
# set-scheduler-none.sh — apply 'none' scheduler to a dedicated NVMe database disk
set -euo pipefail
DEVICE="nvme0n1"
SCHED_PATH="/sys/block/${DEVICE}/queue/scheduler"
if [[ ! -w "$SCHED_PATH" ]]; then
echo "[ERROR] $SCHED_PATH not writable, check device name" >&2
exit 1
fi
echo none > "$SCHED_PATH"
echo "[OK] Active scheduler for $DEVICE: $(cat "$SCHED_PATH")"
4. mq-deadline: fairness with latency guarantees
The mq-deadline scheduler guarantees every request a maximum wait time before it must be executed, regardless of how many newer requests have arrived in the meantime. This prevents individual requests from starving under a constant stream of newer arrivals, a scenario that does occur under heavily parallel access. For SATA SSDs and older rotating disks, which lack the highly parallel internal queue management of NVMe devices, mq-deadline remains the more solid choice.
On shared storage too, for example when MySQL and other services share the same block device, mq-deadline provides a more balanced latency distribution between competing workloads than none. The disk I/O scheduler mq-deadline is therefore the current default for many distributions on systems that are not exclusively NVMe based, because it delivers robust behavior across a wide range of storage types without requiring the fine tuning that none sometimes needs on mixed storage.
5. bfq and kyber: when they still make sense
The bfq scheduler (Budget Fair Queueing) distributes available I/O bandwidth fairly between processes, with configurable priorities per process or cgroup. For a dedicated database server where practically only MySQL generates relevant I/O, this fairness between processes is usually unnecessary and costs additional CPU overhead without any measurable benefit. On a mixed server that also runs backup jobs, log rotation and other I/O intensive background processes alongside MySQL, however, bfq can prevent a single backup run from driving up database query latency.
The kyber scheduler specifically targets latency sensitive workloads with mixed read and write patterns and tries to maintain configurable target latencies for reads and writes separately. In practice, kyber rarely shows a clear advantage over none on pure database workloads, but on mixed workloads with many small synchronous reads alongside large sequential writes it can be an interesting alternative worth its own benchmark.
# Configure bfq weight per cgroup for a shared server running MySQL + backups
# /sys/fs/cgroup/io.bfq.weight (cgroup v2, requires bfq scheduler active)
echo "default 500" > /sys/fs/cgroup/mysql.slice/io.bfq.weight
echo "default 100" > /sys/fs/cgroup/backup.slice/io.bfq.weight
# MySQL now receives roughly 5x the I/O bandwidth priority over backup jobs
6. Determining and changing the active scheduler
Before switching a disk I/O scheduler, it is worth checking whether the block device in question is actually a real rotating disk or a solid state device. The file /sys/block/[device]/queue/rotational shows a value of 0 for an SSD or NVMe device, and 1 for a classic rotating disk. This information helps to assign the right scheduler recommendation in the first place, because a scheduler that makes sense for rotating disks can be counterproductive on an NVMe SSD.
The switch itself is done by simply writing the scheduler name into the scheduler file, without requiring a restart of the system or the MySQL service. This change takes effect immediately for all new I/O requests to the device, running requests are not interrupted. For production systems it is still advisable to perform the switch outside of peak load hours and observe the effect using iostat.
7. Persistent configuration with udev rules
A change made via echo into /sys/block/.../scheduler is lost on every reboot, because the kernel resets the scheduler back to the kernel or distribution default during boot. For a permanent configuration, a udev rule is the most reliable way to correctly set the disk I/O scheduler automatically on every boot and whenever the device is added, independent of the exact device name, which can theoretically change between reboots.
The rule matches on ID_MODEL or the kernel subsystem assignment and sets the desired scheduler value as soon as the device is detected by the kernel. This approach is more robust than a simple systemd boot hook, because udev also applies on hot plug events, for example when an NVMe card is newly detected in a running system.
# /etc/udev/rules.d/60-io-scheduler.rules
# Persist 'none' scheduler for all NVMe devices across reboots
ACTION=="add|change", KERNEL=="nvme[0-9]n[0-9]", ATTR{queue/scheduler}="none"
# Persist 'mq-deadline' for rotational (spinning disk) devices
ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="1", ATTR{queue/scheduler}="mq-deadline"
# Reload rules without reboot:
# udevadm control --reload-rules
# udevadm trigger --type=devices --action=change
8. Making the difference measurable with fio
Claims about the benefits of a disk I/O scheduler without an actual measurement carry little weight, because the real impact depends heavily on the specific hardware, RAID layout and I/O pattern of the application. The fio tool allows recreating the access pattern typical for InnoDB: many small, random 16 kilobyte writes for redo logs, mixed with larger sequential reads for table scans.
A benchmark run with an identical fio profile, once per scheduler under test, delivers comparable latency and throughput values on which a well founded decision can be based, instead of relying on generic recommendations from the internet that may have been measured on entirely different hardware.
#!/usr/bin/env bash
# benchmark-io-scheduler.sh — compare schedulers with a MySQL-like access pattern
set -euo pipefail
DEVICE="/dev/nvme0n1"
SCHED_PATH="/sys/block/nvme0n1/queue/scheduler"
for scheduler in none mq-deadline bfq kyber; do
echo "$scheduler" > "$SCHED_PATH" 2>/dev/null || continue
echo "=== Testing scheduler: $scheduler ==="
fio --name=innodb-redo-sim \
--filename="$DEVICE" \
--rw=randwrite \
--bs=16k \
--iodepth=32 \
--direct=1 \
--runtime=30 \
--time_based \
--group_reporting \
--output-format=terse | tee -a "/tmp/fio-${scheduler}.log"
done
9. Schedulers compared directly
The table below summarizes which disk I/O scheduler is the better choice for which storage and workload profile on database servers.
| Scheduler | Suitable storage | CPU overhead | Recommendation for MySQL |
|---|---|---|---|
| none | Dedicated NVMe SSD | Minimal | First choice for single purpose DB servers |
| mq-deadline | SATA SSD, HDD, mixed storage | Low | Solid default without fine tuning |
| bfq | Shared storage with competing workloads | High | When backup jobs run on the same device |
| kyber | Latency sensitive mixed workloads | Medium | Use only after your own fio benchmark |
For the clear majority of Magento database servers on dedicated NVMe hardware, none delivers the lowest latency with minimal CPU overhead. The disk I/O scheduler mq-deadline remains the robust alternative for mixed or older storage, while bfq and kyber only justify a switch for specific requirements and after your own benchmark.
Mironsoft
Linux storage tuning and database performance for Magento
Is the wrong I/O scheduler blocking your database performance?
We analyze your storage setup, benchmark the available schedulers with realistic MySQL access patterns, and set up a persistent, production ready configuration.
Storage analysis
Check NVMe, SSD and HDD setups for the right scheduler
fio benchmarking
Measure realistic InnoDB access patterns against different schedulers
Persistent configuration
udev rules for a permanently correct scheduler after every reboot
10. Summary
The right disk I/O scheduler primarily depends on the storage type: none offers dedicated NVMe database servers the lowest latency with the least overhead, because modern NVMe controllers efficiently handle the internal organization of requests themselves. mq-deadline remains the robust standard for SATA SSDs, rotating disks and mixed storage, because it provides fairness without extensive tuning.
Specialized schedulers like bfq and kyber are only worthwhile in specific scenarios with competing workloads on the same device or particularly latency sensitive mixed patterns, and the decision should always be based on your own fio benchmark rather than generic recommendations. Persistent udev rules ensure the chosen configuration survives a reboot.
Disk I/O Scheduler for Database Servers — The Essentials at a Glance
NVMe servers
Use none, the controller organizes requests more efficiently than the kernel scheduler.
Mixed storage
mq-deadline as a robust default for SATA SSDs and rotating disks.
Measure, don't guess
Benchmark each scheduler individually with fio using an InnoDB-like access pattern.
Persistence
Use udev rules for a durable, reboot-proof scheduler configuration.