服务器入坑指北:虚拟盘上的 I/O 调度取舍
听说"换成某个调度器能更快",照着改了,结果没什么感觉;不知道该不该改,也不确定改了到底有没有用;同一台机器上好几块盘共用,改了个全局设置,别的地方反而变慢。
这类困惑的根源是:这一层能起到的作用常常被稀释——客户机看到的是虚拟设备,真正的排队多半发生在宿主侧;两层都排队,等于多了一层管理。所以判据不是"哪个名字更高级",而是"改动之后,你的负载指标有没有真的变好"。
这篇先讲怎么读现状(唯一可信的现读方式),再给官方的选择逻辑、虚拟盘上为什么常常白改,以及"只对匹配设备改、收益不足就回滚"的动作与判据。设备名与性能数字一律用占位符。另外把话说在前面:不要在生产盘上跑写压测。
先读现状:设备类型与当前生效项
当前生效项怎么读:读某块设备的调度器文件,列表里被方括号括住的那一个,就是当前生效的——这是唯一可信的现读方式。列表的形态是几个名字并列、其中一个在方括号里。
设备类型:机械盘、固态盘、虚拟设备——内核选默认值时的依据就是设备类型,所以"先认清这是什么盘",比"先记住哪个名字更快"重要。
顺序反了的代价:拿着网上的参数表乱套——不看设备类型、也不看当前生效项,上来就改。
# 只读读取(设备名一律用占位符)
cat /sys/block/<设备>/queue/scheduler # 调度器列表:方括号里的那个才是当前生效项
cat /sys/block/<设备>/queue/nr_requests # 队列相关参数的形态(数值按你的机器定,别照抄)
lsblk -o NAME,SIZE,TYPE,MOUNTPOINT # 设备类型:虚拟还是物理,先有个判断
# 驱动信息:需要时进一步确认这块盘的类型与能力
官方的选择逻辑:按设备与负载选
| 设备 / 负载 | 官方推荐方向 | 理由一句话 | 不适合的情况 |
|---|---|---|---|
| 传统机械盘 | 限期类或面向交互的加权方案 | 机械盘有真实寻道,排序有价值 | 已经换成固态时,这类排序的收益不明显 |
| 高性能固态,或"计算受限而存储很快"的系统 | “不用调度器”(备选是轻量方案) | 设备本身够快、并行度高,上层再排一次队反而多一层管理 | 设备其实是共享或虚拟设备、真实排队在别处时 |
| 桌面 / 交互场景 | 面向交互的加权方案 | 优先保证交互响应 | 服务端批处理负载用不到它 |
| 虚拟客户机 | 限期类 | 在客户机里保持一个轻量、可预测的排队顺序 | 主机总线驱动支持多队列时,可以改用"不用调度器" |
官方还给两条明确的建议:内核自动选出的通常已经是合适的选择;确实需要别的,应当按设备匹配规则、只对匹配到的设备切换,而不是全机统一改。
表格里"虚拟客户机"那一行的两个方向并不矛盾:当主机侧的总线驱动支持多队列时,设备本身能并行接收请求,上层再做串行排序的价值就小了,所以可以用"不用调度器";否则保守一点,用限期类更稳。判据是:先现读主机侧总线驱动是否支持多队列,再决定用哪一档。现读入口按本机为准:可以看该设备对应的驱动信息与队列数量一类信息。
为什么在虚拟盘上常常"改了没感觉"
两条机制:
- 客户机看到的是虚拟块设备,真实的调度往往发生在宿主侧。两层都排队,等于叠了一层管理,收益自然被稀释;
- 请求到达宿主之后,由后端存储统一排队——客户机这一层的排序能力,能起到的作用是有限的。
所以判据只有一条:改动之后,你的负载指标有没有真的变好。名字更好听、参数更"高级",都不构成理由。
反过来说:如果你在同一负载下改完却没有可测量的变化,那多半说明瓶颈不在这一层——这时正确的动作是去看存储能力、后端与错峰,而不是继续换调度器名字。
还要记住一个前提:这台机器的存储能力(IOPS 与带宽)由机型与云盘类型决定。如果瓶颈在存储能力或共享后端,调度器这一层再怎么调也解决不了——这时候该做的动作是换盘型、换机型、错峰,而不是继续换调度器名字。
取舍:只改匹配设备,收益不足就回滚
别全机改:全机统一会把不匹配的设备一起换掉——例如机械盘被塞进面向固态的选项。结果往往是一个地方没变好,另一个地方变差了。
优先改匹配设备:按设备匹配规则下发,并持久化(否则重启就变回去)。判据是:只有匹配的设备变了,其它设备原样。还要注意因果顺序:规则写完要让系统重新读取(通常需要重载或重启才会被读到),然后才谈"重启后仍是你的选择"。
记录原值:动手前把当前生效项记下来,这是回滚的依据。
收益不足就回滚:"改了没坏"不是保留它的理由。不回滚、也不记录,等于给系统留下一次没人验证过的变更。
# 动作形态(只给顺序;不在生产环境直接试)
# 1) 记录原值:当前生效项(就是列表里方括号中的那个)
# 2) 运行时切换:写该设备的队列文件(立即生效,重启丢失)
# 3) 持久化:按设备匹配规则下发,只对匹配到的设备生效(重启后仍是你的选择)
# 4) 复核:现读生效项——只有匹配设备变了,其它设备原样
# 回滚:把值改回原值(或删掉那条匹配规则),再复核现读回到原值
收益怎么判:用你自己的负载,同口径前后比
| 指标 | 改前基线怎么取 | 改后怎么比 | 结论 |
|---|---|---|---|
| 业务响应时间(你的关键请求) | 在同一个业务时段取一段稳定期的值 | 同一时段、同一口径再取一段 | 明显变好 → 有效;没有变化 → 无收益,应回滚 |
| 设备的排队与等待形态 | 现读队列相关参数与等待情况 | 用同样的方式现读 | 排队变长、等待变差 → 应回滚 |
| 业务吞吐 | 同一时段、同一接口 | 同上 | 只有吞吐上升、响应没有变差,才算改善 |
| 三条纪律: |
- 用真实业务请求来判断,不要拿别人的基准数字当自己的结论;
- 不要在生产盘上跑写压测——那会把"验证"本身变成事故;确实需要压测,就在副本或测试盘上做;
- 同一指标、同一口径前后比(这条与"两个指标打架时先信谁"的纪律一致)。
取基线时有两个细节:同一个业务时段(避开与业务无关的窗口,例如备份时段),以及同一类请求(读密集与写密集的请求分开看——它们的瓶颈可能并不相同)。如果业务本身波动很大,宁可把观察窗口拉长,也不要拿两段性质不同的数据互比。
验证清单:每项一条判据
- 现读生效项=你期望的那个,并且只有匹配设备变了、其它设备原样。
- 重启后仍是你的选择:持久化生效。
- 同一指标、同一时段前后对比有明显改善。
- 其它设备没有变差:全机层面的指标也要复核一遍。
- 回滚可用:把值改回去,现读能回到原值。
- 验证方式固定成一句话:现读确认范围与持久化、同口径对比确认收益,两者都对,才算通过。
这篇不解决什么
- 不处理文件系统与回收:讲《文件系统选型与回收取舍》的那篇在文件系统与回收层;本篇在块设备调度层。
- 不处理异常分支:讲《云盘变慢或报 I/O 错误时你能做什么》的那篇处理变慢与报错的取证与迁移;本篇处理常态调优的取舍——要不要动调度器、怎么判收益。
- 不重复读数口径理论:讲《内存口径打架时信谁》的那篇是读数口径纪律;本篇只沿用它的"同口径前后比"这一条要求。
踩坑清单
- 照抄网上的参数表:不看设备类型,也不看当前生效项。
- 全机统一改:不匹配的设备被误伤,一个地方没变好、另一个变差。
- 只改运行时、不持久化:重启之后变回默认,还以为自己改过。
- 改了不回滚也不记录:留下一次没人验证过的变更。
- 用别人的基准数字当自己的结论:硬件与负载都不同。
- 在生产盘上跑写压测:把验证做成了事故。
- 拿"感觉快了"当判据:没有同口径的前后对比。
- 忘了宿主侧也在排队:以为客户机这一层是决定性的。
- 瓶颈其实在存储能力或共享后端,却在调度器上反复折腾。
- 改完不看同一指标的前后对比:无法判断有没有收益。
- 把"内核默认选的那个"当成需要改的对象:默认值通常已经是合适的选择。
- 只读一次列表就下结论:要看清方括号在哪里,而不是只看有哪些名字。
- 点赞
- 收藏
- 关注作者
评论(0)