load average 很高但 CPU 很闲:从 D 状态进程到 IO 等待的排查顺序
先理解 load average 到底在数什么
很多人把 load average 当成 CPU 使用率,这是排查时第一个误区。Linux 的 load 统计的是处于可运行状态(R)和不可中断睡眠状态(D)的进程数,再取 1、5、15 分钟的平均值。也就是说,一个进程卡在磁盘 IO 或网络文件系统上,同样会把 load 推高,哪怕 CPU 几乎全空闲。
所以看到 load 很高时,先别急着加 CPU,先确认它是「真的在算」还是「在等」。
第一步:看运行队列和阻塞队列
用 vmstat 看 r 和 b 两列,比只看 load 有用得多:
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
# 2 8 0 512000 12000 980000 0 0 1200 3400 800 1500 5 3 80 12 0
如果 r 很小、b 很大,说明大量进程在等资源,而不是在抢 CPU;如果 wa(IO wait)也高,方向基本就确定了。反过来,r 长期大于 CPU 核数,才是真正的 CPU 不足。
顺便记一下核数,load 要和它比:4 核机器上 load 4 约等于满载,16 核机器上 load 4 很轻松。
第二步:把 D 状态进程揪出来
D 状态是「不可中断睡眠」,普通 kill -9 也杀不掉,通常是在等内核里的 IO 完成。列出它们:
ps -eo state,pid,ppid,wchan:24,comm | awk '$1 ~ /^D/'
# D 18342 1 do_fsync python3
# D 9912 9800 nfs_wait_on_request tar
wchan 显示进程卡在哪个内核函数,do_fsync、nfs_wait_on_request、blkdev_issue_flush 这类名字已经能说明问题。需要更细的内核栈时,root 可以看:
cat /proc/18342/stack
如果 D 状态进程只是零星出现、很快消失,不必紧张;持续几分钟不消失,才是故障。
第三步:确认是本地磁盘还是网络存储
本地块设备看 iostat -x 1,重点不是 %util 一个数字,而是 await 和队列深度:
iostat -x 1 3
# Device r/s w/s rkB/s wkB/s await aqu-sz %util
# vda 120 340 4800 13600 38.2 12.4 99.0
await 几十毫秒、aqu-sz 持续升高,说明设备已经排队。%util 对 NVMe 和多队列设备并不完全准确,不要只盯它。
如果瓶颈在 NFS 或云厂商的块存储,iostat 可能看不出异常,这时要看挂载点和内核日志:
mount | grep -E 'nfs|cifs'
dmesg -T | tail -50
nfsstat -c
NFS 服务端不响应时,访问该挂载点的进程会集体进入 D 状态,load 瞬间升高,但本地磁盘一切正常。这类问题重启本机没用,要么恢复网络,要么用 umount -f / -l 处理挂载点。
第四步:排除内存和 cgroup 的干扰
内存不足时,进程频繁换入换出,也会表现为 IO 等待和 D 状态。看 vmstat 的 si/so 是否长期非零,以及 free -m 的可用内存:
free -m
vmstat 1 5 | awk 'NR>2 {print $7, $8}'
在容器里,还要看 cgroup 的限流,而不是宿主机总负载:
cat /sys/fs/cgroup/io.stat
cat /sys/fs/cgroup/cpu.stat
容器的 io.stat 里 ios 和 wbytes 增长很快、同时应用变慢,说明是磁盘配额或底层设备在限速。
常见诱因与处理顺序
按出现频率排,大概是这几类:日志同步写太多(每个请求一次 fsync)、数据库刷盘、备份或打包任务在高峰期跑、NFS/对象存储挂载点失联、磁盘本身到了 IOPS 上限。处理时先限流和错峰,再考虑换盘或拆分挂载点;直接加 CPU 对 IO 瓶颈没有帮助。
临时缓解可以把非关键任务 ionice 降级,或把日志改成批量落盘;但这些都是止痛,根因还是要落到具体的 IO 路径上。
监控建议
不要只对 load 设阈值。更实用的组合是:D 状态进程数、iowait 占比、设备 await、以及「load 与 CPU 使用率背离」这个条件。比如 load 超过核数两倍但 CPU 使用率低于 30%,就值得告警——这通常意味着有东西在等 IO,而不是在算。
排查顺序可以记成一句话:先看 vmstat 的 r/b,再找 D 状态和 wchan,然后用 iostat 或挂载点确认 IO 路径,最后才考虑内存和 cgroup。这样就不会被「load 高」这个表象带着去加 CPU 了。
- 点赞
- 收藏
- 关注作者
评论(0)