4. IO 调度器发展史:从 CFQ 到 Deadline、NOOP,再到多队列时代

IO 调度器的演进史,本质上是存储硬件性能与软件栈之间不断博弈、消除瓶颈的历史。从机械硬盘时代到 NVMe SSD 时代,调度器的设计哲学经历了从“公平排队”到“最小延迟”,再到“零干预”和“多队列并行”的深刻转变。理解这段历史,是掌握现代调度器参数调优的前提。

核心观点: 调度器的每一次迭代,都是为了解决上一代硬件瓶颈而设计的。NVMe SSD 的出现,使得调度器从“控制者”转变为“协调者”,甚至“旁观者”。

4.1 机械硬盘时代:CFQ 与 Deadline 的统治

在机械硬盘(HDD)时代,IO 调度的核心矛盾是:寻道延迟。磁头在盘片上移动寻找数据位置,一次寻道耗时约 5-10ms,远高于数据传输时间。调度器的首要任务是将随机 IO 合并为顺序 IO,减少磁头移动。

4.1.1 CFQ(完全公平排队)

CFQ 是 Linux 内核 2.6 时代默认的 IO 调度器,其设计哲学是公平性。它为每个进程分配一个时间片,在时间片内,该进程的 IO 请求被优先处理。CFQ 通过复杂的排队和合并算法,试图让所有进程获得大致相等的磁盘带宽。

  • 优点: 多进程并发场景下公平性好,能有效防止单个进程饿死。
  • 缺点: 延迟不可控,上下文切换开销大,对 SSD 的随机 IO 性能提升有限。
  • 适用场景: 传统的数据库服务器、文件服务器等混合负载场景。

4.1.2 Deadline(截止时间)

Deadline 调度器放弃了 CFQ 的完全公平,转而追求延迟确定性。它为每个 IO 请求设置一个截止时间(默认读 500ms,写 5s),并维护读、写两个 FIFO 队列。当有请求即将超时时,Deadline 会优先处理该请求,从而保证最坏情况下的响应时间。

  • 优点: 读延迟低,写操作不会长时间阻塞读操作,适合对响应时间敏感的应用。
  • 缺点: 公平性不如 CFQ,写操作可能被读操作频繁抢占。
  • 适用场景: 实时数据库、Web 服务器、视频监控等对延迟有严格要求的场景。
调优启示: 在 HDD 时代,CFQ 和 Deadline 的调优重点在于调整 slice_idle、fifo_expire 等参数,以平衡公平性与延迟。这些参数在 NVMe SSD 上已基本失效。

4.2 早期 SSD 时代:NOOP 的崛起

当 SATA SSD 开始普及时,其随机 IO 性能(约 10-50K IOPS)远超 HDD,但寻道时间几乎为零。此时,CFQ 和 Deadline 的复杂合并与排序逻辑反而成为性能瓶颈。NOOP(No Operation)调度器应运而生。

NOOP 的核心思想是“什么都不做”。它只维护一个简单的 FIFO 队列,将 IO 请求直接传递给底层驱动,不做任何合并或排序。它假设硬件(SSD 控制器)自己会处理请求的优化。

  • 优点: 开销极低,延迟最小,适合纯随机 IO 场景。
  • 缺点: 无法处理写放大问题,对顺序 IO 没有优化。
  • 适用场景: 早期的 SATA SSD、虚拟化环境(宿主机层面)。
注意: NOOP 并非真正的“无调度”,它仍然会进行简单的请求合并(合并相邻扇区)。在 NVMe SSD 上,NOOP 已被 none 调度器取代。

4.3 多队列时代:blk-mq 与调度器重构

NVMe SSD 的出现,将 IOPS 推高至百万级别,单队列的 IO 栈成为瓶颈。Linux 内核 3.13 引入了 blk-mq(Block Multi-Queue) 框架,彻底重构了 IO 路径。blk-mq 将单队列拆分为多个软件队列(Software Queue)和硬件队列(Hardware Queue),实现了 CPU 核心与 NVMe 队列的直接映射,消除了锁竞争。

在 blk-mq 框架下,传统的 CFQ、Deadline、NOOP 被重新设计为 mq-deadline、bfq(Budget Fair Queueing,CFQ 的替代者)和 none(NOOP 的替代者)。

4.3.1 多队列架构对比

特性 单队列(传统) 多队列(blk-mq)
队列数量 1 个全局队列 多个软件队列 + 多个硬件队列
锁竞争 严重(单锁保护全局队列) 极低(每个队列独立锁)
CPU 亲和性 差(请求可能被任意 CPU 处理) 好(请求绑定到特定 CPU 和硬件队列)
典型调度器 CFQ, Deadline, NOOP mq-deadline, bfq, none
适用硬件 HDD, SATA SSD NVMe SSD, 高性能 SATA SSD

4.3.2 现代调度器选择

  • none: 默认调度器,零开销,适合绝大多数 NVMe SSD 场景。
  • mq-deadline: 当需要保证读延迟上限时使用,例如数据库日志盘。
  • bfq: 桌面环境或交互式应用,提供比 mq-deadline 更好的公平性。

4.4 调度器演进全景图

下图展示了从 HDD 到 NVMe SSD 时代,IO 调度器架构的演进路径。注意观察队列结构从单点到多点的变化,以及调度器角色从“主动控制”到“被动协调”的转变。

IO 调度器演进全景图 IO 调度器演进全景图 HDD 时代(单队列) 核心矛盾:寻道延迟 调度器:CFQ / Deadline 目标:合并请求,减少磁头移动 瓶颈:单队列锁竞争 SATA SSD 时代(单队列) 核心矛盾:调度器开销 调度器:NOOP 目标:最小化软件开销 瓶颈:单队列仍存在锁竞争 NVMe SSD 时代(多队列) 核心矛盾:队列并行度 调度器:none / mq-deadline 目标:CPU 与硬件队列直连 瓶颈:几乎消除软件瓶颈 关键转折点 1. 硬件性能提升:寻道延迟(10ms)→ 随机延迟(0.1ms)→ 队列深度(64K+) 2. 软件架构变革:单队列锁竞争 → blk-mq 多队列无锁路径 3. 调度器角色转变:主动合并排序 → 被动传递(none)或延迟保障(mq-deadline) 队列结构对比 单队列(全局锁) 磁盘 SQ 0 SQ 1 SQ N HQ 0 HQ 1 HQ N NVMe SSD

4.5 总结:调度器选择的黄金法则

在 NVMe SSD 场景下,调度器的选择已经变得非常简单。以下是基于性能测试和生产经验的推荐法则:

  1. 默认选择 none: 对于 99% 的 NVMe SSD 场景,none 调度器能提供最低的延迟和最高的吞吐量。
  2. 特殊场景选择 mq-deadline: 当应用对读延迟有严格上限要求(如数据库 WAL 日志),且无法容忍偶尔的延迟尖刺时,使用 mq-deadline 并调整 fifo_expire_read 参数。
  3. 避免使用 bfq: 除非是桌面环境或需要精细的 IO 带宽控制,否则 bfq 在 NVMe SSD 上的额外开销通常不值得。
性能数据参考: 在 Intel Optane P5800X 上测试,none 调度器相比 mq-deadline 在 4K 随机读场景下,延迟降低约 5-8%,吞吐量提升约 3-5%。在 QD=1 时,none 的延迟优势更为明显。