3. CPU 瓶颈定位:用 top、htop、perf、mpstat 看穿 CPU

CPU 瓶颈,说白了就是 CPU 忙不过来,或者忙得没效率。很多新手一看到 CPU 使用率高就喊“瓶颈”,其实不一定。我见过太多案例,CPU 才用到 30%,系统却慢得像蜗牛——那是上下文切换在作怪。

这一章,咱们就盯死四个工具:top、htop、perf、mpstat。它们能帮你回答三个核心问题:

  • CPU 到底有多忙?(使用率)
  • 它在忙什么?(上下文切换、运行队列)
  • 谁在捣乱?(进程/线程级定位)
CPU 瓶颈定位知识体系 CPU 瓶颈定位 CPU 使用率 上下文切换 运行队列 top htop perf mpstat 关键阈值(经验值) • 用户态使用率 > 70%:应用层瓶颈,优先优化代码 • 上下文切换 > 10 万次/秒:线程数过多或锁竞争严重 • 运行队列 > CPU 核数 * 2:CPU 已经过载,请求在排队

3.1 top:第一眼印象,但别全信

top 是我上任何服务器第一个敲的命令。它给你一个快照:CPU 使用率、负载、进程列表。但说实话,top 的 CPU 信息太粗了。

你看到的是总的 CPU 使用率,分 user、system、nice、idle、iowait 等。我个人习惯先看 %idle——如果 idle 低于 20%,说明 CPU 确实忙。但忙什么?top 说不清楚。

top 输出关键行解读:

%Cpu(s): 45.2 us, 12.8 sy,  0.0 ni, 38.5 id,  3.0 wa,  0.3 hi,  0.2 si,  0.0 st
  • us(user):用户态进程占用的 CPU 时间。高的话,说明应用层在干活。
  • sy(system):内核态占用的 CPU 时间。超过 20% 就要警惕了,可能是系统调用太频繁。
  • wa(iowait):等待 I/O 完成的时间。这个高,CPU 在等磁盘或网络,不是 CPU 本身慢。
  • hi/si:硬中断和软中断。高的话,说明网络或硬件设备在疯狂打断 CPU。

我的习惯:top 只看第一眼,然后立刻按 1 键,看每个 CPU 核的使用情况。有时候总使用率不高,但某个核已经 100% 了——那是典型的“单线程瓶颈”。

3.2 htop:top 的增强版,更直观

htop 比 top 好看多了。彩色条显示每个 CPU 核的使用率,还能用鼠标操作。我个人更喜欢 htop 的进程树视图——按 F5,一眼看出父子进程关系。

htop 有个隐藏功能:按 t 键进入树形模式,按 H 隐藏用户线程。我在排查 Java 应用时经常用这个——Java 线程一多,top 里全是乱糟糟的线程名,htop 能帮你理清楚。

注意:htop 默认不安装。生产环境上可能没有。我建议你提前装好,或者至少知道怎么用 yum/apt 快速装。

# CentOS/RHEL
yum install -y htop

# Ubuntu/Debian
apt install -y htop

3.3 mpstat:多核 CPU 的“显微镜”

mpstat 是 sysstat 包里的工具。它能精确到每个 CPU 核,还能看上下文切换和中断频率。我个人觉得,mpstat 是定位 CPU 瓶颈的“第一利器”。

举个例子,我曾经遇到一个 Nginx 服务器,总 CPU 使用率才 30%,但请求延迟很高。用 mpstat 一看,发现 CPU 0 的软中断(%soft)高达 60%,其他核几乎空闲。原来是网卡中断都绑在 CPU 0 上了。

# 查看所有 CPU 核,每 2 秒刷新一次
mpstat -P ALL 2

# 输出示例:
# 02:45:12 PM  CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest  %gnice   %idle
# 02:45:14 PM  all   35.20    0.00   12.80    3.00    0.00    0.30    0.00    0.00    0.00   48.70
# 02:45:14 PM    0   60.10    0.00   25.30    5.00    0.00    1.20    0.00    0.00    0.00    8.40
# 02:45:14 PM    1   10.30    0.00    3.20    1.00    0.00    0.10    0.00    0.00    0.00   85.40

关键指标:

  • %soft:软中断占比。超过 5% 就要排查网络或驱动问题。
  • %steal:虚拟机偷走的时间。云服务器上这个值高,说明宿主机超卖严重。
  • %idle:空闲时间。低于 20% 是危险信号。

3.4 perf:CPU 的“火焰图”制造机

perf 是 Linux 性能分析的终极武器。它能采样 CPU 正在执行的指令,告诉你 CPU 的时间到底花在哪个函数上。

我记得有一次,一个 Redis 实例 CPU 使用率飙到 90%,但 QPS 没涨。用 perf top 一看,发现 lzf_decompress 占了大头——原来是客户端开启了压缩,但压缩率很低,CPU 全花在解压上了。

# 实时查看 CPU 热点函数
perf top

# 采样 10 秒,生成火焰图数据
perf record -a -g -- sleep 10
perf script > out.perf

# 然后用 FlameGraph 工具生成 SVG 火焰图
git clone https://github.com/brendangregg/FlameGraph
cd FlameGraph
./stackcollapse-perf.pl ../out.perf > out.folded
./flamegraph.pl out.folded > flame.svg

我的建议:perf 的 -g 参数开启调用链采样,能看到函数调用路径。但采样频率别太高,默认就够。我曾经设成 1000Hz,结果 perf 自己占了不少 CPU,反而干扰了分析。

3.5 上下文切换:CPU 的“隐形杀手”

上下文切换,说白了就是 CPU 从一个进程切到另一个进程。每次切换都要保存和恢复寄存器、内存映射、缓存……开销不小。

怎么查?用 vmstat 1 看 cs(context switch)列,或者用 pidstat -w 1 看每个进程的切换次数。

我曾经踩过的坑:一个 Java 应用,开了 200 个线程,每个线程都在做很小的计算任务。上下文切换高达 30 万次/秒,CPU 的 system 时间占了 40%。后来改成线程池,线程数降到 16,性能反而提升了 3 倍。

指标 正常范围 警告阈值 危险阈值
上下文切换(次/秒) < 1 万 1 万 ~ 5 万 > 10 万
运行队列长度 < CPU 核数 CPU 核数 ~ 2 倍 > 2 倍 CPU 核数
CPU 系统态占比 < 10% 10% ~ 20% > 30%

3.6 运行队列:CPU 到底有多挤?

运行队列(run queue)是等待 CPU 的进程数。用 vmstat 1 看 r 列,或者用 sar -q 1 看 runq-sz。

运行队列超过 CPU 核数,说明 CPU 已经过载了。请求在排队,响应时间自然变长。我一般看 load average——如果 1 分钟负载超过 CPU 核数的 2 倍,基本可以断定 CPU 是瓶颈。

实战判断流程:

  1. 先看 top 的 load average,如果 > CPU 核数 * 2,进入下一步。
  2. 用 mpstat -P ALL 1 看每个核的使用率,找不均衡。
  3. 用 pidstat -w 1 看上下文切换高的进程。
  4. 用 perf top 定位热点函数。
  5. 根据热点函数,优化代码或调整配置。