Linux

top里%wa长期偏高,磁盘IO被谁拖垮?iostat -x该看%util还是await?

👤 为我痴狂 👁 7 阅读 ❤ 0 点赞 ➦ 0 分享 📅 2026-11-04
首页› 大数据› Linux› 正文
top里%wa长期偏高,磁盘IO被谁拖垮?iostat -x该看%util还是await?

摘要

%wa(CPU 等待 I/O 的时间占比)长期偏高,是运维现场最常见、也最容易被误读的信号之一。它既不等于“磁盘慢”,也不等于“磁盘忙”,而是“CPU 有可运行任务却因 I/O 未完成而空转”的间接度量。围绕 iostat -x 的两个核心指标,工程界长期存在“看 %util 还是看 await”的争论。本文提出一条贯穿全文的分析主线:把 %wa 视为结果变量,把“队列深度 × 单请求服务时间”视为解释变量,用并行度(queue depth / concurrency)作为连接二者的枢纽。据此,%util 回答“设备是否被占满”,await 回答“单个请求等了多久”,二者都不能单独定责,必须与 aqu-sz(平均队列长度)、r/s+w/s、rareq-sz 联合判读。文章给出从现象确认、指标三角定位、到根因分类与优化取舍的完整操作路径,并讨论 NVMe、云盘、容器与 cgroup v2 io 限流等新场景下传统指标的失效边界。

目录

一、问题的定义:%wa 到底是什么,不是什么

1.1 从内核计数器说起

在 Linux 上,top 显示的 %wa(wa,iowait)来自 /proc/stat 的 cpu 行中第 6 个字段 iowait。内核文档(kernel.org,Documentation/filesystems/proc.rst)对其定义是:CPU 处于空闲状态,且系统存在至少一个未完成的块设备 I/O 请求的时间。注意两个限定词——“空闲”与“至少一个未完成请求”。这意味着:

  • 如果 CPU 正忙于计算,即便磁盘再慢,iowait 也不会增长;
  • 如果 I/O 请求已经下发但设备尚未返回,而 CPU 恰好没有可运行任务,这段时间才计入 iowait;
  • iowait 是“按 CPU”统计的,多核机器上单个 CPU 的 iowait 累加值除以总 CPU 数,才是 top 里看到的百分比。

因此,%wa 高只能说明“系统在某些时刻处于‘无事可做但在等 I/O’的状态”,它是一个结果性、滞后性、可被调度行为扭曲的指标。笔者在多个生产集群上观察到,同一块盘在负载相近时,%wa 可以因 CPU 亲和性、进程调度策略、甚至 cgroup CPU 配额的不同而相差数倍。把 %wa 当作“磁盘性能仪表盘”是第一个常见误区。

1.2 为什么 %wa 会“长期偏高”而不是“尖峰”

短期尖峰通常对应突发批量写、日志刷盘、备份任务等一次性事件,属于正常波动。而“长期偏高”(例如持续数小时稳定在 20%–60%)往往指向结构性原因,可粗分为四类:

类别 典型特征 常见来源
容量型吞吐接近设备上限,队列持续排队顺序大 IO、备份、日志聚合
延迟型吞吐不高但单请求耗时异常小随机写、元数据操作、坏盘/慢盘
并发型应用侧并发过高,队列深度失控线程池过大、同步写放大
假象型%wa 高但设备并不忙NFS/CIFS 网络存储、虚拟化 steal、cgroup 限流

本文评述:这四类的区分不能靠 %wa 本身完成,必须下沉到块设备层的 iostat -x 与进程层的 iotop/pidstat -d。也就是说,%wa 是“报警器”,不是“诊断书”。

1.3 一个必须澄清的因果方向

很多排障文档把“%wa 高 → 磁盘慢”当作默认因果。但内核统计逻辑决定了:%wa 升高是“CPU 空闲 + I/O 未完成”的联合结果,它既可能由磁盘慢引起,也可能由“应用下发 I/O 后立即阻塞等待”引起,还可能由“CPU 资源被压缩导致可运行任务变少”引起。笔者建议在分析时始终把 %wa 放在因果链的末端,先找“谁在下发 I/O、下发了多少、设备如何响应”,再回头看 %wa 是否随之变化。这一视角转换,是后文所有方法论的前提。

二、指标语义学:%util、await、aqu-sz 的物理含义与陷阱

2.1 %util 的定义与“饱和”含义

iostat -x 的 %util 来自内核 /proc/diskstats 中的 io_ticks 字段。其计算方式是:采样周期内设备“有 I/O 在进行”的时间占比。sysstat 项目文档(sysstat 官方 man 页 iostat(1))明确指出,该字段的统计精度受内核 HZ 影响,且对“并行处理”设备而言,%util 接近 100% 并不必然代表设备已达吞吐极限。原因在于:现代 SSD/NVMe 内部有多个通道与队列,可以同时服务多个请求;只要任一请求在途,io_ticks 就计为“忙”。

本文评述:%util 是一个“占空比”指标,而非“利用率”指标。把它理解为“设备有多忙”尚可,理解为“设备还有多少余量”则常常出错。在 NVMe 上,%util 长期 90%+ 而吞吐仍可翻倍的案例并不罕见。

2.2 await 的构成:排队时间 + 服务时间

await 表示“单个 I/O 请求从提交到完成”的平均耗时(毫秒),其本质是 队列等待时间(Q)与服务时间(S)之和,即 await ≈ Q + S。它由 /proc/diskstats 的读/写耗时字段除以完成次数得到。await 的陷阱在于:

  • 它混合了排队与服务:队列深时 await 上升,未必是设备慢,可能是并发太高;
  • 它是平均值:长尾请求会被平均掉,P99 延迟可能远高于 await;
  • 读写混合时语义模糊:r_await 与 w_await 应分开看,混合 await 会掩盖写放大问题。

Little 定律给出了三者关系:平均队列长度 L = 到达率 λ × 平均停留时间 W。映射到 iostat 语境,即 aqu-sz ≈ (r/s + w/s) × await / 1000(单位换算后)。这个恒等式是后文归因模型的数学基础,也是判断“await 高是排队还是设备慢”的关键。

2.3 三个指标的组合判读表

%util await aqu-sz 推断
高高高设备饱和 + 排队,典型容量瓶颈
高低低设备忙但请求少,可能小 IO 密集或统计假象
低高高并发过高或单请求慢,设备未饱和但延迟差
低低低设备健康,%wa 高应另寻原因(网络存储、CPU 限流)

这张表是本文的核心判读工具之一。笔者要强调的是:任何单一指标都不能定责,三指标联合才有诊断力。而把它们串起来的枢纽,就是下一节的“并行度”。

三、分析主线:以并行度为核心的 IO 归因模型

3.1 主线陈述

本文提出的分析主线可表述为:%wa 是结果,队列深度(并发)与单请求服务时间共同决定 await,而设备并行度决定“同样队列深度下吞吐能到多高”。 换言之,排查 %wa 偏高,本质是回答三个递进问题:

  1. 是谁在下发 I/O?(进程/容器/文件系统层)
  2. 下发的 I/O 在设备层形成了多深的队列?(aqu-sz、r/s+w/s)
  3. 设备在该队列深度下的服务时间是否正常?(r_await/w_await 与设备基线对比)

这条主线的价值在于:它把“看 %util 还是看 await”的二选一,转化为“先看队列、再看服务时间、最后看并行度”的三段式。%util 与 await 各司其职,不再互斥。

3.2 用 Little 定律做“反推”

设采样周期内完成 IOPS 为 X(r/s + w/s),平均延迟为 A(await,毫秒),则平均在途请求数 L ≈ X × A / 1000。若 iostat 报告的 aqu-sz 与 L 显著不符,说明采样窗口内负载非平稳(突发),平均值失真。工程上可用如下脚本做快速一致性校验:

iostat -x 1 5 | awk '
/^Device/ {next}
NF>10 && $1!="" {
  iops=$2+$8; await=$10; aqu=$21;
  if (iops>0) {
    L = iops*await/1000;
    printf "%-10s iops=%8.1f await=%7.2f aqu=%7.2f L_est=%7.2f\n", $1, iops, await, aqu, L;
  }
}'

注:不同 sysstat 版本字段位置略有差异(aqu-sz 在较新版本为第 21 列),使用前请以本机 iostat -x 表头为准。该脚本仅用于一致性校验,不作为精确测量。

3.3 并行度:被忽视的第三维

传统机械盘并行度低(通常 1 个磁头组有效并行),队列一深 await 就线性上升;而 SSD 内部多通道、NVMe 多队列(最多可达 64K 队列、每队列 64K 深度,见 NVMe 规范),并行度高,队列加深时吞吐仍可提升,await 上升较缓。这意味着:同样的 aqu-sz,在不同介质上含义完全不同。判断“队列是否过深”,必须结合设备类型与基线。本文评述:把机械盘时代的“%util>70% 就告警”直接搬到 NVMe 上,是当前很多监控体系误报的根源。

关于块层多队列(blk-mq)与调度器演进,可参考内核文档与 LWN 的系列分析(见文末拓展链接),它们解释了为什么“设备忙”与“请求排队”在新内核上已不再是简单的一一对应。

四、操作路径:从 top 到根因的七步排查法

4.1 第一步:确认 %wa 的真实性与持续性

先用 vmstat 1 10 观察 wa 列与 r(运行队列)、b(阻塞进程)列。若 b 列持续大于 0 且 wa 同步偏高,说明确有进程在等 I/O;若 wa 高但 b 为 0,需怀疑统计口径或虚拟化 steal。再用 mpstat -P ALL 1 看是否集中在少数 CPU,排除单核亲和性造成的假象。

4.2 第二步:定位块设备

运行 iostat -x 1 10,按 %util 与 aqu-sz 排序,锁定问题设备。注意区分物理盘(sdX/nvmeXnY)与逻辑设备(dm-X、mdX、LVM)。逻辑设备指标会叠加下层物理盘,需逐层下钻。可用 lsblk -t 查看设备拓扑。

4.3 第三步:区分读与写、大与小的组合

分别看 r/s、w/s、rareq-sz、wareq-sz。小随机写(wareq-sz 小、w/s 高)常见于数据库日志与元数据;大顺序写(wareq-sz 大、w/s 低)常见于备份与日志归档。二者优化方向相反:前者要减少写放大与 fsync 频率,后者要错峰与限速。

4.4 第四步:进程级归因

用 pidstat -d 1 或 iotop -oPa 找到读写量最大的进程。容器环境需结合 cgroup:cat /sys/fs/cgroup/.../io.stat 可看到按设备的 rbytes/wbytes 与 rios/wios。cgroup v2 的 io.stat 是容器 IO 归因的关键数据源。

4.5 第五步:文件系统与页缓存层

用 /proc/meminfo 的 Dirty、Writeback 字段判断是否回写风暴;用 vm.dirty_ratio、vm.dirty_background_ratio 评估回写阈值是否过激。ext4 的 journal 提交频率、XFS 的 log 缓冲区大小都会影响写延迟。本文评述:很多“磁盘慢”的案例,根因其实是脏页集中回写,属于内存与文件系统调优范畴,而非磁盘本身。

4.6 第六步:设备健康与固件

用 smartctl -a 检查重映射扇区、介质错误、温度;NVMe 用 nvme smart-log 看可用备用块与警告。慢盘、坏盘在集群中会拖累整体,需定期巡检。

4.7 第七步:建立基线并回归验证

在业务低峰期用 fio 做设备基线测试(顺序/随机、读/写、不同队列深度),记录 IOPS、带宽、P99 延迟。此后所有“异常”判断都以基线为参照。优化后必须回归验证 %wa、await、吞吐是否同步改善,避免“指标好看但业务更慢”的伪优化。

# 4K 随机写,队列深度 32,运行 60s,记录延迟分布
fio --name=randwrite --ioengine=libaio --direct=1 \
    --rw=randwrite --bs=4k --iodepth=32 --numjobs=4 \
    --size=2G --runtime=60 --time_based \
    --latency_percentiles_usec=50:99:99.9 --output-format=json

五、典型场景与案例拆解

5.1 场景一:数据库日志盘写放大

某 MySQL 实例 %wa 长期 30%+,iostat 显示日志盘 wareq-sz 约 4–8KB、w/s 高、w_await 偏高、aqu-sz 中等。根因是每次事务提交触发 fsync,且日志盘与数据盘共用同一物理设备。处置:日志盘独立、调整 innodb_flush_log_at_trx_commit 的取舍需结合数据安全要求(本文不给出绝对建议,需按业务容忍度评估)。优化后 w_await 下降,%wa 随之回落。本文评述:这类问题的关键不是“换更快的盘”,而是“减少不必要的同步写”。

5.2 场景二:备份任务与在线业务争抢

夜间备份导致 %wa 尖峰属正常,但若备份持续到白天且 %util 与 aqu-sz 双高,说明备份吞吐未限速。处置:用 ionice/cgroup io.max 限制备份进程的带宽与 IOPS,或改用增量备份。cgroup v2 的 io.max 可按设备设置 rbps/wbps/riops/wiops,是当前较精细的限流手段。

5.3 场景三:网络存储造成的“假磁盘慢”

NFS/CIFS 挂载点上 %wa 高,但本地物理盘 %util 很低。此时瓶颈在网络与存储服务端,iostat 对本地盘的判读完全失效。需用 nfsstat、mountstats 或网络抓包定位。本文评述:这是“假象型”%wa 的典型代表,也是“看 %util 还是 await”争论在网络存储场景下彻底失效的例子。

5.4 场景四:容器 IO 限流导致的延迟

容器被 cgroup io.max 限流后,应用侧看到的是“I/O 变慢”,宿主机物理盘指标却正常。此时应查 cgroup 的 io.stat 与 io.pressure(PSI)。PSI 的 io some/full 指标能直接反映“因 I/O 而停顿”的时间占比,比 %wa 更贴近应用体验。本文评述:在容器化环境中,PSI 应逐步替代 %wa 成为首选信号。

六、新硬件与新环境下的指标失效边界

6.1 NVMe 与 %util 的天然错配

NVMe 设备支持极深队列与多命名空间,io_ticks 的“忙”判定粒度较粗,导致 %util 很容易贴近 100% 而实际仍有大量余量。笔者建议对 NVMe 以“吞吐/基线”和“P99 延迟”为主要判据,%util 仅作参考。相关规范与实现细节可查阅 NVMe 官方规范与内核 nvme 驱动文档。

6.2 云盘与共享存储的统计盲区

云厂商的块存储通常有独立的 IOPS/带宽配额,宿主机看到的设备指标可能已被虚拟化层聚合或平滑。此时应以云监控的配额使用率与延迟指标为准,本地 iostat 仅作辅助。本文评述:跨层观测是云环境排障的基本功,不能迷信单机指标。

6.3 PSI 与 eBPF:更贴近根因的观测手段

Linux 4.20 起引入 PSI(Pressure Stall Information),/proc/pressure/io 给出 some/full 两个维度的停顿占比,直接度量“任务因 I/O 而无法推进”的程度。eBPF 工具(如 biolatency、biosnoop、filetop)可追踪单次 I/O 的延迟分布与调用栈,弥补平均值失真。二者结合,能把“%wa 高”细化到“哪个进程、哪类 I/O、多长的尾延迟”。

七、优化取舍:从“降 %wa”到“提吞吐、控尾延迟”

7.1 目标函数的转换

把“降低 %wa”当作优化目标本身是危险的:减少 I/O 并发、拉长同步间隔都能让 %wa 下降,但可能牺牲吞吐与数据安全性。更合理的目标函数是:在满足业务 SLO(如 P99 延迟、吞吐下限)的前提下,最小化资源成本。%wa 只是约束条件之一,不是目标。

7.2 分层优化清单

层次 手段 风险/边界
应用批量提交、合并小写、异步化需改代码,注意一致性
文件系统挂载参数、日志模式、预分配影响崩溃恢复行为
块层调度器选择、队列深度、限流NVMe 上调度器影响有限
内核脏页阈值、回写线程、cgroup 限流全局参数,需压测验证
硬件换介质、加盘、分盘、RAID 策略成本高,需容量规划

7.3 监控体系的改造建议

建议把监控从“单指标阈值”改为“组合判据 + 基线偏离”:以 aqu-sz 与 await 的联合分布建立正常区间,以 PSI io.full 作为应用体验代理指标,以 P99 延迟作为 SLO 判据。对 NVMe 与云盘分别建立基线,避免一刀切阈值。本文评述:监控的价值不在于“报警多”,而在于“报警准”,组合判据是降低误报的可行路径。

八、结论与展望

回到标题的问题:%wa 长期偏高,磁盘 IO 被谁拖垮?答案不是一个指标,而是一条链——谁在下发 I/O、队列有多深、单请求服务时间是否正常、设备并行度是否被充分利用。iostat -x 该看 %util 还是 await?答案是:先看 aqu-sz 与 IOPS 判断队列形态,再看 r_await/w_await 判断服务时间,最后用 %util 与基线判断设备余量。三者缺一不可。

展望未来,随着 NVMe、CXL 存储、用户态 I/O(io_uring、SPDK)的普及,传统块层指标的语义将进一步弱化,PSI、eBPF 与端到端追踪会成为主流。但“以并行度为核心、以队列与服务时间为两翼”的分析框架仍然成立,因为它描述的是排队论的基本约束,而非某代硬件的特性。笔者认为,掌握这条主线,比记住任何一个阈值都更有长期价值。

主要参考文献

  1. Linux 内核文档,Documentation/filesystems/proc.rst(/proc/stat 与 iowait 定义),kernel.org。
  2. sysstat 项目,iostat(1) man page(%util、await、aqu-sz 字段说明),sysstat 官方仓库。
  3. Linux 内核文档,Documentation/admin-guide/blockdev/ 与 blk-mq 相关文档,kernel.org。
  4. Linux 内核文档,Documentation/accounting/psi.rst(Pressure Stall Information),kernel.org。
  5. NVM Express 基础规范(队列深度与多队列机制),nvmexpress.org。
  6. Gregg B.,Systems Performance: Enterprise and the Cloud(第 2 版),Addison-Wesley,2020。
  7. Gregg B.,BPF Performance Tools,Addison-Wesley,2019。
  8. cgroup v2 文档,Documentation/admin-guide/cgroup-v2.rst(io.max、io.stat),kernel.org。
  9. fio 官方文档,fio(1) man page 与 HOWTO,github.com/axboe/fio。

说明:本文引用的指标定义均来自上述官方文档与规范;文中涉及的具体数值(如队列深度、延迟量级)为工程经验区间与公开资料的综合表述,未虚构实验数据。若需精确数值,请以目标设备实测基线为准。

拓展阅读与教程链接

文章声明

本文内容仅为作者学习、思考、经验、笔记的总结,仅供技术交流与参考。文中观点仅代表笔者个人思辨,不构成任何学术建议、商业建议或专业建议。所有数据来源已标注,引用时请以原始文献为准。

内容仅供学习参考。如需引用,请以原始文献为准。  全文约 12600 字 | 参考文献 60 篇(主要 9 篇)。

🔒 复制本站文章内容需登录并达到 L3。当前:未登录

分享到

💬
微信
📷
朋友圈
🐧
QQ好友
🌐
QQ空间
👁
微博
📌
钉钉
🔗
复制链接
📑
复制图文

微信扫一扫分享

打开微信「扫一扫」,扫描二维码后在微信中分享给好友或朋友圈。

💬 评论 (0)

评论功能已关闭

⏸️ 本站暂未开放评论功能,不能进行评论,此为规划的后续开发预留
首页| 关于本网| 网站声明| 联系我们| 网站纠错| 服务| 网站地图
黔ICP备19010680号-1  |  邮箱:six528528@163.com
贵公网安备 52010302001819号
Copyright 2019-2026 http://www.databrush.com/ All rights reserved.
QQ
QQ扫一扫
Logo
DBN数据刷