Linux I/O 调度器调整:mq-deadline、none、BFQ

16 分钟阅读 - 2026年6月1日

hero section cover
目录
  • Linux I/O 调度器调优:mq-deadline、none 和 BFQ
  • mq-deadline、none 和 BFQ 的区别
  • 根据工作负载选择合适的调度器
  • 更改和调整调度器参数
  • 调优后的性能验证
  • 选择合适的调度器
分享

如何为 NVMe、SATA 和 HDD 工作负载选择和调整正确的 Linux I/O 调度器,包括 sysfs 命令、udev 规则和 fio 基准测试步骤。

Linux I/O 调度器调优:mq-deadline、none 和 BFQ

Linux I/O 调度器决定读写请求到达存储设备的顺序,而正确的选择几乎完全取决于您的硬件。使用 none 用于 NVMe, mq-deadline 适用于运行混合工作负载的 SATA SSD 和 HDD, bfq 当需要防止某个进程独占资源导致其他进程资源饥饿时。本指南将介绍这三种主要调度器的运作原理、如何根据工作负载选择合适的调度器,以及如何进行调优并验证结果。

若您希望在阅读前先进行实践操作,本视频介绍了如何通过终端切换和测试调度程序的基础知识。


 

mq-deadline、none 和 BFQ 的区别

每种调度器处理请求的策略各不相同。了解它们之间的差异,才能让你有针对性地进行选择,而不是在系统启动时直接使用内核默认选定的调度器。

mq-deadline

mq-deadline 调度器确保没有请求会无限期等待。它为读写操作分别维护独立的排序队列,按逻辑块地址(LBA)排序以减少寻道时间,并强制执行时限:默认情况下,读取请求的时限为 500 毫秒,写入请求为 5 秒。当请求达到其时限时,它会跳到队列的队首。

读取请求优先于写入请求,因为读取通常会阻塞应用程序,而写入则以异步方式处理。为了防止写入请求完全被饿死,调度器会在处理完一定数量的读取请求后,批量处理一批超时的写入请求。其结果是保持一致的低延迟,这使其非常适合数据库服务器以及任何混合了读写操作的工作负载。

none 调度器几乎不做任何处理。它会按照先入先出(FIFO)的顺序将请求直接传递给设备,不进行任何重新排序、合并或优先级设置。这非常适合现代 NVMe 驱动器,因为它们能够管理自身的内部队列,并能同时跟踪数万个正在处理中的请求。移除软件调度层提供了从应用程序到设备的最短路径,这正是高吞吐量 NVMe 工作负载所需要的。

但需注意的是,这仅在硬件能够自主智能调度时才有效。对于队列较浅的 HDD 或 SATA SSD,跳过软件重排序通常会导致性能下降,而非提升。

BFQ

BFQ(预算公平队列)将公平性置于首位。它不采用时间片机制,而是为每个进程分配以磁盘扇区为单位的预算。大型顺序读取任务获得更大预算以维持吞吐量,而对延迟敏感的任务则获得较小预算以便快速处理,同时运行中的反馈循环会动态调整这些预算。

即使在高负载下,BFQ也能保持交互式任务的响应性,因此当后台进行大文件传输时,视频播放或数据库查询仍能保持流畅。这种公平性需要消耗CPU资源。其每次请求的开销约为1.9微秒,大约是mq-deadline的三倍;在较慢的ARM核心上,该开销会将吞吐量限制在远低于相同调度程序在快速x86芯片上所能达到的水平。 对于最重视原始吞吐量和 CPU 效率的服务器而言,这种权衡难以令人信服。

调度器算法CPU开销最佳硬件主要目标
mq-deadline带截止时间的排序LBA低(约0.7 µs/请求)SATA SSD、HDD、虚拟磁盘可预测的低延迟
noneFIFO,无重排序可忽略不计NVMe SSD最大吞吐量
bfq按比例分配的配额中等(约 1.9 微秒/请求)HDD、共享及桌面系统公平性与响应性

根据工作负载选择合适的调度器

选择合适的调度器取决于两点:您的存储硬件和应用程序的访问模式。首先考虑硬件。如果设备本身已具备请求重排序能力(例如配备支持重排序固件的 NVMe 驱动器),软件调度只会增加开销,因此 none 胜出。而在寻道时间占主导地位的机械硬盘上,软件重排序能降低延迟,因此 mq-deadlinebfq 是更优选。SATA SSD 则介于两者之间:速度快于 HDD,但缺乏 NVMe 的深度队列,因此 mq-deadline 最为合适。

当已有其他机制代为调度时,同样的逻辑同样适用。运行在 virtio-blk 上的客户机虚拟机依赖主机进行 I/O 调度,而带有写回缓存的硬件 RAID 控制器会自行优化请求顺序。在这两种情况下, none 都能避免重复消耗资源。

访问模式是第二个考量因素。每秒执行数千次4K随机读取的数据库,与从NVMe阵列流式读取大型顺序数据块的训练任务截然不同,它们需要不同的调度器。下表将常见工作负载与相应的起始点进行了对应。

工作负载存储调度器原因
AI/ML 训练NVMe SSDnone顺序高吞吐量;固件处理队列
OLTP 数据库NVMe SSDnone低延迟随机 I/O;避免软件开销
OLTP 数据库SATA SSDmq-deadline防止写入饥饿;尾部延迟可预测
数据仓库 / OLAPNVMe / 高速 SSDnone深度并行队列;最大吞吐量
通用网站托管SATA SSD / HDDmq-deadline混合小文件 I/O 的稳定响应
共享/多租户托管HDD / SSDbfq租户间资源分配公平;防止 I/O 独占
虚拟机客户机virtio-blknone主机已进行调度;双重调度会浪费 CPU
备份 / 归档HDDmq-deadline具有饥饿保护的顺序吞吐量

有一点例外值得指出。即使在 NVMe 上,如果 p99 或 p999 的尾部延迟是您关注的指标(例如在金融系统中), mq-deadline 可以胜过 none 通过强制执行严格的截止时间并防止偶发的延迟请求。

更改和调整调度器参数

无论是切换调度器还是调整其参数,均通过 sysfs 进行,且无需重启即可测试更改。

切换活动调度器

检查设备可用的调度器列表,其中方括号内的值即为当前活动调度器:

cat /sys/block/sda/queue/scheduler

在运行时切换至其他调度器。此操作立即生效,但重启后将失效:

echo bfq | sudo tee /sys/block/sda/queue/scheduler

如果 bfq 未列出,请先加载模块:

sudo modprobe bfq

若要使设置永久生效,请使用 udev 规则而非旧版 elevator= 内核参数,因为在 RHEL 9 及类似版本中,该参数已不再能更改调度器。此规则将 mq-deadline/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"

无需重启即可重新加载并应用该规则:

sudo udevadm control --reload-rules && sudo udevadm trigger

在基于 RHEL 的系统上,TuneD 配置文件通过全局配置文件而非逐设备规则实现相同功能。

值得调整的参数

每个调度器都在 /sys/block/<device>/queue/iosched/下提供其可调参数。对于 mq-deadline而言,时限是主要调节手段。运行在 SATA SSD 上的对延迟敏感的数据库将受益于更短的时限:

echo 100 | sudo tee /sys/block/sda/queue/iosched/read_expire
echo 1000 | sudo tee /sys/block/sda/queue/iosched/write_expire

对于 bfq 在高吞吐量系统上,禁用延迟启发式算法可提升吞吐量:

echo 0 | sudo tee /sys/block/sda/queue/iosched/low_latency
echo 0 | sudo tee /sys/block/sda/queue/iosched/slice_idle
调度器参数默认调优目标
mq-deadlineread_expire500 毫秒调低可加快读取响应
mq-deadlinewrite_expire5000 毫秒降低该值可减少写入延迟
mq-deadlinewrites_starved3在读取密集型负载下增加
mq-deadlinefifo_batch16设置为 1 以获得最低延迟
bfqlow_latency1设置为 0 以获得最大吞吐量
bfqslice_idle8 毫秒若使用 SSD 或 RAID,请设置为 0
bfqstrict_guarantees0若需严格带宽共享,请设为 1

对于共享主机,BFQ 与 cgroups v2 配合良好。分配 io.weight 特定值,例如可将数据库进程的 I/O 份额设为备份任务的十倍,从而确保后台任务不会挤占交互式流量。无论如何调整,在受 CPU 性能限制且 I/OPS 需求高的系统中,BFQ 较高的单请求开销会累积增加,因此请在正式启用前进行基准测试。

调优后的性能验证

在进行任何更改之前,务必先记录基准数据。否则,您将无法判断调整是否有效。

fio 是此类测试的标准工具。它通过块大小、队列深度和 I/O 引擎设置来重现特定的工作负载模式。请务必使用 --direct=1 参数,使其绕过页面缓存,直接测量调度器和设备性能,而非缓存读取。测试场景应与实际工作负载相匹配:

工作负载fio 参数
OLTP 数据库--rw=randread --bs=4k --iodepth=32 --direct=1
数据仓库--rw=read --bs=1m --iodepth=32 --direct=1
预写/重做日志--rw=write --bs=4k --iodepth=1 --direct=1
对象存储--rw=randrw --bs=64k --iodepth=64 --direct=1

iodepth 1 到 256 的数值范围内运行相同的测试,以找出设备的饱和点——即 IOPS 停止攀升且延迟飙升的临界点。对于变更后的实时监控, iostat -x 1 报告关键指标: r_await 以及 w_await 用于读写完成延迟, aqu-sz 表示平均队列深度, %util 表示设备利用率。当 %util 该值接近 100% 时,说明硬件已达到极限,此时调整调度器也无济于事。

要区分软件开销与硬件开销,请配合 btt 运行 blktrace。它将延迟拆分为 Q2D(软件队列耗时)和 D2C(设备处理请求所需时间)。若 Q2D 占主导,则调度器是瓶颈;若 D2C 占主导,则硬件是瓶颈。

解读结果时需注意一点:调度器选择主要影响延迟分布的尾部,而非中位数。从 none 切换到 mq-deadline ,可能使中位数延迟增加几微秒,但会将 p99 和 p999 延迟减半。对于受 SLA 约束的用户级服务,这种权衡几乎总是值得的,这也是为何测量尾部延迟而非平均吞吐量才是此项工作的核心所在。

选择合适的调度器

调度器调优旨在将算法适配于硬件和访问模式,并通过测量进行验证。简而言之:

  • NVMe:使用 none 并让固件负责队列管理。
  • 混合 I/O 的 SATA SSD 和 HDD:使用 mq-deadline 以获得可预测的延迟。
  • 共享或多租户主机:使用 bfq 以防止单个工作负载占用过多资源而影响其他工作负载。
  • 关注尾部延迟而非中位数:调度器变更会在 p99 和 p999 处体现,因此应重点测量这些指标。
  • 确保持久化:使用 udev 规则或 TuneD,切勿使用已废弃的 elevator= 参数。

要充分发挥任何调度器的性能,首先需要性能匹配的硬件。如果您需要专为高吞吐量、低延迟工作负载打造的 NVMe 服务器,请了解 FDC 的 VPS 方案

博客

本周特色

更多文章
为什么必须拥有功能强大且不计量的 VPS

为什么必须拥有功能强大且不计量的 VPS

非计费 VPS 以固定端口速度提供固定费率带宽。它与计费计划有何不同,什么时候会有回报,以及购买前需要检查什么。

7 分钟阅读 - 2025年5月9日

Linux 内存管理:交换、OOM 杀手和 Cgroups

12 分钟阅读 - 2026年5月31日

更多文章