云服务器磁盘 I/O 飙高?从 iostat 到进程定位的排查顺序
一、场景:机器“卡”了,CPU 却不高
线上云服务器偶尔会出现这样的情况:SSH 登录变慢,接口响应时间从几十毫秒涨到几秒,但 top 里 CPU 的 us、sy 并不高,负载(load average)却明显升高。这类现象很多时候指向同一个方向:磁盘 I/O 成了瓶颈。
Linux 的 load average 不仅统计正在运行和等待 CPU 的进程,也统计处于不可中断睡眠(D 状态)的进程,而 D 状态大多是在等待 I/O。所以“负载高、CPU 闲”是磁盘问题的典型信号。
排查磁盘 I/O 问题,建议按“先确认是不是 I/O → 再定位哪块盘 → 再定位哪个进程 → 再定位哪个文件 → 最后找根因”的顺序进行,避免一上来就重启服务或扩容。
二、排查顺序
第 1 步:用 top / vmstat 确认 I/O 等待
先看 top 第三行的 wa(iowait):
%Cpu(s): 3.1 us, 1.8 sy, 0.0 ni, 60.2 id, 34.5 wa, 0.0 hi, 0.4 si, 0.0 st
(以上为示意输出)
wa 表示 CPU 空闲且存在未完成磁盘 I/O 的时间占比。它偏高说明有进程在等盘,但要注意:wa 只是一个间接指标,CPU 忙的时候 wa 反而可能被“压低”,所以不能只凭它下结论。
再用 vmstat 1 连续观察:
vmstat 1 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
1 6 0 512000 20480 3100000 0 0 120 85000 1500 2200 3 2 61 34 0
(以上为示意输出)
重点看几列:
b:处于不可中断睡眠的进程数,持续大于 0 值得关注;bi/bo:每秒从块设备读入 / 写出的块数;si/so:每秒换入 / 换出的内存量,如果不为 0,要怀疑是内存不足导致的 swap I/O;wa:与 top 中含义一致。
第 2 步:用 iostat -x 定位是哪块盘
iostat 属于 sysstat 包,没有的话先安装(如 yum install sysstat 或 apt install sysstat)。
iostat -x 1
Device r/s w/s rkB/s wkB/s r_await w_await aqu-sz %util
vda 2.0 3.0 16.0 40.0 0.8 1.2 0.01 0.6
vdb 15.0 1800.0 240.0 72000.0 5.3 48.7 87.60 99.8
(以上为示意输出,已省略部分列)
各列含义:
r/s、w/s:每秒完成的读、写请求数,即实际 IOPS;rkB/s、wkB/s:每秒读、写的数据量,即吞吐;r_await、w_await:读、写请求的平均耗时(毫秒),包含在队列中排队的时间和设备服务时间;旧版本只有一个合并的await;aqu-sz:平均队列长度(旧版本叫avgqu-sz),持续偏大说明请求在排队;%util:设备有 I/O 在处理的时间占比。
两点需要特别说明:
svctm已被去掉。较新版本的 sysstat 已不再输出svctm,因为它是推算值、并不可靠,不要再拿它当“磁盘服务时间”来判断。%util在 NVMe、SSD 和很多云硬盘上不可靠。这类设备能并行处理多个请求,%util只表示“这段时间里至少有一个请求在处理”,到 100% 不代表设备已经饱和。判断是否饱和,应结合await是否明显升高、aqu-sz是否持续偏大,以及 IOPS / 吞吐是否接近云硬盘规格上限。
上面的示意中,vdb 写请求多、w_await 高、队列长,可以初步判断是 vdb 上的写入压力过大。可以用 lsblk 或 df -h 确认该盘挂载在哪个目录。
第 3 步:用 pidstat / iotop 定位进程
找到盘之后,下一步是找“谁在读写”。
pidstat -d 1
UID PID kB_rd/s kB_wr/s kB_ccwr/s iodelay Command
0 2345 0.00 68000.00 0.00 120 java
999 1888 12.00 800.00 0.00 5 mysqld
(以上为示意输出)
kB_rd/s、kB_wr/s 是进程每秒读写量;iodelay 是进程等待块 I/O 的延迟(以时钟节拍计),可以帮助发现“写得不多但等得很久”的进程。
也可以用 iotop(需 root 权限):
iotop -oPa
参数含义:-o 只显示有实际 I/O 的进程,-P 按进程而不是线程显示,-a 显示累计量而不是瞬时速率。累计模式适合观察一段时间内谁写得最多,能抓住间歇性的写入。
第 4 步:查 D 状态进程
ps -eo stat,pid,cmd | awk '$1~/D/'
输出中 STAT 列包含 D 的进程处于不可中断睡眠,通常在等 I/O。如果这类进程长期存在且数量不少,基本可以确认 I/O 是瓶颈。必要时可以看 cat /proc/<pid>/stack(需 root)了解它卡在内核哪个调用上。
第 5 步:定位进程在读写哪些文件
确定进程后,看它打开了哪些文件:
lsof -p <pid>
重点关注日志文件、数据文件、临时文件。再看进程累计 I/O 统计:
cat /proc/<pid>/io
rchar: 1203345
wchar: 9876543210
read_bytes: 40960
write_bytes: 9871234048
...
(以上为示意输出)
rchar/wchar 是进程通过 read/write 等系统调用读写的字节数,包含命中页缓存的部分;read_bytes/write_bytes 才是真正落到块设备层的量。间隔几秒取两次相减,就能估算出它真实的磁盘读写速率。
第 6 步(可选):blktrace 深入分析
如果需要分析请求大小、顺序还是随机、时间消耗在哪一阶段,可以用 blktrace 配合 blkparse 或 btt 抓取块设备层事件。它开销较大、数据量多,建议短时间抓取,一般排查用不到这一步。
补充:排查时的几个注意事项
- 先保留现场再处理。在清理日志或重启进程前,把
iostat、pidstat的输出保存下来,方便事后复盘和对比。 - 注意区分读和写。读压力大通常与缓存命中率低、扫描类查询有关;写压力大更多与日志、刷盘策略、批量导入有关,两者的优化方向不同。
- 观察要有时间跨度。I/O 问题常常是周期性的,例如定时任务每小时触发一次,只看一两秒的数据容易漏掉,可以用
sar -d查看 sysstat 采集的历史数据。 - 不要只看单机。同一个应用如果多台机器同时出现问题,更可能是业务流量或公共依赖变化引起的,而不是某块盘本身故障。
三、常见原因与处理
1. 日志暴涨
应用开启了 DEBUG 日志、异常堆栈循环打印,都会造成大量写入。处理:调整日志级别,配置 logrotate 按大小切割和压缩,日志尽量与数据放在不同的盘上。
2. 磁盘或 inode 写满
空间接近写满时,文件系统分配效率会下降;df -h 看空间,df -i 看 inode。大量小文件(如会话文件、缓存碎片)可能导致 inode 用尽,空间还剩但无法创建新文件。处理:清理过期小文件并找出产生它们的程序。
3. 内存不足导致 swap I/O
vmstat 中 si/so 持续不为 0,说明系统在频繁换页,磁盘压力其实来自内存不足。处理:用 free -h、top 按内存排序找出占用大的进程,优化内存或升配,而不是盲目换更快的盘。
4. 云硬盘性能达到上限
云硬盘按类型和容量有对应的 IOPS 和吞吐上限,部分类型还提供突发能力,突发额度用完后性能会回落到基准水平。如果 iostat 中 IOPS 或吞吐长时间贴近规格值、await 随之升高,就是触到了上限。具体数值以云厂商当前文档和控制台监控为准。处理:更换更高性能的云硬盘类型、扩容(部分类型性能随容量增长)、或把读写分散到多块盘。
5. 业务本身的 I/O 模式问题
例如数据库缺少索引导致全表扫描、备份或批量任务在业务高峰运行、小块随机写过多。处理:优化 SQL,把备份等任务挪到低峰并用 ionice 降低优先级,合并小写入。
四、小结
磁盘 I/O 排查可以按固定顺序推进:
top/vmstat看wa、b、si/so,确认是否是 I/O 问题;iostat -x 1看 IOPS、吞吐、await、aqu-sz,定位是哪块盘,不要单看%util,也不要再看svctm;pidstat -d 1、iotop -oPa找出读写最多的进程;ps查 D 状态进程,确认等待情况;lsof、/proc/<pid>/io定位具体文件和真实落盘量;- 结合日志、inode、swap、云硬盘规格上限分析根因。
按这个顺序,大多数“CPU 不高但机器很卡”的问题都能在较短时间内找到源头。平时也建议在云监控中为磁盘读写 IOPS、吞吐和使用率配置告警,在问题影响业务前发现它。
- 点赞
- 收藏
- 关注作者
评论(0)