找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

4626

积分

0

好友

602

主题
发表于 1 小时前 | 查看: 4| 回复: 0

CPU 飙高是 Linux 服务器最典型的性能问题之一。监控告警里 CPU 使用率冲破 90%,业务接口开始超时,SSH 连上去都卡得难受。很多运维同学习惯性重启服务或者直接 kill 进程,说到底只是把问题暂时压下去,根本没找到根因。本文按"从全局到局部、从表象到根因"的思路,分三个阶段展开,覆盖从 60 秒快速诊断到代码级热点分析的完整链路。


一、核心知识点速览

排查 CPU 问题之前,有几个概念得先弄清楚:

  • load average:系统的"平均活跃进程数",既包括正在运行的,也包括等待 I/O 的。如果 load 比 CPU 逻辑核数还大,说明有进程在排队等调度。
  • %us / %sy / %wa / %id / %st:CPU 时间的分布——用户态、内核态、I/O 等待、空闲、被虚拟化平台偷走的时间。
  • 上下文切换 (context switch):CPU 在不同进程/线程间切换的频率,过高意味着大量 CPU 时间浪费在调度而不是业务上。
  • 运行队列 (run queue):处于可运行状态但还在等 CPU 调度的进程数,超过核数就说明 CPU 已经饱和。

排查时关注的是 CPU 的调度单位(逻辑核),nproclscpu 输出的 CPU(s) 就是逻辑核总数,不需要单独区分物理核和逻辑核。


二、第一阶段:60 秒全局扫描

登录服务器后先别急着一头扎进日志里,用一组命令快速摸清系统整体状态。这一步的目标就是定性:到底是 CPU 瓶颈、内存瓶颈、I/O 瓶颈,还是网络问题。

2.1 uptime —— 看负载基线

$ uptime
 14:32:15 up 12 days,  3:21,  2 users,  load average: 18.52, 15.34, 10.87

如果 load average 超过了 CPU 逻辑核数,说明有任务在排队。但单看 load 不够,得结合后面的命令判断瓶颈到底在哪。

2.2 top —— 看 CPU 时间分布和 TOP 进程

$ top
top - 14:32:15 up 12 days,  3:21,  2 users,  load average: 18.52, 15.34, 10.87
Tasks: 256 total,   3 running, 253 sleeping,   0 stopped,   0 zombie
%Cpu(s): 12.5 us,  8.3 sy,  0.0 ni, 45.8 id, 33.3 wa,  0.0 hi,  0.0 si,  0.0 st
MiB Mem :  31967.6 total,   1224.5 free,  18234.2 used,  12508.9 buff/cache
MiB Swap:   4096.0 total,    345.2 free,   3750.8 used.   7123.4 avail Mem

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
 3456 root      20   0  ......

关键看 %Cpu(s) 这一行的分布:

字段 含义 警戒线
us 用户态 CPU 时间 持续 > 70% 说明业务进程吃 CPU
sy 内核态 CPU 时间 持续 > 30% 说明系统调用频繁或驱动问题
wa I/O 等待时间 持续 > 20% 说明磁盘是瓶颈
id 空闲时间 持续 < 10% 说明 CPU 饱和
st 被偷走的时间 云服务器上 > 5% 说明宿主机资源争抢

操作技巧:

  • P 键按 CPU 使用率排序
  • 1 键展开每个逻辑核的负载分布
  • c 键显示完整命令行

2.3 vmstat —— 看调度和阻塞

$ 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
16  2 204800  12245    ...

重点看前四列和 CPU 列:

字段 含义 警戒线
r 运行队列长度 > 核数说明 CPU 饱和
b 不可中断睡眠进程数(D 状态) > 0 说明 IO 或锁阻塞
cs 上下文切换次数/秒 > 50000 说明线程切换频繁
wa I/O 等待 > 20% 看 IO 子系统

关键判断:如果 r 高但 wa 低,CPU 是瓶颈;如果 b 高且 wa 高,磁盘是瓶颈;如果 cs 极高,说明线程过多或者锁竞争严重——这类问题在运维排查中非常典型,通常需要结合 perf 和 strace 才能确认具体原因。

2.4 mpstat —— 看各核负载是否均衡

$ mpstat -P ALL 1 3
Linux 5.15.0-91-generic ... 08/15/2025  _x86_64_ (32 CPU)

14:32:15  CPU    %usr   %sys   %iowait    %idle
14:32:16  all   12.50   8.30     33.30   45.80
14:32:16    0    3.20   5.10     12.40   79.30
14:32:16    1   60.50  15.30      5.20   19.00
...

关注点:如果个别核的 %usr 或 %sys 明显高于其他核,说明存在绑核或单线程瓶颈。如果所有核负载一致偏高,那就是全局限流,比如 CPU 密集型任务或者连接数打满了。

2.5 第一阶段判断矩阵

症状 可能原因 下一步
us 高 + r 高 + wa 低 CPU 密集型任务 第二阶段定位进程
sy 高 + cs 高 锁竞争 / 系统调用频繁 strace/perf 深挖
wa 高 + b 高 磁盘 IO 瓶颈 iostat/iotop 定位
st > 5%(云服务器) 宿主机超分 迁移实例或提工单
单核 us 高 + 其他低 单线程瓶颈 确认是否有绑核

三、第二阶段:定位进程与线程

全局扫描确定了方向,接下来锁定嫌疑进程。

3.1 pidstat —— 按进程统计 CPU

# 每 1 秒统计一次,取 CPU 使用率最高的前 15 个
$ pidstat -u 1 5 | sort -k 8 -nr | head -15

Linux 5.15.0-91-generic ... 08/15/2025  _x86_64_ (32 CPU)

14:35:22      UID       PID    %usr %system  %guest   %wait    %CPU   CPU  Command
14:35:23        0      3456   85.30    2.10    0.00    0.00   87.40     2  java
14:35:23      999      5678   12.40    1.20    0.00    0.00   13.60    15  nginx

%wait 列(需要 procps-ng 4.0+)表示进程处于可运行状态但等待 CPU 调度的百分比。如果 %wait 高,说明 CPU 确实是瓶颈,进程在排队等调度。

3.2 top -H —— 看线程级 CPU 消耗

找到嫌疑进程后,再看看是进程里哪个线程在吃 CPU:

$ top -H -p 3456

或者一步到位:

$ ps -p 3456 -To pid,tid,%cpu,cmd --sort=-%cpu | head -10
  PID   TID %CPU CMD
 3456  3456  0.0 java
 3456  6789 42.5 java
 3456  6790 38.1 java

记下高 CPU 的 TID,转成十六进制用于线程 dump:

$ printf "%x\n" 6789
1a85

这个十六进制值在线程 Dump(jstackpstack/proc/<PID>/task/<TID>/stack)中用于查找对应线程。

3.3 perf top —— 实时代码级热点

perf top 能直接显示哪个函数在消耗 CPU,比 top 精确到代码级别:

# 实时查看 CPU 热点函数(建议先装 linux-tools-common)
$ sudo perf top -p 3456 -g

输出示例:

Samples: 1M of event 'cpu-clock', 4000 Hz, Event count (approx.): ...
Overhead  Shared Object       Symbol
 35.20%  java                [.] JFFS2_write_super
 12.50%  libjvm.so           [.] Unsafe_GetLongVolatile
  8.30%  [kernel]            [k] _raw_spin_unlock_irqrestore

-g 参数显示调用链,可以逐层回溯是谁调用了这个热点函数。perf 是 Linux 性能分析的利器,但也涉及内核态和用户态的交互机制,理解它需要一定的基础。

3.4 perf record:生成火焰图

需要更精确的分析或对外汇报时,用采样模式:

# 采样 30 秒
$ sudo perf record -F 99 -p 3456 -g --sleep 30
$ sudo perf report --no-children

参数说明:

  • -F 99:每秒采样 99 次,兼顾精度和开销
  • -g:记录调用链
  • -k 1:强制包含内核栈(重要,否则大量 [unknown]
  • --no-children:展开真实调用深度,避免折叠优化隐藏信息

生成火焰图:

$ sudo perf script -i perf.data > out.perf
$ git clone https://github.com/brendangregg/FlameGraph
$ cd FlameGraph
$ ./stackcollapse-perf.pl ../out.perf > out.folded
$ ./flamegraph.pl out.folded > cpu.svg

火焰图横轴是采样比例,纵轴是调用栈。找到最宽的"平顶"区域就是热点函数,从那里往上看调用路径。

3.5 strace -c —— 统计系统调用开销

如果 %sy(内核态 CPU)偏高,用 strace 看系统调用耗时分布:

# 统计 5 秒内系统调用的耗时分布(不带 -p 则启动新进程)
$ sudo strace -c -p 3456
strace: Process 3456 attached
^C
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 45.20    0.452000          12       376           futex
 30.15    0.301500           5       603           epoll_wait
 12.10    0.121000           4       302           write
  8.50    0.085000           3       283           read

常见高耗时系统调用:

系统调用 含义 可能问题
futex 用户态锁操作 锁竞争激烈,线程同步瓶颈
epoll_wait 等待事件 I/O 多路复用,正常等待
write/read 文件读写 IO 频繁,检查是否写日志
sendto/recvfrom 网络收发 网络 IO 密集

四、第三阶段:根因分类与深挖

根据前两阶段的数据,把 CPU 飙高归入以下典型类别,再针对性排查。

4.1 CPU 密集型任务

特征:%usr 高(> 70%),perf top 显示业务函数占主导。

排查方向:

  • 查看是否有新上线的代码逻辑变更
  • 检查定时任务(crontab)是否集中在同一时段
  • 使用 perf record 生成火焰图确认业务热点

典型场景:突发流量导致连接处理数暴增、某些查询 SQL 通过 ORM 映射成循环查询、正则表达式回溯导致 CPU 打满。

4.2 死循环或无限递归

特征:单进程 CPU 固定 100%(单核),strace -c 显示某个系统调用频率极高。

排查方向:

# 先确认是单线程还是多线程在跑
$ top -H -p <PID>
# 对 Java 应用取 Thread Dump
$ kill -3 <PID>
# 结合前面十六进制的 TID 定位线程

典型场景:某次 while 循环缺少 break 条件、递归调用缺少终止条件、异常重试机制进入死循环。

4.3 锁竞争激烈

特征:%sy 高(> 30%),vmstat 1cs 列 > 50000,strace -e futex 高频。

# 只看 futex 相关调用
$ sudo strace -e futex -p <PID>

# 或用 perf 分析锁相关事件
$ sudo perf record -e sched:sched_stat_runtime -p <PID> -- sleep 10
$ sudo perf report

典型场景:高并发下 HashMap 扩容死循环(JDK 1.7 及以前)、ArrayList 并发写入、热门行数据库锁、Redis 热点 key。

4.4 上下文切换过高

特征:CPU 消耗不高(%idle 高),但 load average 高,vmstatcs > 80000。

# 查看自愿/非自愿上下文切换
$ pidstat -w 1 5
  • 自愿切换 (cswch/s):线程等待 I/O 或锁时主动让出 CPU,过高可能因为 IO 频繁。
  • 非自愿切换 (nvcswch/s):时间片用完被强制调度,过高因为线程数过多(超过 CPU 核数太多)。

4.5 中断处理过多

特征:%si(软中断)或 %hi(硬中断)异常高。

# 查看中断分布
$ cat /proc/interrupts
# 查看软中断
$ cat /proc/softirqs

典型场景:网卡 RX 队列中断集中到单个 CPU(RPS/RSS 未配置)、TPS 过高导致网络软中断多。

4.6 云服务器 vCPU 争抢

特征:%st(steal)持续 > 5%,且 %idle 低。

# 持续观察 steal 值
$ mpstat -P ALL 1 | grep -v Linux | awk '{print $1,$13}'

%st 表示 hypervisor 从当前 VM 偷走的 CPU 时间。如果持续高于 10%,说明宿主机的 CPU 已经严重超分,本地怎么优化都没用,直接提工单找云厂商。


五、CPU 飙高 8 大根因速查表

# 根因 关键症状 确诊命令 应对措施
1 业务代码 CPU 密集 %usr > 70%,load > 核数 perf report 优化算法/增加缓存/扩容
2 死循环/无限递归 单进程 CPU 100%,无 IO strace -cjstack 修复代码逻辑
3 锁竞争 %sy > 30%,futex 高频 strace -e futex 减少锁粒度/无锁化
4 上下文切换过多 %idle 高但 load 高,cs > 50000 vmstat 1 减少线程数/使用协程
5 I/O 密集导致 CPU 等待 wa > 20%,b 列高 iostat -x 1 换 SSD/优化 SQL/加缓存
6 内核内存回收 %sys 高,kswapd0 进程 CPU 高 vmstat 1 增加内存/检查内存泄漏
7 软中断/网络收包 %si 高,单核中断不均衡 cat /proc/softirqs RPS/RSS 设置/网卡多队列
8 云服务器 steal 高 %st > 10% mpstat -P ALL 1 提工单/换实例/非高峰期操作

六、生产环境注意事项

  1. 采样频率控制perf record-F 不要超过 10000Hz,否则 perf 本身的开销会成为干扰。生产环境推荐 99-997Hz。

  2. 必须包含内核栈perf record -k 1,否则大量热点显示为 [unknown]

  3. 谨慎使用 strace -p:在高并发进程上附加 strace 会导致进程显著变慢(5~10 倍性能下降),建议只在低峰期使用,或者先统计后采样。

  4. 不要直接 kill 高 CPU 进程:除非确认是恶意进程,否则先确认进程归属(java、mysql、httpd 可能有业务在跑)。

  5. 绑核排查:如果看到单核 100% 而其他核空闲,检查是否有 taskset 绑核或 irqbalance 未运行。

  6. 云服务器注意 %st:如果 %st 高,本地优化无效,直接联系云厂商。

  7. 操作前记录现场

$ date +%F_%H:%M:%S > /tmp/cpu_debug_$(date +%s).txt
$ uptime >> /tmp/cpu_debug_*.txt
$ top -bn1 >> /tmp/cpu_debug_*.txt
$ vmstat 1 5 >> /tmp/cpu_debug_*.txt

七、总结

CPU 飙高排查的核心思路可以浓缩为三句话:

  1. 先 60 秒全局扫描——用 uptime / top / vmstat / mpstat 定性是 CPU、IO、内存还是网络问题。
  2. 再锁定嫌疑进程——用 pidstat / perf top / strace 热点分析定位到具体的进程、线程、函数。
  3. 最后对症下药——根据根因分类走对应的优化路径:算法优化、锁优化、扩容、配置调整。

盲目重启或加机器只是暂时掩盖问题,只有找到根因才能彻底解决。建议将上述排查流程固化成一个排查脚本,每次 CPU 飙高时自动采集现场数据,再按流程逐步分析。如果你还处在性能分析的入门阶段,不妨先去云栈社区翻翻技术文档和避坑指南,很多原理性的东西理解透彻之后,排查起来会顺手得多。




上一篇:ChatGPT Plus与Pro区别详解:程序员该选哪个?买前必看
下一篇:为什么 MySQL 不推荐 join?只答性能差仅 30 分,分库分表才是关键
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-8-13 05:42 , Processed in 1.194408 second(s), 41 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表