磁盘IO打满了?别急着换SSD,先花5分钟定位真凶
CPU 正常、内存正常、负载 30+?——这次真不是 CPU 的锅
——上篇讲了 CPU,这篇讲它"最常甩锅"的兄弟:磁盘 IO(附银行 3 个真实案例)
阅读时长:约 6 分钟
适合谁:Linux 运维、系统管理员、DBA、银行/金融运维
前置阅读:《CPU飙到99%?一条命令定位罪魁祸首》
你能得到:3 步定位法 + iostat 4 个核心指标 + 银行 3 个真实案例 + 根因速查表
一、一个容易被忽视的信号
上篇 CPU 排查文章里,我提到过一句话:
如果
top里wa(I/O 等待)那列很高,说明 CPU 在等磁盘——这不是 CPU 的锅,去看磁盘。
今天就展开讲这个"去看磁盘"。
上周有位读者在后台给我发了一段截图:top 显示 CPU 使用率只有 15%,内存剩了 40%,但系统负载飙到了 30+,应用接口全部超时。
他第一反应是 CPU 有问题。
但其实——CPU 没问题,它在等磁盘。
磁盘 IO 瓶颈有一个很"迷惑"的特征:CPU、内存、网络全看着正常,但系统就是慢。 因为 CPU 的利用率是被 IO 等待稀释了——它大部分时间在"歇着"等磁盘回应,所以 top 里 CPU 的使用率反而不高。
这种问题在银行环境里特别常见,原因我后面说。
先讲排查方法。
二、第一件事:读懂 iostat(大部分人只看一个数字)
SSH 上去,第一件事不是 top,而是:
iostat -xz 1 3
输出示例(这是一个写密集型场景的典型数据):
Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await %util
sda 0.00 15.00 120 800 500 32000 78.2 8.50 42.3 2.0 45.2 98.5
100 个运维里,80 个只盯着
%util。 看到 98.5% 就喊"磁盘满了",然后去找存储厂商。但
%util只是告诉你"磁盘有多忙",没告诉你"它忙得有没有效率"。
真正判断 IO 是否有问题的关键是这 4 个指标:
| 字段 | 含义 | 健康线 | 危险信号 |
|---|---|---|---|
| await | IO 请求平均响应时间(ms) | < 10ms | > 30ms,磁盘扛不住了 |
| %util | 磁盘忙碌时间占比 | < 70% | 接近 100%,已达性能上限 |
| r/s, w/s | 每秒读/写次数(IOPS) | 视磁盘类型 | 机械盘 > 300 就可能饱和 |
| avgqu-sz | 平均 IO 队列长度 | < 2 | 接近设备队列深度上限,说明请求排队严重 |
💡 补充:NVMe SSD 的特殊性
以上健康线主要针对机械盘(HDD)和 SATA SSD。NVMe SSD 因为多队列并行能力(最多 64K 队列),%util经常跑到 100% 但await只有 0.1ms——它只是"忙",不是"忙不过来"。判断 NVMe 是否有瓶颈,重点看await,不用太在意%util。
一个很多人不知道的判断技巧
%util 100% 不等于磁盘有问题。
如果 await 只有 2ms,说明磁盘虽然忙,但处理速度极快——它能扛住。
真正让人崩溃的组合是:
%util接近 100% +await很高 → 磁盘既忙又慢,请求越积越多,雪崩开始了。
记住这个组合,比死记 %util 有用十倍。
三、3 步定位法:从"磁盘慢"到"谁干的"
Step 1:看读写比例——读爆了还是写爆了?
iostat -x 1
观察 r/s 和 w/s 的比例,以及 rkB/s、wkB/s:
- r/s 远大于 w/s → 读密集型(典型场景:数据库查询、日志检索)
- w/s 远大于 r/s → 写密集型(典型场景:日志写入、备份同步、数据导入)
- 两者都高 → 混合压力
这一步决定排查方向,别跳过。
🏦 银行实战 1:某次核心系统批量对账,数据库响应时间从 3ms 飙到 800ms。
iostat显示r/s高达 3200,但w/s只有 50。纯读压力——后来发现是对账程序没有走缓存,每条记录都直接命中数据库的物理读。加了 Redis 缓存层后,IO 峰值降了 70%。
Step 2:找出哪个进程在疯狂读写
iotop -o
-o 只显示正在做 IO 的进程。按 r 键按读排序,按 w 键按写排序。
一眼就能看到是 MySQL、Java 日志、还是某个备份脚本 在抢磁盘。
如果没装
iotop:yum install iotop或apt install iotop不想装也没关系,一行替代命令:
pidstat -d 1 3
🏦 银行实战 2:某次生产环境应用超时,
iotop显示排名第一的不是业务进程,而是clamd(ClamAV 杀毒引擎)。原来是安全部门在所有应用服务器上部署了实时文件扫描,每次业务写日志,
clamd就跟读一遍。业务写 1 次,实际产生 2 次 IO。后果:全行 200 多台应用服务器的 IO 峰值翻倍。
最后改为定时扫描(每小时一次),实时监控改为 inotify 事件触发,IO 峰值降了 45%。
Step 3:定位到具体文件
知道进程 PID 了,看看它在读写哪些文件:
lsof -p <PID> | grep -E "REG|DIR"
或者更硬核的做法——用 strace 抓文件打开事件:
strace -p <PID> -e trace=open,openat -c
等 10 秒按 Ctrl+C,-c 模式会按调用次数排序输出,出现最多的一定是最频繁读写的文件路径。
⚠️ 不要用
strace -e trace=read,write -c来找文件——read/write系统调用统计里只有 fd(文件描述符数字),没有文件名。要看路径必须抓open/openat。⚠️
strace会短暂影响目标进程性能,生产环境慎用,建议在测试窗口或低峰期操作。lsof无侵入,优先使用。💡 如果
lsof找不到可疑的 REG 文件,IO 可能来自匿名内存映射(mmap)或管道。用vmstat 1看是否有持续的si/so(swap in/out),确认是否触发了 swap IO。
四、完整复盘:银行批量备份"雪崩"事件
上面三步讲完,用一个完整的银行案例串一遍。
背景:某国有行清算系统,每周二、四凌晨 2 点准时 IO 飙升,持续 45 分钟到 1 小时。期间核心交易响应时间从 50ms 飙到 3 秒以上。
现象:%util 100%,await 飙到 200ms,但 CPU、内存、网络全正常。
排查过程
Step 1 — iostat -x 1 → w/s 1200,wkB/s 45MB,写压力极大,读很低。
Step 2 — iotop -o → 发现 rsync 进程吃掉了 80% 的写 IO。
Step 3 — lsof -p 17234 → 发现它在把应用日志同步到备份服务器,而且每次都是全量同步当天全部日志(约 80GB)。
根因:
之前的运维同事写的备份脚本,每次都用 rsync -av 全量同步,没有用 --link-dest 做增量。随着日志量逐月增长,同步时间从 10 分钟膨胀到 45 分钟,IO 带宽直接被打满。
修复:
#!/bin/bash
# 修改前:全量同步,每次传 80GB
rsync -av /app/logs/ backup_server:/backup/logs/
# 修改后:判断是否有昨天的备份,有则增量,无则全量
TODAY=$(date +%Y%m%d)
YESTERDAY=$(date -d "1 day ago" +%Y%m%d)
DEST_BASE=/backup/logs
if [ -d "$DEST_BASE/$YESTERDAY" ]; then
# 增量:硬链昨天的文件,只传变化的
rsync -av --link-dest="$DEST_BASE/$YESTERDAY" \
/app/logs/ "backup_server:$DEST_BASE/$TODAY/"
else
# 首次或昨天备份不存在:全量
rsync -av /app/logs/ "backup_server:$DEST_BASE/$TODAY/"
fi
💡
--link-dest指向的目录必须是已存在的完整备份。首次运行时如果昨天的目录不存在,不加判断直接用会静默退化为全量复制。上面这个脚本做了兼容处理。
同步时长从 45 分钟降回 8 分钟,IO 峰值从 100% 降到 35%。
你看,问题根源不是磁盘慢,而是备份策略设计失误。 换 SSD 只能多撑半年,该崩还是会崩。
这也是为什么银行运维有一句话:“能用策略解决的问题,不要用硬件兜底。”
五、根因速查表(建议收藏)
| 现象 | 可能原因 | 确认命令 | 解决方向 |
|---|---|---|---|
await 高 + r/s 高 |
随机读太多(大量小查询) | iotop,pt-query-digest |
加缓存层、优化慢查询、换 SSD |
await 高 + w/s 高 |
顺序写或随机写压力大 | iotop,lsof 看目标文件 |
异步写、合并写、日志分级 |
%util 不高但 await 很高 |
磁盘坏道 / 控制器故障 | smartctl -a /dev/sda |
换盘,检查阵列卡状态 |
avgqu-sz 持续 > 10 |
IO 队列深度过大 | iostat -x,检查应用连接池 |
限流、拆盘、加缓存 |
iostat 正常但业务慢 |
文件系统挂了 sync 模式 |
mount | grep sda |
改为 async 挂载 |
iostat 正常但业务慢 |
NFS/NAS 后端网络延迟 | mount | grep nfs,nfsstat |
检查 NFS 挂载参数(noatime、rsize/wsize),排查网络 |
| 只在特定时间段飙升 | 定时任务冲突(备份、跑批) | crontab -l,ls /etc/cron* |
错峰执行、改增量策略 |
| 只在某块盘上飙升 | 日志/数据没做分区隔离 | iostat -x 对比各设备 |
把高 IO 业务拆到独立磁盘 |
六、一张图记住全流程
应用变慢 / 负载高 / 接口超时
↓
vmstat 1 看 wa 列
↓
wa 高?→ iostat -xz 1
↓
看 %util + await 组合
↓
┌────┴────┐
↓ ↓
读高 写高
↓ ↓
iotop -o iotop -o
找读进程 找写进程
↓ ↓
lsof -p lsof -p
找文件 找文件
↓ ↓
└────┬────┘
↓
判断根因:慢查询?日志?备份?杀毒扫描?
↓
对症下药(优化策略,而非换硬件)
七、实操练习(5 分钟就能上手)
练习 1:自己制造 IO 压力,再排查它
打开终端,模拟一个写压力:
# 注意:不要写在 /tmp 上!很多系统的 /tmp 是 tmpfs(内存文件系统),
# 写 tmpfs 不经过磁盘,iostat 抓不到变化。
dd if=/dev/zero of=/app/io_test bs=1M count=5000 oflag=direct &
oflag=direct绕过页缓存,确保 IO 真实落盘,iostat才能观察到变化。
同时打开另一个终端:
iostat -x 1 # 观察 w/s 和 %util 飙升
iotop -o # 抓到 dd 进程
lsof -p $(pgrep dd) | grep REG # 确认它在写 /app/io_test
做完后记得清理:rm -f /app/io_test
练习 2:一行命令找出当前最耗 IO 的进程
# 按磁盘读写总量排序(单位 KB/s)
pidstat -d 1 3 | awk 'NR>3{print $0}' | sort -k3 -rn | head -10
💡
pidstat -d的列号在不同 sysstat 版本(10.x / 12.x)有差异,用 awk 过滤表头后再排序更稳妥。
把这一行存成 alias io_top,下次直接敲。
八、写在最后
上篇 CPU 文章的结尾我说过一句话:“先定位,后操作。重启是最后的手段,不是第一选择。”
磁盘 IO 问题也一样。
换 SSD 可以掩盖很多问题,但不懂定位的运维,永远在背"换硬件"的锅。
今天的三步法:
iostat -xz看指标(不要只看 %util,看 await 组合)iotop -o抓进程lsof -p定位文件
掌握它,你就能在老板喊"找存储厂商"之前,用数据告诉他真相。
这篇文章和上篇 CPU 排查文章是同一系列的,建议两篇连着看——排查系统性能问题,CPU 和 IO 是最先看的一对。
📌 下期预告:《IO 排队太长怎么办?用 blktrace 看内核 IO 栈》
💬 你遇到过最离谱的磁盘 IO 问题是什么?
- A. 备份脚本把磁盘打满,业务跟着陪葬
- B. 安全杀毒引擎"帮倒忙",IO 翻倍
- C. 开发把日志写到了系统盘根分区
- D. 其他(评论区说说你的故事)
👇 评论区留言,我看到必回。 分享最精彩的 3 位,下期文章开头置顶展示。
关注「云间豹变」,会不定期Linux系统排障技巧(从入门到进阶),系统掌握知识点,应急更高效!
| 回复关键词 | 获取内容 |
|---|---|
命令手册 |
Linux 运维命令速查 PDF 版 |
脚本模板 |
运维脚本打包下载 |
提示词 |
AI 运维提示词模板合集 |
📌 系列文章:
- 《CPU飙到99%?一条命令定位罪魁祸首》—— 银行支付网关事故完整复盘
- 《线上网络丢包?5步+tcpdump快速定位》—— 薛定谔的故障排查实录
- 《小王把脚本贴给AI,差点变成安全事件》—— 代码脱敏+安检实操指南
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)