Appearance
负载飙到 30,CPU 却是空闲的:load average 到底在统计什么
一个自相矛盾的现象
$ uptime
03:12:41 up 88 days, 4:22, 2 users, load average: 30.42, 28.91, 22.17
$ top -bn1 | head -3
%Cpu(s): 11.8 us, 1.2 sy, 0.0 ni, 86.4 id, 0.4 wa4 核的机器,load average 是 30,但 CPU 有 86% 在空闲。
这两组数字看起来直接矛盾。于是有了两种常见的错误反应:
- 运维说"负载 30 了,机器要炸了,赶紧扩容"
- 开发说"CPU 明明很闲,是你们监控配错了"
两个人都错了。 正确结论是:load average 从来就不是一个 CPU 指标。
load average 的定义
先说清楚它到底在数什么。
load average 是内核维护的一个计数器,可以在 /proc/loadavg 里直接读到原始值:
bash
cat /proc/loadavg
0.00 30.42 28.91 22.17 3/412 28491对应的 uptime 输出:
load average: 30.42, 28.91, 22.17
↑ ↑ ↑
1 分钟 5 分钟 15 分钟三个数字是同一时刻的三种时间窗口,每个都是指数衰减的移动平均。它的用途是看趋势:
- 1 分钟值 >> 15 分钟值 → 负载正在快速上升,出事了
- 1 分钟值 << 15 分钟值 → 正在从高峰回落
- 三个值都高且平稳 → 这是常态压力,不是突发故障
关键在定义:load average 统计的是处于 R 状态和 D 状态的进程数量之和。
| 状态 | 含义 | 通俗解释 |
|---|---|---|
R | Running / Runnable | 正在跑,或者排着队等 CPU |
D | Uninterruptible Sleep | 不可中断睡眠——通常卡在 IO 上 |
S | Interruptible Sleep | 可中断睡眠(等网络、等事件)——不计入负载 |
注意 D 被算进去了。 这就是一切困惑的源头。
最大的误区:Linux 把 D 状态也算作负载
这不是 bug,是刻意的设计差异。
传统 Unix(BSD 系)的负载只统计 R 状态——也就是"有多少任务在抢 CPU"。而 Linux 把 D 也算了进去,理由是:
这些进程虽然不消耗 CPU,但它们占着资源排队——具体说,占着磁盘 IO。系统确实被"压住"了,只是压它的不是 CPU。
这个设计选择让 load average 在 Linux 上变成了一个更宽泛的"系统饱和程度"指标,而不是纯粹的 CPU 排队长度。
代价是:它变得很容易被误读。
D 状态是什么,为什么它这么特殊
D(不可中断睡眠)是 Linux 进程状态里最特殊的一个。它出现在进程必须等待某个内核操作完成、且期间不能被信号打断的时候。典型场景:
- 读写本地磁盘(尤其是机械盘遇到坏道重试)
- NFS / 网络存储挂载卡住 ← 运维最常遇到的元凶
- 块设备驱动的底层等待
- 某些内核锁竞争
它最反直觉的两个特性:
WARNING
处于 D 状态的进程,kill -9 也杀不掉。 因为它不响应任何信号——包括 SIGKILL。信号只有在进程返回用户态时才会被处理,而它正卡在内核里出不来。你唯一能做的是消除它等待的条件(修好磁盘、恢复 NFS),或者重启机器。
TIP
如果你发现一堆进程处于 D 且 kill -9 无效,不要反复尝试杀进程,直接去查它在等什么。命令: cat /proc/<pid>/wchan 或 cat /proc/<pid>/stack(需 root)
怎么判断负载是"CPU 型"还是"IO 型"
这是全文最实用的一段。判据在 vmstat 的前两列:
bash
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
30 0 0 812340 92144 4829104 0 0 12 48 1204 3801 92 3 5 0 0
28 0 0 812340 92144 4829104 0 0 8 52 1198 3766 90 4 6 0 0r列高、b列低 → 进程在抢 CPU,是真的 CPU 瓶颈r列低、b列高 → 进程卡在 IO,CPU 是无辜的- 上面这个输出
r=30, b=0→ CPU 型,需要扩 CPU 或优化代码
再看一个 IO 型的:
bash
vmstat 1 3
r b swpd free buff cache si so bi bo in cs us sy id wa st
2 28 0 210344 18244 1120848 0 0 4820 9224 842 1204 3 2 82 13 0
1 31 0 210144 18244 1120848 0 0 5104 8812 820 1186 2 3 80 15 0r 只有 1~2,b 有 30 左右,wa(IO wait)13~15%。
这才对应上开头那个 load 30 / CPU 86% idle 的场景——b 列就是那 30 的来源,它们全卡在 IO 上。
load 数值该怎么解读
第二个误区:拿 load 绝对值和某个"安全线"比。 load 必须除以 CPU 核数才有意义——4 核上的 load 8 和 32 核上的 load 8,是完全不同的两回事。
bash
nproc # 核数
grep -c processor /proc/cpuinfo # 同上经验阈值(仅适用于 CPU 型负载):
| load / 核数 | 含义 |
|---|---|
| < 0.7 | 正常,有余量 |
| 0.7 – 1.0 | 接近饱和,关注趋势 |
| > 1.0 | 已有任务在排队等 CPU |
| > 2.0 | 明显过载 |
但这张表对 IO 型负载完全无效。 磁盘慢的时候 load 冲到 50 也可能服务好好的——因为排队的是 IO,不是 CPU。判断"要不要扩容"必须结合 vmstat 的 r/b 分布,光看数字会做错决策。
实战:load 飙升的定位路径
一条从粗到细的排查链:
第 1 步:看趋势,判断是突发还是常态
bash
uptime
# load average: 30.42, 28.91, 22.17
# 三个值齐涨 → 持续加压,不是瞬时抖动第 2 步:区分 CPU 型 / IO 型
bash
vmstat 1 10
# 盯 r 和 b 两列,看哪个高第 3 步:如果是 IO 型,找是哪块盘
bash
iostat -x 1 3重点看三列:
| 列 | 含义 | 危险信号 |
|---|---|---|
%util | 设备繁忙度 | 持续 > 90% |
await | 单次 IO 平均耗时(含排队) | 机械盘 > 20ms,SSD > 2ms |
aqu-sz | 平均队列长度 | 持续 > 1 说明在排队 |
第 4 步:找出正在 D 状态的进程
bash
ps -eo stat,pid,ppid,wchan:20,comm | awk '$1 ~ /^D/' | sort -k4输出会直接告诉你是哪些进程卡住、卡在哪个内核函数上。
第 5 步:如果是网络存储,先看挂载
NFS / CIFS 挂载卡死是 load 飙升的头号元凶,而且症状非常典型:
- load 几分钟内从个位数冲到几十上百
- 一堆进程处于
D,kill -9无效 df -h、ls这类命令会直接挂住不动
bash
mount | grep -E "nfs|cifs"
cat /proc/mounts | grep nfs
# 关键排查:确认后端存储是否可达
rpcinfo -p <nfs-server>
showmount -e <nfs-server>如果后端存储已经不可达,正确处理是用 umount -f -l 强制卸载后修复存储,而不是等它自己恢复。注意:对已经卡住的 NFS 直接 umount 会挂住,-f(force)+ -l(lazy)是关键。
一个更现代的替代指标:PSI
load average 是 1993 年随 Linux 0.99 引入的,它的问题是把 CPU、IO、内存三种完全不同的压力混成一个数字。
Linux 4.20 之后内核提供了 PSI(Pressure Stall Information),直接把压力拆成三类:
bash
cat /proc/pressure/cpu
some avg10=0.02 avg60=0.05 avg300=0.00 total=128491
cat /proc/pressure/io
some avg10=13.40 avg60=8.22 avg300=3.11 total=9482713
full avg10=9.82 avg60=5.44 avg300=1.20 total=6120483
cat /proc/pressure/memory
some avg10=0.00 avg60=0.00 avg300=0.00 total=0含义解释:
some:至少有一个任务被卡住的时间占比full:所有可运行任务都被卡住的时间占比(这个更接近"用户真的感觉慢了")
上例中 io some avg10=13.40%,意思就是最近 10 分钟里有 13.4% 的时间,至少有一个任务因为 IO 而停滞。
PSI 的价值在于它不再需要你猜——/proc/pressure/io 数值高就直接说明 IO 是瓶颈,不用再去算 r 和 b 的比例。如果你的监控体系支持(node_exporter、较新的内核),建议用它替代 load average 作为主要压力指标。
WARNING
PSI 不是所有机器都有。它需要内核 ≥ 4.20 且编译时开启 CONFIG_PSI,部分发行版还要求启动参数带 psi=1。 CentOS 7(内核 3.10)、Debian 9 及更早的系统完全没有这个文件——cat /proc/pressure/cpu 会直接报 "No such file or directory"。 先确认存在再用:
bash
ls /proc/pressure/ 2>/dev/null || echo "本机内核不支持 PSI,继续用 load + vmstat"小结
| 问题 | 答案 |
|---|---|
| load average 是 CPU 指标吗 | 不是。它统计 R + D 状态进程数 |
| 为什么负载高但 CPU 空闲 | D 状态进程卡在 IO,不占 CPU 但计入负载 |
| 怎么区分是哪种 | 看 vmstat 的 r(抢 CPU)和 b(等 IO)两列 |
| load 多少算高 | 必须除以核数;且阈值只对 CPU 型有效 |
D 状态进程能 kill 吗 | 不能,kill -9 也无效。要消除它等待的条件 |
| 更好的指标 | PSI(/proc/pressure/*),能区分 CPU / IO / 内存压力 |
一句话记住:load average 回答的不是"CPU 有多忙",而是**"有多少任务在排队等你"**。至于它们等的是 CPU 还是磁盘,得你自己去看 r 和 b。