Linux内核学习轨迹第七部:IO调度器(第五节) 5. IO调度器全解析none/mq-deadline/kyber/bfqIO调度器是Linux块层的核心组件位于blk-mq软件队列与硬件队列之间是IO性能调优的核心抓手。它的核心职责是对上层下发的IO请求做合并、排序、优先级调度、公平性控制解决两大核心问题一是减少机械硬盘的磁头寻道开销最大化吞吐量二是控制IO延迟、解决IO饥饿、保证业务的服务质量。Linux 6.0内核完全移除了传统单队列架构的调度器cfq、传统deadline、noop仅保留适配blk-mq多队列架构的调度器。本章节基于Linux 6.6 LTS内核完整拆解生产环境主流的4个调度器none原noop、mq-deadline、kyber、bfq包括设计思想、核心实现、适用场景、调优方案与避坑指南。5.1 IO调度器的核心设计目标所有IO调度器都围绕以下核心目标设计不同调度器在不同目标上做了取舍最大化吞吐量通过IO合并、扇区排序减少机械硬盘的磁头寻道次数提升磁盘的有效IO吞吐量控制IO延迟保证同步IO、关键业务IO的低延迟避免长尾延迟恶化解决IO饥饿避免某一类IO如随机读被另一类IO如大块顺序写长时间阻塞导致请求饿死保证公平性在多进程/多租户场景下保证每个进程/租户能公平占用IO资源避免单个进程占用所有IO带宽适配硬件特性针对机械硬盘、SSD、NVMe等不同存储硬件的特性做针对性的优化最小化CPU开销。5.2 主流IO调度器深度拆解5.2.1 none调度器原noop调度器none调度器是blk-mq架构下的无操作调度器对应传统单队列架构的noop调度器是Linux 6.6内核NVMe设备的默认调度器。核心设计思想none是最简单的调度器不做任何IO排序、优先级调度、延迟控制仅做最基础的前后IO合并完全按照FIFO先进先出的顺序把请求下发给驱动。它的核心设计逻辑是现代NVMe SSD内部的FTL闪存转换层已经实现了高级的IO调度、磨损均衡、垃圾回收内核层的排序调度只会带来额外的CPU开销没有任何性能收益。核心实现原理none调度器的源码实现极简核心逻辑只有两点IO合并新请求入队时仅尝试和队列中已有的请求做前向/后向合并不做任何排序FIFO下发请求入队后直接加入到硬件队列的分发链表按照入队顺序下发给驱动没有任何调度逻辑。核心特性与适用场景优势CPU开销极低完全无锁调度最大化发挥NVMe多队列硬件的并行能力随机IO场景下IOPS最高、延迟最低劣势无任何公平性、延迟控制、饥饿保护机制机械硬盘场景下吞吐量极差多进程大流量顺序写会导致读请求严重饥饿最佳适用场景所有NVMe SSD/企业级SSD场景、随机IO为主的业务数据库、KV存储、高频交易、多核多队列硬件环境。5.2.2 mq-deadline调度器mq-deadline是传统单队列deadline调度器的blk-mq适配版是机械硬盘HDD的默认调度器也是生产环境HDD场景的首选。核心设计思想mq-deadline的核心目标是解决IO饥饿问题同时最大化机械硬盘的吞吐量。它给每个IO请求设置一个「截止时间」保证请求不会被长时间饿死同时通过扇区排序减少磁头寻道开销。针对读写的业务特性做了差异化设计读请求通常是同步阻塞的对延迟更敏感因此读的截止时间更短优先级更高写请求通常是异步的对延迟不敏感截止时间更长。核心实现原理mq-deadline的核心是4个独立的队列双逻辑1.4个核心队列读排序队列按IO请求的起始扇区升序排序用于减少磁头寻道提升吞吐量读截止队列按IO请求的截止时间升序排序用于保证读请求不会饿死写排序队列同读排序队列用于写请求的扇区排序写截止队列同读截止队列用于写请求的饥饿保护。2.截止时间默认配置可通过sysfs调整读请求截止时间500ms写请求截止时间5000ms写饥饿阈值当连续处理了16个写请求后必须处理一次读请求避免读请求被饿死。3.核心调度逻辑新请求入队时同时加入排序队列和截止队列设置对应的截止时间调度器优先从排序队列取请求最大化吞吐量每次调度前先检查截止队列中是否有超时的请求如果有优先处理超时的请求保证不会饿死当连续处理的写请求达到阈值后强制切换到读队列处理保证读请求的优先级。核心特性与适用场景优势完美适配机械硬盘的物理特性吞吐量最高同时有效解决IO饥饿问题保证读请求的低延迟稳定性极强20年生产环境验证劣势对NVMe SSD优化不足有一定的CPU开销无公平性保证多租户场景下无法隔离IO资源最佳适用场景所有机械硬盘HDD/RAID阵列场景、顺序读写为主的业务大数据、视频存储、日志服务、混合读写的通用服务器场景。5.2.3 kyber调度器kyber是Facebook为NVMe SSD低延迟场景设计的blk-mq专属调度器在Meta的大规模数据中心经过了生产验证是低延迟业务场景的首选。核心设计思想kyber的核心目标是控制NVMe SSD的IO长尾延迟保证同步IO的低延迟和可预测性。它完全摒弃了传统的扇区排序逻辑基于令牌桶算法多队列深度限制实现调度核心逻辑是限制同步读、同步写的最大队列深度避免IO请求在硬件队列中排队导致延迟升高同时异步写不做限制最大化后台写入的吞吐量。核心实现原理kyber的核心是双令牌桶动态调优1.两个核心令牌桶同步读令牌桶默认令牌数128同步读请求必须拿到令牌才能入队下发同步写令牌桶默认令牌数32同步写请求必须拿到令牌才能入队下发异步写脏页回写无令牌限制直接下发不占用同步IO的令牌资源。2.动态调优逻辑调度器会实时统计IO的平均延迟和长尾延迟动态调整两个令牌桶的大小如果延迟升高就减少令牌数降低队列深度减少排队延迟如果延迟低于阈值就增加令牌数最大化IOPS。3.调度优先级同步读 同步写 异步写/丢弃/刷新请求严格保证业务同步IO的优先级避免后台异步写抢占IO资源。核心特性与适用场景优势专为NVMe SSD设计CPU开销极低完美控制99.9%长尾延迟同步IO的延迟可预测性极强适配高并发低延迟的业务场景劣势机械硬盘场景完全不适用无排序逻辑吞吐量极差无公平性保证多租户场景无法隔离IO资源最佳适用场景NVMe SSD低延迟场景、数据库/分布式KV存储、高频交易、在线业务等对长尾延迟敏感的场景。5.2.4 bfq调度器bfqBudget Fair Queueing预算公平队列是传统单队列cfq调度器的blk-mq升级版专为桌面/嵌入式/多租户场景设计核心目标是保证IO资源的完全公平分配同时优化交互场景的低延迟。核心设计思想bfq的核心是基于权重的完全公平调度它给每个进程/线程分配一个独立的IO队列按照进程的权重分配「IO预算」默认按时间分配也可按扇区数分配进程用完预算后调度器切换到下一个进程的队列保证所有进程按权重公平占用IO资源。同时对前台交互进程、实时进程做优先级提升大幅降低桌面场景的应用启动延迟、操作卡顿。核心实现原理bfq的核心是独立进程队列预算分配优先级提升1.Per-Process独立队列每个进程对应一个独立的IO队列进程的IO请求只会进入自己的队列完全隔离避免不同进程的IO相互干扰2.预算分配机制每个进程默认权重相同分配相同的IO预算可以通过ionice工具调整进程的IO优先级和权重高权重进程分配更多的预算调度器按照预算比例给每个队列分配执行时间预算用完立即切换队列保证完全公平3.交互场景优化自动识别前台交互进程、应用启动进程临时提升其IO优先级和预算让应用启动更快、操作更流畅对后台批量写进程降低优先级避免后台任务占用IO资源导致前台卡顿4.低延迟模式针对SSD场景优化的低延迟模式减少预算切换的开销保证低延迟的同时兼顾公平性。5.核心特性与适用场景优势完美的IO公平性多进程/多租户场景下IO资源隔离效果极佳桌面交互场景延迟极低体验流畅支持精细化的IO优先级控制劣势CPU开销较高高并发IO场景下吞吐量不如none/kyber机械硬盘大吞吐量场景不如mq-deadline最佳适用场景桌面Linux发行版、安卓/嵌入式系统、多租户容器/虚拟机场景、需要精细化IO优先级控制的场景。5.3 四大调度器核心对比与选型指南特性维度nonenoopmq-deadlinekyberbfq核心设计无调度仅合并FIFO下发截止时间保护扇区排序防饥饿高吞吐令牌桶队列深度控制NVMe低延迟按进程权重公平分配保证隔离性IO排序无按扇区升序排序无可选排序延迟优化无额外延迟原生硬件延迟优化读请求延迟防饥饿极致优化99.9%长尾延迟优化前台交互进程延迟公平性保证无无无完全公平按权重分配CPU开销极低低极低中高适配硬件NVMe SSD/企业级SSD机械硬盘HDD/RAID阵列NVMe SSD低延迟场景桌面/嵌入式/多租户场景典型业务场景数据库、KV存储、高频交易、通用NVMe场景大数据、日志存储、HDD通用服务器在线业务、低延迟交易、分布式存储桌面Linux、安卓、容器多租户生产环境选型最佳实践1.NVMe SSD场景绝大多数通用场景、随机IO为主的业务优先选noneCPU开销最低IOPS最高对99.9%长尾延迟敏感的在线业务、数据库优先选kyber延迟可预测性更强多租户容器/虚拟机场景选bfq保证IO资源公平隔离。2.机械硬盘HDD/RAID阵列场景所有通用场景、顺序读写为主的业务必须选mq-deadline吞吐量最高防饥饿效果最好桌面/嵌入式HDD场景选bfq保证交互流畅度。3.SATA/SAS SSD场景企业级SSD选none/kyber消费级SSD选mq-deadline消费级SSD的FTL能力较弱内核排序能提升一定性能。5.4 IO调度器的配置与调优5.4.1 调度器的查看与临时配置1.查看当前设备的调度器# 查看sda设备的调度器中括号标记的是当前生效的调度器 cat /sys/block/sda/queue/scheduler # 查看所有块设备的调度器 for dev in $(ls /sys/block/ | grep -E sd|nvme|hd); do echo $dev: $(cat /sys/block/$dev/queue/scheduler); done2.临时修改调度器重启失效# 把nvme0n1设备的调度器设置为kyber echo kyber /sys/block/nvme0n1/queue/scheduler # 把sda设备的调度器设置为mq-deadline echo mq-deadline /sys/block/sda/queue/scheduler3.永久配置调度器方式1通过udev规则配置编辑/etc/udev/rules.d/60-io-scheduler.rules# NVMe设备默认使用kyber调度器 ACTIONadd|change, KERNELnvme*n1, ATTR{queue/scheduler}kyber # SATA/SAS HDD设备默认使用mq-deadline调度器 ACTIONadd|change, KERNELsd*, ATTR{queue/rotational}1, ATTR{queue/scheduler}mq-deadline # SATA SSD设备默认使用none调度器 ACTIONadd|change, KERNELsd*, ATTR{queue/rotational}0, ATTR{queue/scheduler}none方式2通过内核启动参数配置在grub的GRUB_CMDLINE_LINUX中添加elevatormq-deadline执行update-grub重启生效该配置会设置所有设备的默认调度器。5.4.2 各调度器的核心调优参数所有调度器的调优参数都在/sys/block/[设备名]/queue/iosched/目录下针对不同调度器有不同的可调参数。1.mq-deadline调度器调优参数参数核心含义默认值调优建议read_expire读请求的截止时间单位毫秒500读延迟敏感场景调小到200顺序读大文件场景调大到1000write_expire写请求的截止时间单位毫秒5000写敏感场景调小到1000日志写入场景调大到10000writes_starved连续处理多少个写请求后强制处理读请求16读优先场景调小到8写优先场景调大到32front_merges是否开启前向合并1随机IO场景关闭设为0减少CPU开销顺序IO场景开启2.kyber调度器调优参数参数核心含义默认值调优建议read_lat_nsec读请求的目标延迟单位纳秒20000002ms低延迟场景调小到10000001ms高IOPS场景调大到50000005mswrite_lat_nsec写请求的目标延迟单位纳秒1000000010ms同步写敏感场景调小到20000002ms异步写场景调大到2000000020ms3.bfq调度器调优参数参数核心含义默认值调优建议low_latency低延迟模式开关1开启桌面场景保持开启高吞吐量服务器场景关闭设为0timeout_sync同步进程的最大预算时间单位毫秒124桌面场景调大到200提升前台进程性能服务器场景调小到50提升公平性max_budget单个进程的最大IO预算单位扇区数0自动计算大文件顺序写场景调大到2048小文件随机IO场景调小到5125.5 工程实践避坑指南1.NVMe SSD不要用mq-deadline调度器很多老教程会推荐NVMe用deadline调度器这是完全错误的。现代NVMe SSD的FTL已经实现了高级的IO调度内核层的排序只会带来额外的CPU开销不会提升性能反而会导致延迟升高。NVMe场景优先用none或kyber。2.不要混用单队列和多队列调度器Linux 6.0之后传统单队列的cfq、deadline、noop已经被完全移除很多老教程的配置已经失效强行配置会导致调度器不生效回退到none调度器。生产环境必须使用blk-mq原生的4个调度器。3.调度器选型要匹配硬件不要一刀切不要给所有设备都配置同一个调度器机械硬盘用mq-deadlineNVMe用none/kyber桌面场景用bfq必须根据硬件和业务场景做差异化配置否则会导致性能严重下降。4.ionice优先级仅对bfq调度器生效ionice工具设置的IO优先级仅在bfq调度器下生效其他调度器会忽略优先级配置。很多人误以为设置了ionice就会生效却不知道调度器不支持导致优先级配置完全无效。5.不要随意调大截止时间和队列深度很多人为了提升吞吐量盲目调大mq-deadline的截止时间、kyber的令牌数会导致IO排队延迟急剧升高99.9%长尾延迟恶化10倍以上尤其是在线业务场景必须优先保证延迟再追求吞吐量。