Load 平均 200 但 top 看 CPU 都不到 10%——load 到底是什么?
本文目录
先说结论
第一条:Linux 的 load average 不是 CPU 使用率,是 R + D 状态进程的数量——运行中的(R)加上不可中断睡眠(D,主要是 IO 等待)之和。
第二条:Load 高但 CPU 低,100% 是 IO 问题(磁盘慢 / NFS hang / 内核锁等待),要去查 D 状态线程,不是去查 CPU 热点。
第三条:1/5/15 分钟三个值要联合看——只有 1 分钟高是瞬时抖动,三个都高才是持续问题,如果 1 > 5 > 15 说明 load 在上升,趋势比绝对值更重要。
先把这段讲顺;面试官追问时,再展开下面的原理和细节。
详细讲解
某短剧出海公司一次线上事故:报警说某台 Flink 机器的 1 分钟 Load 飙到 200+,运维火急火燎上去 top 一看——CPU 占用加起来不到 10%,内存也够,磁盘也正常。监控页面红得发紫,机器看起来却像在「睡觉」。这种「load 炸了但 CPU 不高」的场景,是 Linux 面试里最能区分「看过监控」和「懂原理」的一道题。
我当时上去 5 分钟就定位了——是 NFS checkpoint 目录 hang 住,几十个 Flink 线程全部卡在 D 状态(uninterruptible sleep),这些线程不吃 CPU,但会算进 load average。Load 是「正在运行 + 不可中断等待」的总和,不是「CPU 使用率」。
Load 到底怎么算的——最容易被问穿的一道题
uptime 输出里那三个数字,几乎每个人都看过,但真正能讲清它怎么算出来的人不到 20%:
uptime
# 15:30:22 up 120 days, 6 users, load average: 0.50, 1.20, 2.10
# ↑ ↑ ↑
# 1min 5min 15min
# 内核每 5 秒采样一次 '运行队列长度'
# 运行队列 = 当前 CPU 上跑的 + Runnable 但没排上 CPU 的 + D 状态进程
# 然后用指数衰减平均(EMA)算 1/5/15 分钟的平滑值
# 关键公式(内核源码 kernel/sched/loadavg.c):
# load(t) = load(t-5s) * exp(-5/60) + active(t) * (1 - exp(-5/60))
# 这就是为什么叫 'average'——不是算术平均,是带记忆的指数平均
# cat /proc/loadavg 看得更全
cat /proc/loadavg
# 0.50 1.20 2.10 3/850 12345
# ↑ ↑
# 当前可运行进程/总进程数 最近创建的PID
最关键的认知颠覆:'load=2' 在单核机器上是 '满载+还有 1 个在排队',但在 32 核机器上是 '只用了 6%'。Load 的数字必须除以 CPU 核数才有意义。一台 32 核机器 load 到 32 才叫满载,到 200 就是 6 倍超载——全都挤在等 IO。
场景还原:那次 NFS hang 的 200 load
某短剧出海公司那台 Flink 机器 load 冲到 200+,我 ssh 上去先跑这三板斧:
# 第一板斧:看当前运行队列和阻塞队列
vmstat 1 5
# procs -----------memory---------- ---swap-- -----io----
# r b swpd free buff cache si so bi bo
# 1 52 0 2000 80 30000 0 0 200 50
# 2 58 0 1998 80 30000 0 0 180 40
# ↑ b 列是 'blocked on IO' 的进程数,长期 50+
# ↑ 这就是 load 200 的根源:50+ 个线程在等 IO
# 第二板斧:直接找 D 状态的进程/线程
ps -eLo pid,tid,stat,comm | awk '$3 ~ /D/' | head -20
# PID TID STAT COMM
# 12345 12346 Dl java
# 12345 12347 Dl java
# 12345 12348 Dl java
# ...一堆 java 线程都在 D
# 第三板斧:看这些线程 stack 卡在哪
cat /proc/12345/task/12346/stack
# [<ffffffff>] rpc_wait_bit_killable+0x24/0xb0
# [<ffffffff>] __rpc_execute+0x1f5/0x470
# [<ffffffff>] nfs4_handle_exception+0x75/0x130
# ← 卡在 NFS RPC 等待,checkpoint 目录是个 NFS mount
10 分钟定位:NFS server 那端因为带宽被另一个作业打爆,checkpoint 写请求全部在 RPC 层面排队等响应。Flink 那边看不出任何 CPU 问题,但因为 checkpoint 涉及的线程数量很多,几十个线程同时卡 D,load 瞬间冲到 200。
Load 高的 5 种常见根因
| 类型 | vmstat r/b | 典型症状 | 排查命令 | 常见根因 |
|---|---|---|---|---|
| CPU 饱和 | r 高 b 低 | top CPU 100% | top -H + jstack | 热点代码 / GC |
| IO 阻塞 | r 低 b 高 | CPU 不高但 load 高 | ps D + iostat | 磁盘慢 / NFS hang |
| 线程暴涨 | r 高 | 进程数几千 | ps -eLf | wc -l | 线程池没限制 |
| 锁竞争 | r 中等 | 多进程轮流饥饿 | perf lock | 内核/用户锁 |
| 僵尸进程 | 过程计数高 | ps 看到 <defunct> | ps -eo stat | 父进程没 wait |
面试高频追问
追问一:D 状态和 S 状态有什么区别?为什么 D 状态 kill -9 都杀不掉?
S 是可中断睡眠(interruptible sleep),线程在等事件,比如 sleep、epoll_wait——收到信号立刻醒来响应,kill -9 能直接干掉。D 是不可中断睡眠(uninterruptible sleep),通常是在等硬件 IO(磁盘、网络)或内核锁释放,这种状态下信号会被挂起到等待结束再处理。kill -9 对 D 状态无效,因为内核为了数据一致性不允许中途打断——你必须等底层 IO 完成或超时。这就是为什么 NFS hang 时那些 java 线程 kill -9 打不掉,最后只能重启机器或 umount -f。
追问二:负载均衡器前面挂了 10 台机器,load 都在 20 左右,你觉得算高吗?
不能凭绝对值判断,要先问两个问题:一是每台机器的 CPU 核数,32 核和 8 核的 load 20 含义完全不同;二是业务在什么场景,批处理任务 load 高是正常(CPU 打满算正常),但在线服务 load 高意味着 P99 延迟会飙。我的经验值是:CPU 密集型服务 load/核数 保持在 0.7 以下有弹性,在线服务保持 0.5 以下,批处理任务可以接近 1.0。超过 1.0 就意味着有排队,排队就意味着延迟。
追问三:你怎么把 D 状态线程的 stack 采集下来做事后分析?
最快的方式是 cat /proc/
实战踩坑:那次 NFS hang 之后我们改了什么
某短剧出海公司那次事故后做了三件事:第一,所有 Flink 作业的 checkpoint 目录从 NFS 迁到对象存储(OSS/S3)+ 本地盘双写,彻底消除 NFS 单点;第二,监控加了「D 状态线程数 > 20 持续 5 分钟」告警,比等 load 冲到 200 提前得多;第三,制定「load/核数 > 3」必须立即上机排查的响应规范。
bash - D 状态数监控脚本
#!/bin/bash
# /data/monitor/check_d_state.sh 每分钟一次
set -euo pipefail
D_COUNT=$(ps -eLo stat | awk '/^D/' | wc -l)
LOAD=$(awk '{print $1}' /proc/loadavg)
CORES=$(nproc)
NORM_LOAD=$(echo "$LOAD / $CORES" | bc -l)
if [ $D_COUNT -gt 20 ]; then
curl -sS -X POST $WEBHOOK -d "{\"text\":\"D 状态线程=$D_COUNT load=$LOAD\"}"
# 顺便抓一次 D 状态栈存档,便于事后排查
ps -eLo pid,tid,stat | awk '$3 ~ /D/ {print $1,$2}' | head -10 \
| while read pid tid; do
echo "--- $pid/$tid ---" >> /data/d_stacks/$(date +%F-%H%M).txt
cat /proc/$pid/task/$tid/stack >> /data/d_stacks/$(date +%F-%H%M).txt 2>/dev/null
done
fi
效果:上线后半年,凡是 load 异常的事故都能在 5 分钟内自动拿到 D 状态快照和栈,定位时间从平均 30 分钟缩到 5 分钟内。这种「问题发生时主动采集现场」的思路比事后回溯有效得多。
总结
Load average 是 Linux 最容易被误解的指标:它不是 CPU 使用率,是 R + D 状态进程的指数移动平均;它必须除以核数才有意义;load 高而 CPU 低 100% 是 IO 问题。面试时把「D 状态」「vmstat r/b」「load/核数」这三组关键词讲清楚,面试官就知道你看过真正的生产告警,不是光会背 uptime 命令。
下一篇预告:vmstat 敲下去 si so 一直在涨——swap 被触发的完整排查。
你遇到过最离谱的 load 异常是什么?是 CPU 爆了还是 IO hang?评论区聊聊。
关注「Wood聊大数据」| 9年大数据老兵,聊技术、聊职场、聊成长