Linux 服务器 CPU 飙高排查:一套实战定位思路
CPU 问题排查的核心链路:先分层,再深入;先分态,再取证;先保现场,再做动作。 整机 → 进程 → 线程 → 调用栈,顺着四层往下走,判断不会跑偏。
一、快速诊断三问
| 问题 | 命令 | 判断要点 |
|---|---|---|
| 整机还是单进程? | mpstat -P ALL 1 3 / ps -eo pid,%cpu --sort=-%cpu |
所有核都高→流量/全局中断;单核高→单线程热点 |
| 用户态还是系统态? | top -b -n 1 | head -10 / mpstat -P ALL 1 5 |
%usr高→应用代码;%sys高→内核/中断/网络;%steal高→宿主机抢资源 |
| 哪个线程/栈在热? | top -H -p <PID> → perf top -p <PID> -g |
固定占满单核→死循环/自旋锁;多线程高→并发放大 |
二、整机级判断(先别急着动)
# 一轮命令固定现场
date && hostname -f && uptime
w
top -b -n 1 | head -20
mpstat -P ALL 1 3
vmstat 1 5
sar -u 1 5
sar -q 1 5
free -h
df -h
dmesg -T | tail -50
关键指标解读:
| 指标 | 含义 | 异常阀值 |
|---|---|---|
%usr |
用户态 CPU | 高了先看应用进程/线程热点 |
%sys |
系统态 CPU | 高了先看网络/磁盘/中断/内核路径 |
%iowait |
IO 等待 | ⚠️ 很多人误判成 CPU 问题,根因在 IO |
%steal |
虚拟化偷取 | 云主机关键指标,>5% 找云平台 |
run queue (r) |
运行队列 | 持续 > CPU 核数 → 调度压力大 |
load average |
负载均值 | 不等于 CPU 打满,需结合 IO 判断 |
三、用户态 vs 系统态
区分方向
| CPU mode | 排查方向 | 典型根因 |
|---|---|---|
%usr + %nice 高 |
应用进程 | 代码热点、死循环、JSON 编解码、正则、GC、压缩解压 |
%sys 高 |
内核路径 | 系统调用密集、网络包处理、软中断、连接风暴 |
%soft 高 |
软中断 | 网卡小包洪峰、iptables/conntrack 压力 |
%irq 高 |
硬中断 | 网卡队列、某些存储控制器异常 |
%steal 高 |
宿主机 | 不是你机器的问题,先找云平台 |
%sys 高 → 继续定位
cat /proc/softirqs
cat /proc/interrupts
sar -n DEV,EDEV 1 5
ethtool -S eth0 | egrep 'drop|miss|error|timeout'
%usr 高 → 进入进程/线程定位
四、进程级定位
# 找 CPU 高的进程
ps -eo pid,ppid,lstart,etime,pcpu,pmem,cmd --sort=-pcpu | head -20
# 看进程周期性 CPU 走势
pidstat -u -t -p <PID> 1 5
# 看进程打开的文件描述符
lsof -p <PID> | head -50
不同语言排查路径
| 语言 | 工具 | 命令 |
|---|---|---|
| Java | top + jstack | top -H -p <PID> → printf '0x%x\\n' <TID> → jstack <PID> \| grep -A 20 <hex> |
| Go | pprof | curl -s http://127.0.0.1:6060/debug/pprof/profile?seconds=30 -o cpu.pprof → go tool pprof -top cpu.pprof |
| Python | top + strace | top -H -p <PID> → strace -p <PID> -c(统计模式) |
实战判断点
- 新拉起的进程 CPU 高 → 新版本发布、配置变更、缓存未热
- 运行几天后的进程 CPU 高 → 线程泄漏、连接泄漏、任务堆积、GC 退化
- 只有某个工作线程高 → 优先抓线程栈,不要先抓完整堆 dump
五、线程级定位(最有价值的一步)
# 查看进程内线程 CPU
top -H -p <PID>
ps -Lp <PID> -o pid,tid,psr,pcpu,stat,comm --sort=-pcpu | head -20
pidstat -t -p <PID> 1 5
# perf 热点采样(比 strace 更适合 CPU 分析)
sudo perf top -p <PID> -g # 实时查看
sudo perf record -F 99 -p <PID> -g -- sleep 30 # 采样留存
sudo perf report --stdio | head -80 # 查看热点函数
| 现象 | 结论 |
|---|---|
| 某个线程固定占满 100% 单核 | 典型死循环、自旋锁、空轮询 |
| 多个线程同时高 | 并发打满、线程池放大、热点 key |
| CPU 高 + 上下文切换也高 | 锁竞争、频繁唤醒、线程数过多 |
六、系统态 CPU 高:中断/网络/磁盘
排查顺序:网卡收发 → 软中断分布 → 连接状态 → 磁盘队列 → 内核日志
sar -n DEV,EDEV,TCP,ETCP 1 5
ss -s
ss -ant state syn-recv
netstat -s | egrep -i 'listen|overflow|drop|retrans'
cat /proc/softirqs
cat /proc/net/softnet_stat
iostat -xz 1 5
pidstat -d 1 5
| 模式 | 根因 | 解决 |
|---|---|---|
NET_RX 飙高 |
小包洪峰、负载均衡不均、网卡队列/中断绑核不合理 | 启用 irqbalance、调多队列、绑核 |
ksoftirqd 高 |
软中断堆积 | 检查网卡中断分布 |
SYN-RECV 很多 |
连接风暴、半连接队列不足 | 调大 somaxconn、tcp_max_syn_backlog |
七、云主机/容器特殊检查
# 宿主机抢占
mpstat -P ALL 1 5 # 看 %steal
# 容器 CPU 节流
grep . /sys/fs/cgroup/cpu.max 2>/dev/null || cat /sys/fs/cgroup/cpu/cpu.cfs_quota_us
cat /sys/fs/cgroup/cpu.stat 2>/dev/null
# K8s 视角
kubectl top node
kubectl top pod -A --containers | head -30
kubectl describe node <name> | egrep -A3 'Allocated resources'
⚠️ 容器 CPU 100% 先换算: 500m 配额下 100% ≠ 整机 100%。nr_throttled 持续增长说明被节流。
八、案例复盘
案例 1:Java 线程死循环打满单核
现象: 订单服务发布后 RT 从 30ms → 800ms,单核 100%
排查: top -H -p 28461 → 线程 28513 占 99% → jstack 显示卡在规则引擎死循环
// 问题代码:死循环 + 正则匹配 3 万条规则
while (true) {
for (String rule : rules) {
if (rule.matches(".*VIP.*")) { ... }
}
}
修复: 摘流 → 回滚 → 缓存预编译规则替代 matches → 加退出条件
效果: 单核 99% → 整机 31%,P99 1.2s → 85ms
案例 2:软中断堆积导致网关 %sys 70%
现象: API 网关高峰 %sys 70%,NET_RX 在 CPU0/1 严重倾斜
排查: cat /proc/softirqs 发现 NET_RX 不均 → ksoftirqd/0 持续占 CPU
修复: 启用 irqbalance + 调整 smp_affinity 使中断均衡分布
效果: %sys 72% → 28%,超时率 5.4% → 0.3%
九、应急响应 — 先止血再排查
CPU 飙到 100% 时,先保护现场再止血,优先恢复业务而非根因分析。
应急流程
- 固定现场 —
top -b -n1/mpstat/vmstat/dmesg -T | tail -50留存 - 判断影响 — 单机还是集群?单进程还是全局?有无关联告警?
- 止血 — 根据场景选择以下措施
- 通知 — 同步值班群:影响范围 + 已采取措施 + 预计恢复时间
- 复盘 — 事后写 RCA(Root Cause Analysis)
止血手段
| 场景 | 止血操作 | 命令 |
|---|---|---|
| 单进程 CPU 打满 | kill 或 restart | kill -15 <PID> / systemctl restart <svc> |
| 流量突增 | 限流/降级 | Nginx limit_req / Sentinel 降级 |
| 代码 Bug(刚发布) | 回滚 | kubectl rollout undo |
| 机器异常 | 摘流/切走流量 | 摘除 LB 后端 |
| 集群级 | 紧急扩容 | kubectl scale --replicas=N |
常见根因速查
| 根因 | 快速确认 | 特征 |
|---|---|---|
| 死循环 | perf top -p <PID> 看热点函数 |
单核 100%,固定线程 |
| GC 频繁 | jstat -gc <PID> 1s |
%usr 高 + 内存波动 |
| 正则回溯 | strace -p <PID> 大量重复调用 |
单线程高 + 响应慢 |
| 加密/压缩 | perf top 看 crypto/zlib 函数 |
%usr 高 + 吞吐低 |
| 短连接风暴 | ss -s 看 TIME-WAIT 数 |
%sys + %soft 高 |
| 挖矿病毒 | ps 找到未知进程 + lsof -p <PID> |
陌生进程名、持续 100% |
| 容器限流 | cat .../cpu.stat 看 nr_throttled |
stretch 高但 sys/user 不高 |
十、常驻监控与告警
关键指标
# 整机 CPU > 85% 持续 5 分钟
100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 85
# 软中断占比 > 25% 持续 3 分钟
avg by(instance) (rate(node_cpu_seconds_total{mode="softirq"}[5m])) * 100 > 25
# 容器节流持续增长
sum by(pod, namespace) (rate(container_cpu_cfs_throttled_periods_total[5m])) > 20
自动快照脚本
当 CPU 告警触发时,自动执行 /usr/local/bin/cpu_hotspot_snapshot.sh 保存现场(详见 raw 源文件)。
关联页面
| 页面 | 关联点 |
|---|---|
| cpu-spike-3-commands | CPU 飙高三命令速查(top → strace -c → /proc/PID/fd/,同伴快速参考) |
| server-performance-four-dimensions | 服务器五维排查(CPU/内存/磁盘/网络/文件系统) |
| linux-load-average-guide | Load Average 完全解读(iowait vs CPU 判断) |
| linux-server-load-case-study | 负载过高排查案例实战(含 CPU 场景) |
| linux-disk-io-tuning | 磁盘 IO 调优(%sys 高的 IO 路径排查) |
| linux-api-performance-tuning-case-study | Linux 系统调优实战案例(CPU→JVM→网络全栈) |
| linux-load-high-cpu-low-troubleshooting | Linux Load 高但 CPU 低的排查思路 — 从现象确认到根因定位的系统化诊断流程(含 vm |
| linux-perf-troubleshooting-handbook | Linux 服务器性能排查实战手册 — 60 秒快速摸底/4 大瓶颈排查/3 个实战案例/监控阈值/ |
| online-troubleshooting-checklist | 四维排查速查清单(CPU/磁盘/内存/网络 + Java 工具 jstack/jmap/jstat/tcpdump) |
- linux-essential-commands-reference — Linux 30 个高频命令速查(top/ps/kill 等进程管理命令速查)
- linux-compression-tools-comparison — 压缩解压工具对比(gzip/xz 高 CPU 占用场景)
- java-cpu-100-case-study — Java 应用 CPU 100% 排查实战(四步法 + 四大原因 + 真实案例)