Linux块层IO调度器工作原理与演进
Linux内核块层IO调度器负责对提交到块设备的IO请求进行排序、合并与调度,直接影响磁盘吞吐量和延迟表现。从2.6内核到6.x内核,调度器经历了从Deadline到CFQ再到MQ-Block多队列架构的重大演进。现代内核默认使用mq-deadline调度器,同时提供bfq和kyber作为可选方案。
理解IO调度器的核心概念:请求队列(request queue)接收上层bio请求后,调度器根据算法策略决定请求的派发顺序。合并(merge)将相邻扇区的请求合并为一个更大IO,排序(sort)按LBA地址排列减少磁头寻道,回写(writeback)与预读(readahead)影响IO模式判断。
mq-deadline调度器配置与适用场景
mq-deadline是当前Linux默认调度器,核心思想是给每个IO请求设置截止时间,确保请求不会无限期等待。它维护两个队列:排序队列按扇区地址排列减少寻道,限期队列按提交时间排列防止饥饿。读请求默认期限500ms,写请求5000ms。
查看和修改调度器的方法:
# 查看当前调度器
cat /sys/block/sda/queue/scheduler
# 输出示例: [mq-deadline] bfq kyber none
# 切换调度器
echo bfq > /sys/block/sda/queue/scheduler
# 查看deadline参数
cat /sys/block/sda/queue/iosched/read_expire
cat /sys/block/sda/queue/iosched/write_expire
# 调整读请求期限为200ms
echo 200 > /sys/block/sda/queue/iosched/read_expire
echo 2000 > /sys/block/sda/queue/iosched/write_expire
mq-deadline适合通用场景,尤其是SSD和NVMe设备。因为SSD没有机械寻道开销,排序队列的意义减弱,限期队列防止饥饿成为主要价值。数据库服务器默认使用此调度器即可获得稳定延迟。
BFQ调度器带宽公平分配与交互式应用优化
BFQ(Budget Fair Queueing)适合多任务竞争IO带宽的场景,如桌面系统、虚拟化宿主机。它以进程/组为单位分配IO预算,保证每个IO发起者获得公平的带宽份额。BFQ的参数调优空间较大:
# 切换到BFQ
echo bfq > /sys/block/sda/queue/scheduler
# 调整权重(范围1-1000,默认100)
echo 200 > /sys/block/sda/queue/iosched/weight
# 低延迟模式(桌面系统推荐开启)
echo 1 > /sys/block/sda/queue/iosched/low_latency
# 最大sector数
cat /sys/block/sda/queue/iosched/max_budget
BFQ的low_latency模式会将大块写IO拆分为小批次,确保交互式读请求快速获得服务。代价是整体吞吐量下降约5-10%。在视频渲染等顺序写密集场景下应关闭low_latency以获得最大吞吐。
cgroup v2可以配合BFQ实现IO带宽的精细化控制,将不同服务的IO权重隔离:
# 为数据库服务设置IO权重
mkdir /sys/fs/cgroup/db_service
echo 300 > /sys/fs/cgroup/db_service/io.bfq.weight
echo $(pgrep -f mysqld) > /sys/fs/cgroup/db_service/cgroup.procs
Kyber调度器NVMe低延迟场景调优
Kyber调度器专为NVMe等高速设备设计,采用令牌桶模型控制请求派发速率,避免设备内部队列拥塞。它维护两个阈值:read_latency和write_latency,通过同步请求的完成延迟来调整派发速率。
# 切换到Kyber
echo kyber > /sys/block/nvme0n1/queue/scheduler
# 查看当前配置
cat /sys/block/nvme0n1/queue/iosched/read_latency
cat /sys/block/nvme0n1/queue/iosched/write_latency
Kyber在高并发随机读写场景下延迟方差最小,但吞吐量不如none模式。对于NVMe设备,另一个选择是完全跳过调度器:
echo none > /sys/block/nvme0n1/queue/scheduler
none模式直接将请求提交给设备,依赖NVMe自身的硬件队列管理。在NVMe SSD基准测试中,none模式通常获得最大IOPS和最低延迟,但缺乏公平性保障,不适合多租户场景。
fio磁盘性能基准测试与调度器效果验证
调优前后必须量化测试。fio是最可靠的IO基准测试工具:
# 4K随机读测试(模拟数据库OLTP负载)
fio --name=randread --ioengine=libaio --iodepth=64 \
--rw=randread --bs=4k --numjobs=4 --size=10G \
--runtime=60 --time_based --group_reporting \
--filename=/dev/sda1
# 顺序写测试(模拟日志写入)
fio --name=seqwrite --ioengine=libaio --iodepth=32 \
--rw=write --bs=128k --numjobs=1 --size=10G \
--runtime=60 --time_based \
--filename=/dev/sda1
# 混合读写测试(70%读30%写)
fio --name=mixed --ioengine=libaio --iodepth=64 \
--rw=randrw --rwmixread=70 --bs=4k \
--numjobs=4 --size=10G --runtime=60 \
--time_based --group_reporting \
--filename=/dev/sda1
测试结果重点看三个指标:IOPS(每秒IO次数)、平均延迟(latency mean)、延迟P99值。切换调度器后如果P99延迟明显改善而IOPS基本不变,说明调度器优化了尾部延迟分布。生产环境中P99往往比平均值更能反映用户体验。
调度器选择决策矩阵与生产建议
根据设备类型和工作负载特征选择调度器的速查表:
NVMe SSD + 数据库OLTP → none或kyber,追求最低延迟
NVMe SSD + 通用服务器 → mq-deadline,延迟和吞吐均衡
SATA SSD + 多租户 → bfq + cgroup,带宽公平分配
HDD + 冷存储归档 → mq-deadline,排序减少寻道
HDD + 虚拟化宿主机 → bfq + low_latency=1,保障VM交互响应
生产环境持久化配置建议写入udev规则,避免重启后恢复默认:
# /etc/udev/rules.d/60-ioscheduler.rules
ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/scheduler}="mq-deadline"
ACTION=="add|change", KERNEL=="nvme[0-9]+n[0-9]+", ATTR{queue/scheduler}="none"
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/linux-ci-pan-io-diao-du-suan-fa-shen-du-dui-bi-yu-sheng/