CPU 使用率低但 load 高,可能的原因有哪些,如何一步步定位

解读

国内面试官问这道题,核心想验证三件事:

  1. 对 Linux load 本质的理解(TASK_UNINTERRUPTIBLE 任务也会算进去);
  2. 能把“低 CPU”与“高 load”这一对看似矛盾的现象,拆成可度量的子问题;
  3. 有系统化的现场排查思路,而不是一上来就“重启试试”或“换台机器”。
    回答时先给“可能原因”分类,再给“可落地的定位步骤”,最后能反推业务影响,基本就能拿到高分。

知识点

  1. Linux load average:1/5/15 分钟内活跃与不可中断态任务数的指数加权移动平均。
  2. 不可中断态(D 态):通常等在 I/O、NFS、锁、raw socket、swap-in、raid 卡、dm 层、内核 mutex。
  3. CPU % 与 load 无必然关系:CPU 空闲但大量任务卡在 D 态,load 依旧飙高。
  4. 国内云环境常见“隐形 I/O”:超卖云盘、共享存储 QoS 限流、KVM virtio-block 队列打满、容器 cgroup blkio throttle。
  5. 排查工具链:top/htop(D 态标记)、vmstat 1(procs 列 b)、dstat -cldmnsy 1、pidstat -d 1、iostat -xm 1、perf top、/proc/PID/stack、/proc/sys/fs/file-nr、lsof、strace -f -p、sysrq+w、ebpf(biosnoop、runqlat)、ftrace、perf sched。
  6. 国内合规要求:生产网多数禁用外源包,优先用系统自带或公司源里的 perf/bcc;定位报告需脱敏,不能带客户 ID、订单号。

答案

一、可能原因(按出现频率从高到低)

  1. 磁盘或云盘 I/O 长时间得不到响应,大量任务进入 D 态;
  2. 内存不足触发 swap-in/swap-out,磁盘成为瓶颈;
  3. NFS/CIFS、NAS 挂载点网络抖动,远端无响应;
  4. 容器 blkio throttle 或 cgroup io.max 限制过低,队列堆积;
  5. 内核锁竞争(inode、ext4 journal、nvme 驱动 mutex、raid 卡 firmware bug);
  6. 超线程核饥饿,任务一直在 runnable 态排队,但 CPU 统计里算 idle;
  7. 僵尸进程过多,父进程不回收,导致 task struct 不释放;
  8. 内核 bug(已知 3.10-693 与 4.19 某批次 xfs 死锁)导致 D 态永久睡眠。

二、定位步骤(现场可操作,5 分钟出方向,15 分钟出根因)
Step0 确认现象
 top 或 htop 观察 1 分钟:CPU idle>70%,但 load 15 以上;同时看“Tasks”行 D 态数量。
Step1 区分是“D 态堆积”还是“ runnable 排队”
 vmstat 1 看 procs 列:
  b 列持续 > cpu 核数 → D 态;
  r 列持续 > cpu 核数 → runnable 排队。
Step2 若 b 列高,立即找谁在等 I/O
 dstat -cldmnsy 1 或 iostat -xm 1:
  await>100 ms 且 %util≈100 → 磁盘/云盘瓶颈;
  svctm 接近 await → 设备慢;
  aqu-sz 持续 > 核数 → 队列堆积。
 pidstat -d 1 定位进程:看 kB_rd/s、kB_wr/s 最高者。
Step3 确认是否 swap 引起
 free -m 看 swap used 是否波动;
 si/so 列在 vmstat 中是否非 0;
 cat /proc/PID/status 的 VmSwap 是否很大。
Step4 若 NFS/CIFS
 mount | grep -E 'nfs|fuse' 找到挂载点;
 nfsstat -c 看 retrans 是否持续增长;
 tcpdump -i eth0 port 2049 看重传。
Step5 容器场景
 cat /sys/fs/cgroup/blkio/docker/*/blkio.throttle.read_bps_device
 若值很小,直接 echo 调大或让运维改 kubelet --cgroups-per-qos;
 kubectl top pod + crictl inspect 找到容器内部 pid,再回宿主机看 /proc/PID/stack。
Step6 锁竞争或内核 bug
 perf top -g 看是否 _raw_spin_lock、ext4_journal_start 占头;
 cat /proc/PID/stack 反复采样,若总在 io_schedule 或 mutex_lock 同一函数 → 锁;
 对比 RedHat/CentOS 官方 kernel errata,看是否已修复。
Step7 给出量化结论
 记录“D 态任务数=xx、磁盘 await=xx ms、swap si/so=xx、NFS retrans=xx”,
 明确根因:如“云盘单盘 IOPS 被限 800,业务峰值需 3000,导致 平均 D 态 120 个,load 飙到 32”。
Step8 推动优化
 短期:升配云盘 IOPS、加内存关 swap、cache 预热;
 长期:业务层做分库分表、异步刷盘、本地 SSD cache、读链路加 Redis。

拓展思考

  1. 如果现场只能保留 3 个命令,你会留哪 3 个?
     答:top(看 D 态)、vmstat 1(r/b 列)、iostat -xm 1(await/util)。理由:10 秒内就能判定是 CPU 排队还是 I/O 排队,决定后续方向。
  2. 云厂商超卖场景下,iostat 显示 %util 很低但 await 很高,如何说服运维不是“应用写大了”而是“后端限流”?
     答:用 fio 做 4 k 随机读,iodepth=1,直接压裸盘,若 latency 与业务进程一致,说明云盘侧 QoS 限速;同时把云监控 API 的 VolumeReadLatency 截图附在报告里,形成“三证据”链。
  3. 容器内 pid 看到的 D 态栈是 vfs_read->nfs_readpage,但宿主机上该 pid 不存在,怎么继续?
     答:容器 pid ns 独立,需在宿主机用 nsenter -t <container-pid> -p cat /proc/1/stack,或在宿主机用 perf record -e sched:sched_blocked_reason -a 抓取 ns 间映射,再反解容器内业务进程。