4. 请求队列深度(Queue Depth)定义
请求队列深度(Queue Depth)是块设备驱动与IO调度器之间最核心的调优参数之一。它直接决定了系统在任意时刻能够向存储设备提交的未完成IO请求数量,进而影响IO延迟、吞吐量和CPU利用率。本章将深入剖析队列深度的本质含义,并清晰区分硬件队列深度与软件队列深度的差异。
4.1 队列深度的含义
队列深度(Queue Depth,简称QD)在块设备IO栈中,指的是在任意时刻,已经提交给存储设备但尚未完成(即未收到完成中断或完成通知)的IO请求数量。它反映了系统与存储设备之间“在途”请求的并发程度。
从宏观视角看,队列深度可以理解为IO流水线的“容量”。当队列深度为1时,系统必须等待前一个IO完成才能发送下一个IO,此时设备利用率低,但延迟可预测。当队列深度增大时,设备可以同时处理多个请求,利用内部并行能力(如多通道、NCQ、NVMe多队列)提升吞吐量,但过深的队列会导致请求排队时间增加,从而抬高延迟。
4.2 硬件队列深度(Hardware Queue Depth)
硬件队列深度是指存储设备(如NVMe SSD、SATA HDD、SAS磁盘)内部硬件层面能够同时处理的命令数量。它由设备的固件、控制器、物理通道数以及协议能力共同决定。
| 设备类型 | 典型硬件队列深度 | 限制因素 |
|---|---|---|
| NVMe SSD (高端) | 64K (每队列) / 多队列 | PCIe带宽、控制器通道数、NAND闪存并行度 |
| SATA SSD (AHCI) | 32 (NCQ最大深度) | AHCI协议限制、单队列设计 |
| SAS HDD (企业级) | 256 ~ 1024 | SAS协议、磁盘旋转延迟、磁头寻道 |
| NVMe SSD (消费级) | 16K ~ 32K (每队列) | 主控能力、DRAM缓存大小 |
| 虚拟化存储 (vSAN/CEPH) | 128 ~ 1024 | 网络延迟、后端存储聚合能力 |
硬件队列深度是设备能力的上限。驱动程序在初始化时,会通过设备寄存器或协议协商获取该值,并在IO提交时确保不超过此限制。超过硬件队列深度的请求会被设备拒绝或导致命令超时。
4.3 软件队列深度(Software Queue Depth)
软件队列深度是指操作系统内核中IO调度器或块设备驱动层维护的、尚未提交给硬件或正在等待处理的请求数量。它由软件层面的队列结构(如请求队列、per-CPU队列、blk-mq的软件暂存队列)管理。
软件队列深度通常大于硬件队列深度,因为软件层需要缓冲请求以进行合并、排序、优先级管理以及应对硬件处理速度的波动。在blk-mq(多队列块层)架构中,软件队列深度由以下因素决定:
- per-CPU软件暂存队列(Software Staging Queue):每个CPU核心拥有独立的软件队列,深度可配置,默认通常为128~256。
- 调度器内部队列:如CFQ的cfq_queue、BFQ的bfq_queue、Kyber的domain queue,深度由调度器策略和配置决定。
- 驱动层提交队列(Dispatch Queue):从软件队列向硬件队列传递请求的中间队列,深度影响批处理效率。
4.4 硬件队列深度与软件队列深度的核心区别
理解两者的区别是进行队列深度调优的前提。下表从多个维度对比了硬件队列深度与软件队列深度的关键差异:
| 对比维度 | 硬件队列深度 | 软件队列深度 |
|---|---|---|
| 定义位置 | 存储设备固件 / 控制器内部 | 操作系统内核 (IO调度器 / 块驱动) |
| 决定因素 | 设备物理能力、协议限制 (NVMe/SATA/SAS) | 内核配置、调度器策略、驱动参数 |
| 典型范围 | 32 ~ 65536 (视设备类型) | 128 ~ 4096 (可动态调整) |
| 修改方式 | 设备固件升级 / 协议协商 (通常不可动态修改) | sysfs参数 / ioctl / 模块参数 (可运行时调整) |
| 主要作用 | 限制设备并发处理能力上限 | 缓冲请求、合并排序、控制提交节奏 |
| 超限后果 | 设备拒绝请求 / 命令超时 / 总线错误 | 内存占用增加 / 调度延迟上升 / 优先级反转 |
| 调优目标 | 匹配设备最佳并发度 (避免过度提交) | 匹配硬件队列深度 + 预留缓冲空间 |
4.5 队列深度在IO路径中的实际作用
在块设备IO的完整路径中,队列深度的影响贯穿始终。以下是一个典型的IO请求生命周期,展示了队列深度如何在不同阶段发挥作用:
- 应用层提交:应用发起read/write系统调用,IO请求进入VFS层。
- 块层合并排序:IO调度器将请求放入软件队列,进行合并(merge)和排序(sort),此时软件队列深度增加。
- 驱动层提交:驱动从软件队列取出请求,放入硬件提交队列(如NVMe SQ),此时硬件队列深度增加。
- 设备执行:存储设备从硬件队列取出命令,执行实际数据传输,完成后通过完成队列(CQ)通知驱动。
- 完成处理:驱动处理完成中断,释放请求,软件队列深度和硬件队列深度同时减少。
4.6 查看当前队列深度的方法
在Linux系统中,可以通过以下方式查看当前生效的队列深度:
# 查看块设备的软件队列深度 (以nvme0n1为例)
cat /sys/block/nvme0n1/queue/nr_requests
# 查看硬件队列深度 (NVMe设备)
cat /sys/block/nvme0n1/device/queue_depth
# 查看blk-mq每队列深度
cat /sys/block/nvme0n1/mq/*/nr_requests
# 使用iostat观察实际在途IO数量 (avgqu-sz列)
iostat -x 1
# 使用blktrace追踪队列深度变化
blktrace -d /dev/nvme0n1 -o - | blkparse -i -
其中,nr_requests 表示软件队列深度,queue_depth 表示硬件队列深度。实际运行时,avgqu-sz 反映了当前时刻的平均队列深度,是判断队列深度设置是否合理的重要参考指标。
avgqu-sz 和 await (平均IO等待时间)。如果 avgqu-sz 接近硬件队列深度但 await 仍然较低,说明还有提升空间;如果 await 急剧上升,说明队列深度已经过深。