load average 不是 CPU 使用率
一句话总结
Linux 的 load average 是可运行进程数加上处于不可中断等待(D 状态)的进程数。磁盘变慢时它也会上升;它衡量的并不是 CPU 使用率。
为什么需要这些知识
“load 达到 20,应该增加 CPU”是一种常见误诊。在 8 核机器上,load 20 可能表示 CPU 不足,也可能表示 NFS 停滞,20 个进程都被困在 I/O 等待中。面对后一种情况,增加 CPU 不会产生任何效果。
其他 Unix 系统计算 load 时只统计等待 CPU 的进程,只有 Linux 还会统计 D 状态(uninterruptible sleep)。这是 1993 年为了更准确表示“系统繁忙”而引入的变更。从那以后,Linux load 就成为系统需求指标,而不是 CPU 指标。
它是如何运作的
三个数字分别是 1 分钟、5 分钟和 15 分钟的指数移动平均值。变化方向本身就是信息。
cat /proc/loadavg
# 8.24 4.10 2.05 3/512 12345
# └1분 └5분 └15분 └실행중/전체 스레드 └마지막 PID
1 分钟值大于 15 分钟值,说明情况正在恶化;反之则表示正在恢复。
要确定是什么推高了 load,应按状态统计。
ps -eo state,comm --no-headers | awk '{print $1}' | sort | uniq -c | sort -rn
# R = 실행 가능, D = 끊기지 않는 대기(대개 I/O), S = 잠듦
如果 D 很多,问题就不在 CPU。
要查看 CPU 本身,应读取两次 /proc/stat 并计算差值。
awk '/^cpu /{print $2+$3+$4+$6+$7+$8, $5}' /proc/stat # (busy, idle)
使用两个时点的差值计算使用率,这正是 top 所做的事情。
常见误解
top 中的 %CPU 超过 100 并不是缺陷。 默认模式下,%CPU 以单个核心为基准。4 个线程各自满负载运行时,结果就是 400%。在 top 中按 Shift+I,即可切换为以全部核心为基准的模式,即关闭 Irix 模式。
容器内的 top 查看的是整个宿主机。 因为 /proc 来自宿主机,所以核心数和 load 都是宿主机的值。容器实际分配到的份额必须通过 cgroup 查看。
cat /sys/fs/cgroup/cpu.max # "쿼터 주기" — max 면 제한 없음
cat /sys/fs/cgroup/cpu.stat # nr_throttled, throttled_usec
如果 nr_throttled 持续增加,说明不是 CPU 不足,而是触及了限制。二者的处理方式完全不同。
负载平均值不是 CPU 指标
Linux 的 load average 表示正在运行或等待运行的进程数量,但其中还包括 D 状态,即不可中断等待,通常是磁盘 I/O。这一点不同于其他 Unix,也是误解的来源。
$ uptime
load average: 8.42, 6.10, 4.33 ← 코어가 4개인데 8?
$ vmstat 1 3
r b ... ← r 은 실행 대기, b 는 I/O 대기
1 7 ... ← 실행 대기는 1뿐, 나머지 7은 디스크를 기다린다
如果 r 小而 b 大,增加 CPU 不会带来任何改善。 真正的问题在磁盘。仅凭 load average 就扩大实例规模,是对这个指标代价最高的误解。
使用率相同,原因也可能不同
top 中哪一列数值较大,决定了调查方向。
| 较大的列 | 含义 | 首先查看的位置 |
|---|---|---|
us (user) |
应用程序代码 | profiler、热点函数 |
sy (system) |
内核——系统调用过多 | strace -c、文件与 socket 使用模式 |
wa (iowait) |
等待磁盘 | iostat -x、await 列 |
si (softirq) |
网络中断 | /proc/softirqs、RSS/RPS 设置 |
st (steal) |
被 hypervisor 抢走的时间 | 云实例等级、邻居噪声问题 |
如果 st 超过 5%,问题就不在自己的代码。可能是可突发实例(t 系列)耗尽了信用额度,也可能是物理宿主机过度拥挤。无论如何修改代码,都无法解决。
查找谁在使用 CPU 的顺序
- 谁在使用——通过
top -H -p <pid>深入到线程级别。很多时候并不是整个进程都在运行,而是某一个线程持续占用,例如 GC 线程或轮询循环。 - 在哪里使用——通过
perf top -p <pid>查看热点符号。如果大量符号显示为[unknown],说明缺少调试符号,应改用对应语言的 profiler。 - 为什么使用——回到代码中查看该函数为何如此频繁地被调用。通常是 O(n²)、缓存未命中,或同步写入日志。
没有 perf 的容器中,也可以通过 /proc/<pid>/stack 或语言运行时的线程 dump(jstack、py-spy dump)了解一半情况。
生产环境中真正重要的事
在 Kubernetes 中设置 CPU limit,会启用 cgroup quota。请求涌入时发生 throttling,p99 延迟会突然升高,但 CPU 使用率图表会在 limit 附近保持平坦,很容易被误读为“仍有余量”。确认 throttling 应查看 cpu.stat,而不是使用率。