CPU飙到99%?一条命令定位罪魁祸首(附银行真实案例)

0 阅读7分钟

一、凌晨两点,手机震了

凌晨 2:47,手机震了一下。

监控群里弹出一条红色告警:

⚠️ 支付网关集群 CPU 持续 100%,订单创建成功率暴跌至 12.3%

值班工程师的第一反应是 SSH 上去敲了个 top——

%Cpu(s): 98.5 us, 1.2 sy, 0.0 ni, 0.3 id
PID     USER    %CPU   COMMAND
24789   app     774%   java

774%。

8 核服务器,正常上限 800%,意味着这个 Java 进程几乎吃光了所有 CPU 核心。

此时距离下一波早高峰支付还有 5 个小时。摆在面前的只有两条路:

  • A. 重启了事——眼前能止血,但病因没找到,后天照样崩
  • B. 趁着故障还在,现场排查——多花 5 分钟,但能一劳永逸

他选了 B。接下来的排查过程,就是这篇文章要讲的内容。


二、先搞清楚一件事:CPU 高 ≠ 一定是 CPU 的锅

很多人一看到 CPU 99% 就慌了,其实 top 里那行 %Cpu 细分了四种模式,搞清楚到底是哪种"高",方向才不会错

指标含义一句话排查方向
us用户态——你的应用在跑找具体进程/线程/代码
sy内核态——系统在跑频繁系统调用/锁竞争
waI/O 等待——CPU 在等磁盘这不是 CPU 的锅,去看磁盘
si软中断——网络包处理在烧 CPU看网络流量/是否被攻击

💡 如果 CPU 只有 10%-20% 但 load 飙到 200+,说明大量进程卡在 I/O 等待上,用 dmesg | tail 看看是不是磁盘或 NFS 出问题了。

这篇文章主要讲 us 高的情况——因为这是生产环境 80% 以上 CPU 故事的起因。


三、5 分钟定位四步法(建议收藏)

这四步是我从几十次线上故障里提炼出来的标准流程,不管你是 Java、Python 还是 C 程序,前两步都通用。

Step 1:找到吃 CPU 的进程

top

进入后按 P 键(大写),按 CPU 使用率排序。找到 %CPU 最高的那行,记下 PID。

💡 也可以用一行命令直接搞定:

ps aux --sort=-%cpu | head -5

Step 2:找到吃 CPU 的线程

进程只是个"容器",真正烧 CPU 的是里面的某个线程。

top -Hp 24789

同样按 P 键排序,找到 %CPU 最高的线程 TID。

PID     USER    %CPU   COMMAND
24812   app     95.5%  java

💡 一步到位的替代命令:

ps H -eo pid,tid,%cpu --sort=-%cpu | head -5

Step 3:把线程 ID 转成十六进制

Linux 用十进制显示线程 ID,但 Java 堆栈里用的是十六进制,这一步必不可少。

printf "%x\n" 24812

输出 60ec,记下 0x60ec

Step 4:定位到具体代码行

jstack 24789 | grep "0x60ec" -A 20

输出示例:

"http-nio-8080-exec-6" #25 prio=5
    at com.xxx.OrderService.getOrder(OrderService.java:146)
    at com.xxx.OrderController.get(OrderController.java:486)

到这里,你已经从"CPU 高了"缩小到了"某行代码有问题"。

剩下的就是让开发同事去看这行代码——是死循环、全量查询、还是锁竞争,代码层面一目了然。

💡 如果服务器上没有 jstack,可以用 kill -3 <PID> 生成线程 dump 到标准输出,效果一样。


四、回到那个凌晨:银行事故的完整复盘

前半部分讲了排查方法,现在用那晚的真实事故,把四步法串一遍。

事故背景:2024 年 6 月,广州某股份制银行核心支付网关集群,3 台 Pod 同时 CPU 100%。

影响范围

  • 支付成功率从 99.98% 跌到 12.3%
  • 平均延迟从 320ms 飙升到 8.4 秒
  • Kafka 消费积压暴涨 4200 倍
  • 17 分钟内约 14.7 万笔订单创建失败

排查过程

值班工程师按照四步法操作:

Step 1top -c → 发现 Java 进程 PID 24789,CPU 774%

Step 2top -Hp 24789 → 多个线程 CPU 95%+,不是一个线程的问题

Step 3jstack 24789 → 抓取全量线程堆栈

Step 4 — 分析堆栈后发现:

  • 应用日志高频出现 java.lang.OutOfMemoryError: Metaspace
  • JVM 启动参数未配置 -XX:MaxMetaspaceSize
  • Spring Cloud Gateway 的路由规则热更新持续膨胀元空间
  • 元空间溢出 → Liveness Probe 失败 → K8s 滚动重启 → 重启风暴加剧雪崩

看到了吗?这次 CPU 高的根因不是"计算过载",而是"内存泄漏导致的连锁反应"。 CPU 只是表面症状,内存才是真正的病因。

修复过程

# 1. 紧急扩容 + 配置 Metaspace 上限
kubectl patch configmap payment-gw-jvm-config -n payment-gw \
  --patch '{"data":{"JVM_OPTS":"-XX:MaxMetaspaceSize=512m -XX:MetaspaceSize=256m"}}'

# 2. 隔离故障 Pod
kubectl drain pod/payment-gw-7f9c4b5d8-2xqz9 \
  -n payment-gw --ignore-daemonsets --force

新 Pod 启动后,Metaspace 稳定在 220-380 MB,故障解除。

事后感悟

如果值班工程师当时选了"重启了事":

  • ❌ 第二天高峰期同样的问题会再次出现
  • ❌ 反复重启被认定为"临时规避",绩效考核受挫
  • ❌ 根因分析被无限推迟,团队对问题本质始终模糊

但他完成了完整的排查流程,换班前提交了一份故障报告,第二天早会直接给出了根因和修复方案。

这,才是运维工程师该有的样子。


五、CPU 飙高的 5 大常见根因(速查表)

工具告诉你"哪里"出了问题,根因分析告诉你"为什么"。我把最常见的五种原因整理成了一张表,建议截图保存

根因特征快速定位命令
🔴 死循环单线程 CPU 100%,其他线程正常jstack <PID> | grep -A 20 "0x十六进制" 看循环逻辑
🟠 频繁 Full GCFGC 每分钟几十次,CPU 在 GC 线程上烧jstat -gcutil <PID> 1000 观察 FGC 列
🟡 线程过多vmstat 显示 cs 每秒 10 万+vmstat 1 看上下文切换
🟢 第三方 C 库jstack 看不到有用的堆栈信息perf top -p <PID> 看底层函数调用
🔵 消息积压CPU 随消息量增长线性飙升检查 MQ 配置的确认机制是否为"自动确认"

💡 补充一个避坑点:如果 jstackperf 都定位不了,问题可能出在 JNI 层或底层 C 库。某金融客户的 APISIX 网关就遇到过——perf 只能看到 pkey_rsa_decrypt 占了 44.8% CPU,Java 侧完全无感。最后是用 async-profiler 的全栈动态追踪才定位到是自定义插件频繁调用 RSA 解密。常规工具在混合语言栈面前有时会"失明",记得准备备选方案。


六、一张图记住全流程

把今天的排查流程浓缩成一张决策图,下次遇到 CPU 告警直接照着走:

CPU 告警 → top 看 us / sy / wa / si
              │
              ├── us 高(最常见)
              │     → Step 1: top / ps 找进程 PID
              │     → Step 2: top -Hp 找线程 TID
              │     → Step 3: printf 转十六进制
              │     → Step 4: jstack 定位代码行
              │
              ├── sy 高
              │     → strace -p <PID> -c  找系统调用热点
              │
              ├── wa 高
              │     → iostat -x 1  → 这不是 CPU 的锅,去看磁盘
              │
              └── si 高
                    → sar -n DEV 1  → 检查网络流量/是否被攻击

把这四步打印出来贴在工位上——下次凌晨告警响的时候,你不会慌。


七、写在最后

排查 CPU 飙高,本质上是一个不断缩小范围的过程:

进程 → 线程 → 代码行 → 业务逻辑

每缩小一步,离真相就更近一步。

记住这句话——

"先定位,后操作"。重启是最后的手段,不是第一选择。


📌 下期预告:《CPU 排查进阶:用火焰图一眼看穿性能瓶颈》 关注不迷路,下一篇教你用可视化方式定位性能热点,比 jstack 更直观。


我是一名国有银行数据中心运维工程师,每周分享运维实战笔记。

关注「云间豹变」回复【命令手册】获取Linux排查命令速查表。 742a62d16c23c194d424d95cfbe51093.jpg