磁盘 IO 打满:iowait 居高不下的定位与治法

一、现象与影响面:机器变卡但 CPU 不忙

症状很有辨识度:应用响应出现尖刺,但 CPU 使用率不高;ssh 登录比平时慢半拍;命令行 tab 补全卡一下;top 一看,%wa 一栏居高不下。wa(iowait,等待 IO 的 CPU 时间占比)高说明 CPU 没病,它在等磁盘——瓶颈在存储侧。

反模式一:一卡就重启,一见大文件就 rm。rm 掉几个 G 的日志本身就是一次重 IO,会让已经打满的盘雪上加霜;重启则丢掉全部现场。IO 问题的证据都在实时计数器里(iostat、/proc),动手前先采一轮。

排查主线四层:确认是不是 IO(iostat)→ 找进程(iotop/pidstat)→ 找文件(fd 与 fatrace)→ 分层治法。所有命令默认在宿主机上跑;容器本质上就是宿主机进程,宿主机视角最全。

二、第一层:确认”是不是 IO”——iostat -x 怎么读

# 宿主机上;sysstat 包自带,没有就装:apt install sysstat 或 yum install sysstat
iostat -x 1 5        # 每 1 秒采一次、共 5 组;第一组是开机以来的均值,判断看后几组

vmstat 1 5           # 复核:si/so 持续非零说明在换页;b 列(不可中断进程)持续大于 0 与 iowait 互相印证

关键列速查表(量级为经验参考值,非实测):

列 含义 参考量级
r/s、w/s 每秒读/写请求数 无绝对标准,看基线
rkB/s、wkB/s 每秒读/写吞吐 机械盘上限约百 MB/s 级,SSD 再高一个量级以上
r_await、w_await 单次读/写平均耗时(毫秒,含排队时间) SSD 亚毫秒到 2ms;机械盘 5-15ms;持续远超基线即异常
aqu-sz 平均队列长度(老版本 sysstat 列名是 avgqu-sz) 持续高于设备并发能力即堆积
%util 设备忙于处理 IO 的时间占比 机械盘逼近 100% 即接近饱和

组合判读:

读数组合 解读 下一步
%util 高 + await 高 设备真饱和,请求在排队 进第三节找进程
%util 高 + await 低 持续小 IO 但没排队,未必是病 对照历史基线再定
await 正常、应用仍慢 慢在别处 转延展阅读的 CPU 分诊一篇
w_await 高、r_await 正常 写路径堵:日志与刷盘类根因嫌疑大 第四节重点看写文件

两个必要提醒。其一,%util 对 NVMe 这类多队列设备会骗人——设备能并行处理多路 IO,%util 100% 不代表饱和,判断以 await 为准。其二,”异常”必须有基线才成立:第一次排查顺手把正常时段的 iostat 存档,以后所有判断都有参照。

三、第二层:找进程——iotop 与 pidstat

# 宿主机上,需要 root
iotop -oPa            # -o 只看有 IO 的,-P 按进程聚合(不拆线程),-a 按累计量排序
pidstat -d 1 10       # 每秒采一次进程级读写 kB/s,共 10 轮,适合留档对比
cat /proc/<PID>/io    # 单进程精确读数:write_bytes 是真正落盘的字节,wchar 只是请求量

1. 容器环境多一步认领:宿主机 PID 对回容器名
docker ps -q | xargs docker inspect --format '{{.Name}} {{.State.Pid}}'
1. /app  12345      ← PID 12345 就是 app 容器的主进程

容器里再看反而片面(PID namespace 只显示自己那一截),宿主机定位、必要时才 docker exec 进去复核——与内存排障同一思路。这层锁定嫌疑人(比如 mysqld、某个 node 进程、rsyslogd),进第三层找它写的是哪个文件。

四、第三层:找文件——谁在写、写哪个文件

# 宿主机上,需要 root
ls -l /proc/<PID>/fd | head -20     # 进程当前打开的文件,一眼看到在写哪个日志
lsof -p <PID> | grep REG            # 同样信息的 lsof 视角,可再 grep log 过滤

fatrace | grep -i mysql             # 实时流:哪个进程在动哪个文件(fatrace 需单独安装)
1. mysqld[1234]: W /var/lib/mysql/ib_logfile0

1. Docker 日志专项体检:json-file 驱动默认不带轮转,会无限长
du -sh /var/lib/docker/containers/*/*-json.log 2>/dev/null | sort -rh | head -5

dmesg -T | grep -iE 'i/o error|blk_update'    # 出现 I/O error 则有硬件层嫌疑,见根因表末条

fatrace 的输出把进程和文件对上了,这是多数根因的最后一环:日志风暴会看到某应用高频 W 同一个 log 文件;数据库刷盘会看到 redo 日志或 RDB 临时文件;备份任务会看到 tar/gzip 的读与目标文件的写。

五、根因清单:按出现频率排序

根因 特征 一行验证
日志风暴(debug 刷屏、错误循环重试) 某应用 write_bytes 疯涨、w_await 高 /proc/PID/io 对照日志文件增长速度
容器日志无轮转 /var/lib/docker/containers 下巨型 json.log 第四节的 du 一条命令
swap 抖动 si/so 持续非零,多见于内存紧张的机器 vmstat 1 看 si/so;内存侧排查见延展阅读 OOM 一篇
数据库刷盘 定点尖峰,与 checkpoint/RDB 保存点时刻吻合 尖峰时间对数据库日志
备份、压缩任务撞业务高峰 定点出现,与 crontab 对得上 crontab -l 对照时间戳
页缓存反复回收 内存 available 低、回写频繁 free -h 看 available
硬件衰减 dmesg 有 I/O error,await 全线抬高 dmesg;进一步用 SMART 工具查健康度,以厂商工具为准

六、治法:分层下药

层 招 代价与边界
应用层 日志降到合适级别、错误重试加退避、高频日志改采样 治本,但要改代码
配置层 日志轮转全覆盖;Docker 配 max-size/max-file;定时任务错峰 轮转本身占额外磁盘,配监控别转出新的”磁盘满”
系统层 swappiness 调小减少换页;cgroup io 限额给容器划边界 调脏页刷新参数(vm.dirty_*)会放大掉电丢数据的窗口,慎动
硬件层 机械盘换 SSD、加内存扩页缓存 最贵,但加内存同时缓解 IO 与 OOM 两种病

配置层的 Docker 日志轮转给个可抄的样板:

# compose 里给单个服务配;全局则在 /etc/docker/daemon.json 配 log-driver 与 log-opts
services:
  app:
    logging:
      driver: json-file
      options:
        max-size: "50m"
        max-file: "3"

网上常见”用 ionice 让备份让路”的招,注意它只在 CFQ/BFQ 这类调度器下有效,NVMe 默认的 none 调度器会无视 ionice——用了没效果先查调度器(cat /sys/block/<盘>/queue/scheduler)。

反模式二:把别人的治法当配置包抄。看到”iowait 高就调 vm.swappiness”就抄——swap 抖动导致的 IO 高,病根在内存预算,内存够用的机器根本不该有大 swap 流量;不确认根因先调参,等于闭眼吃药,参数还留在系统里继续起作用。

七、预防

措施 做什么 边界
基线与告警 定期采 iostat 存档,await 与 %util 超基线告警 没有基线的阈值告警全是误报
轮转巡检 cron 巡检 docker 日志目录与应用日志大小 新上线的服务也要纳入,漏一个就白巡
任务错峰表 备份、压缩、批量任务登记时间窗,避开业务高峰 跨团队任务要有共同的排期视图
容量与内存规划 内存富余则页缓存足,IO 压力天然小 与内存限额策略通盘考虑,别按下葫芦浮起瓢

这套走法收拢成一句:先确认(await 才是判决,%util 会骗人),再找进程(iotop/pidstat),再找文件(fd 与 fatrace),最后分层治(应用 → 配置 → 系统 → 硬件)。每一步都有明确的”看到什么才进下一步”,这比记住任何根因清单都重要。

延展阅读:内存侧的孪生问题(swap 抖动、页缓存压力的另一半真相)见《OOM 与内存泄漏的定位》;应用”慢”但 IO 正常时先做 CPU 侧分诊,见《CPU 100% 的定位路径》;本文命令的日常基本功见《Linux 常用命令》。

版本核实(以官方文档为准):iostat 列名随 sysstat 版本有 aqu-sz 与 avgqu-sz 之别,列含义以 sysstat 手册为准;%util 在多队列设备上的局限、ionice 依赖 CFQ/BFQ 调度器为社区长期共识;Docker json-file 默认无轮转与 logging options 为长期稳定文档。文中量级数字为经验参考值,非实测数据。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇