Linux 服务器 CPU 飙高怎么排查?一套实战定位思路讲清楚
线上 CPU 飙高最怕两件事:一是盯着 top 看了半小时,最后还是不知道是谁打满了核;二是误把负载高当成 CPU 高,处理动作做反了,越处理越抖。生产环境里,CPU 问题通常不是单一指标异常,而是一条完整链路:业务请求放大、线程模型失控、内核软中断堆积、磁盘或网络抖动把 CPU 拖进系统态,最后体现在告警平台上只剩一行“CPU usage > 90%”。
我自己排这类故障时,不会一上来就改参数,也不会先重启服务。第一步永远是把现场固定住,先判断是整机 CPU 打满、单进程热点、线程级死循环、内核态消耗,还是虚拟化层 steal time。判断对了,后面的命令基本就是顺着证据走。
一、概述
1.1 背景介绍
Linux 服务器 CPU 飙高,常见表现有三类:
- 监控中
CPU usage 连续 5 分钟超过 85%,业务 RT 明显抬升 load average 飙升,但应用日志没有大量报错,看起来像“无声故障”
这类问题本质上是在回答四个问题:
- 是业务代码导致,还是网络、中断、磁盘、容器配额把 CPU 顶上去
1.2 技术特点
- 分层排查:先看整机,再看进程,再看线程,再看调用栈,避免在错误层级浪费时间
- 证据驱动:每一步都保留命令输出、时间点和 PID,复盘时能回放故障过程
- 兼容生产环境:优先使用低侵入命令,只有在证据不足时才上
perf、strace 这类更重的工具
1.3 适用场景
- 电商、支付、营销活动高峰期间,某台业务机 CPU 持续 90% 以上
- Java、Go、Python 服务 RT 抬升,容器副本正常但单 Pod 热点明显
- Nginx、网关、日志采集节点出现高系统态 CPU,怀疑是软中断或连接风暴
1.4 环境要求
| | |
|---|
| CentOS 7/8、Rocky Linux 8/9、Ubuntu 20.04+ | |
| procps-ng、sysstat、perf、strace、lsof | sysstat 提供 sar/mpstat/pidstat,perf 用于热点采样 |
| | 低于该配置时 CPU 抖动更明显,结论要结合负载模型看 |
| Prometheus + Node Exporter 或等价方案 | |
二、详细步骤
2.1 准备工作
2.1.1 系统检查
先确认这台机器是不是真的在烧 CPU,而不是负载高、IO 卡顿或虚拟机被宿主机抢占。第一轮命令我通常固定成下面这一组:
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:高了不一定是 CPU 问题,很多人会误判%steal:云主机上很关键,高了说明宿主机在抢 CPUrun queue:vmstat 中的 r 持续大于 CPU 核数,说明调度压力明显load average:只说明等待队列变长,不等于 CPU 一定打满
如果现场还没装诊断工具,先补齐:
# Debian / Ubuntu
sudo apt update
sudo apt install -y sysstat linux-tools-common linux-tools-generic \
strace lsof iotop dstat tcpdump linux-cpupower
# RHEL / CentOS / Rocky / AlmaLinux
sudo yum install -y sysstat perf strace lsof iotop dstat tcpdump kernel-tools
生产环境里建议把 sysstat 常驻打开,不要等出事了才发现机器上没有 sar 历史数据。
2.1.2 固定现场
CPU 问题最容易丢现场,尤其是应用被自动拉起、容器自动重建或者值班同学顺手重启服务。先把关键现场落盘:
sudo mkdir -p /var/log/cpu-hotspot/$(date +%F_%H%M%S)
SNAPSHOT_DIR=$(ls -dt /var/log/cpu-hotspot/* | head -1)
top -b -n 1 > "${SNAPSHOT_DIR}/top.txt"
ps -eo pid,ppid,cmd,%mem,%cpu --sort=-%cpu | head -50 > "${SNAPSHOT_DIR}/ps_top_cpu.txt"
mpstat -P ALL 1 5 > "${SNAPSHOT_DIR}/mpstat.txt"
pidstat -u -r -d -h 1 5 > "${SNAPSHOT_DIR}/pidstat.txt"
vmstat 1 5 > "${SNAPSHOT_DIR}/vmstat.txt"
sar -u 1 5 > "${SNAPSHOT_DIR}/sar_u.txt"
sar -n DEV 1 5 > "${SNAPSHOT_DIR}/sar_net.txt"
sar -d 1 5 > "${SNAPSHOT_DIR}/sar_disk.txt"
dmesg -T | tail -200 > "${SNAPSHOT_DIR}/dmesg_tail.txt"
如果是容器环境,再补一组 cgroup 和 kubelet 侧信息:
kubectl top pod -A --containers | sort -k3 -hr | head -20
kubectl describe pod <pod-name> -n <namespace>
crictl stats
cat /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 || cat /sys/fs/cgroup/cpu/cpu.stat
这里最容易踩坑的是:容器里看到 CPU 100%,但那是**单核 100%**,不是整机 100%。如果 Pod 只给了 500m,应用被节流后一样会表现成 RT 飙升。
2.2 核心排查步骤
2.2.1 先判断是整机问题还是单进程问题
先看全局,再看个体:
mpstat -P ALL 1 3
ps -eo pid,user,ni,pri,psr,stat,%cpu,%mem,etime,cmd --sort=-%cpu | head -30
pidstat -u -p ALL 1 5
判断原则我一般这么定:
- 所有核心都高,且多个业务进程都在跑:优先看流量、批处理、宿主机争抢、全局中断
- 只有 1-2 个核心高:大概率是单线程热点、锁竞争、自旋或绑核不均
- 整机 CPU 不高,单进程 CPU 很高:直接进线程级定位
- 整机
%sys 高、进程视角又不明显:转到中断、网络栈、磁盘 IO 路径
业务止血时,不要急着 kill -9。先确认是不是可以摘流:
# 例如在负载均衡层先摘掉异常节点
curl -s http://127.0.0.1:9090/healthz
sudo ipvsadm -Ln
sudo ss -s
如果节点还能响应,优先在网关或注册中心把实例摘掉,再做深度取证。
2.2.2 区分用户态 CPU 和系统态 CPU
这一刀必须切清楚,不然会把应用问题排到内核层,或者把中断风暴排到业务线程。
top -b -n 1 | head -10
mpstat -P ALL 1 5
sar -u ALL 1 5
经验上可以这样理解:
%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 高,直接进入进程和线程热点定位。
2.2.3 进程级定位:谁在吃 CPU
先把 top 进程抓出来,再看生命周期和启动参数:
ps -eo pid,ppid,lstart,etime,pcpu,pmem,cmd --sort=-pcpu | head -20
pidstat -u -t -p <PID> 1 5
cat /proc/<PID>/status
cat /proc/<PID>/limits
lsof -p <PID> | head -50
几个实战判断点:
- 新拉起的进程 CPU 高:通常和新版本发布、配置变更、缓存未热有关
- 运行几天后的进程 CPU 高:更多见于线程泄漏、连接泄漏、任务堆积、GC 退化
- 只有某个工作线程高:优先抓线程栈,不要先抓完整堆 dump,太重
Java 进程排查可以直接把高 CPU 线程映射到十六进制线程号:
top -H -p <PID>
printf'0x%x\n' <TID>
jstack <PID> | less
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 进程如果 CPU 很高,先看是不是纯 Python 死循环、JSON 序列化过重,或者多进程 worker 数配大了:
top -H -p <PID>
strace -p <PID> -tt -T -f -o /tmp/python.strace -c
2.2.4 线程级定位:哪个线程、哪段栈在热
线程级定位是 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
常见现象和结论:
- 某个线程固定占满 100% 单核:典型死循环、自旋锁、空轮询
- 多个线程同时高:并发打满、线程池放大、热点 key 或批任务冲击
- 线程 CPU 高且上下文切换也高:可能是锁竞争、频繁唤醒、线程数过多
继续往下抓热点时,优先用 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
perf 的使用原则:
- 采样 15-30 秒通常够了,时间太长会把短期热点冲淡
- 线上优先
-F 99 或 -F 49,不要把采样频率拉太高 perf top 适合现场判断,perf record/report 适合留证据
如果内核限制了 perf_event_paranoid,临时调整前先评估权限:
sysctl kernel.perf_event_paranoid
sudo sysctl -w kernel.perf_event_paranoid=1
改完记得回收,别把调试口子常驻留在生产机上。
2.2.5 系统态 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 飙高:小包洪峰、负载均衡不均、网卡队列/中断绑核不合理ksoftirqd 高:软中断堆积,用户态进程看起来并不高,但 CPU 已经被内核吃掉wa 高、avgqu-sz 高:不是 CPU 问题本身,根因在 IOSYN-RECV 很多:上游连接风暴、半连接队列不足、攻击流量或健康检查异常
2.2.6 云主机、虚拟化和容器环境的特殊检查
很多“CPU 高”其实是资源被限制或者宿主机抢占。
mpstat -P ALL 1 5
sar -u ALL 1 5
grep . /sys/fs/cgroup/cpu.max 2>/dev/null
grep . /sys/fs/cgroup/cpu.stat 2>/dev/null
cat /sys/fs/cgroup/cpu/cpu.cfs_quota_us 2>/dev/null
cat /sys/fs/cgroup/cpu/cpu.cfs_period_us 2>/dev/null
重点关注:
%steal 持续超过 5%:宿主机资源争抢,业务再怎么调也只是缓解nr_throttled 持续增长:容器被 CPU quota 节流usage_usec 增长平缓但 RT 很高:可能是线程拿不到调度片
Kubernetes 场景里,再补两步:
kubectl top node
kubectl top pod -A --containers | head -30
kubectl describe node <node-name> | egrep -A3 'Allocated resources|Non-terminated Pods'
如果节点超卖严重,优先做资源回收、亲和性重排、Pod 驱逐,而不是只盯某个业务进程。
2.3 验证排查结论
2.3.1 先验证临时止血动作是否生效
常见止血动作有三种:摘流、限流、降并发。动作做完后至少看 10 分钟,不要看 30 秒就下结论。
watch -n 2 "uptime; echo '---'; mpstat -P ALL 1 1; echo '---'; ss -s"
止血动作有效时,通常会看到:
2.3.2 再验证根因是否闭环
根因验证至少要满足三件事:
# 1. 热点线程已消失或热点函数占比明显下降
sudo perf report --stdio | head -40
# 2. 关键业务接口恢复
curl -s -o /dev/null -w "%{http_code} %{time_total}\n" http://127.0.0.1:8080/health
# 3. 监控指标回归
sar -u 1 3
闭环标准建议写得硬一点:
- CPU 峰值恢复到过去 7 天同时间窗 P95 附近
三、示例代码和配置
3.1 完整配置示例
3.1.1 主配置文件
下面这份配置用于开启 sysstat 长期采样,保证故障发生后能回看 CPU、IO、网络的历史走势。线上机器不留历史指标,很多 CPU 故障最后只能靠猜。
# 文件路径:/etc/sysconfig/sysstat
HISTORY=28
COMPRESSAFTER=7
SADC_OPTIONS="-S DISK -S XDISK -S INT -S IPV6"
SA_DIR=/var/log/sa
YESTERDAY=no
CentOS 7/8 系列还要确认定时任务开启:
# 文件路径:/usr/lib/systemd/system/sysstat-collect.timer
[Unit]
Description=Run system activity accounting tool every 10 minutes
[Timer]
OnCalendar=*:00/10
AccuracySec=1min
Persistent=true
[Install]
WantedBy=timers.target
启用方式:
sudo systemctl daemon-reload
sudo systemctl enable --now sysstat
sudo systemctl enable --now sysstat-collect.timer
sudo systemctl enable --now sysstat-summary.timer
3.1.2 辅助脚本
这份脚本用来在 CPU 异常时自动抓取快照,适合挂在定时任务、Prometheus Alertmanager webhook 或值班手册里。脚本目标不是“自动修复”,而是把最容易丢的现场第一时间保存下来。
#!/usr/bin/env bash
# 文件名:/usr/local/bin/cpu_hotspot_snapshot.sh
# 功能:采集 CPU 异常现场,保留进程、线程、网络、磁盘和内核证据
set -euo pipefail
BASE_DIR="/var/log/cpu-hotspot"
TS="$(date +%F_%H%M%S)"
OUT_DIR="${BASE_DIR}/${TS}"
mkdir -p "${OUT_DIR}"
echo"[INFO] snapshot dir: ${OUT_DIR}"
{
echo"===== basic ====="
date
hostname -f
uptime
uname -a
echo
echo"===== top ====="
top -b -n 1 | head -40
echo
echo"===== mpstat ====="
mpstat -P ALL 1 3
echo
echo"===== vmstat ====="
vmstat 1 5
echo
echo"===== sar cpu ====="
sar -u ALL 1 3
echo
echo"===== sar net ====="
sar -n DEV,EDEV,TCP,ETCP 1 3
echo
echo"===== iostat ====="
iostat -xz 1 3
} > "${OUT_DIR}/system.txt" 2>&1
ps -eo pid,ppid,user,psr,stat,%cpu,%mem,lstart,cmd --sort=-%cpu | head -50 \
> "${OUT_DIR}/top_processes.txt"
pidstat -u -r -d -t -h 1 5 > "${OUT_DIR}/pidstat.txt" 2>&1 || true
ss -s > "${OUT_DIR}/ss_summary.txt" 2>&1 || true
cat /proc/softirqs > "${OUT_DIR}/softirqs.txt" 2>&1 || true
cat /proc/interrupts > "${OUT_DIR}/interrupts.txt" 2>&1 || true
dmesg -T | tail -200 > "${OUT_DIR}/dmesg_tail.txt" 2>&1 || true
TOP_PID="$(ps -eo pid,%cpu --sort=-%cpu | awk 'NR==2 {print $1}')"
if [[ -n "${TOP_PID}" && "${TOP_PID}" =~ ^[0-9]+$ ]]; then
ps -Lp "${TOP_PID}" -o pid,tid,psr,pcpu,stat,comm --sort=-pcpu \
> "${OUT_DIR}/pid_${TOP_PID}_threads.txt" 2>&1 || true
timeout 20 perf record -F 49 -p "${TOP_PID}" -g -- sleep 15 \
> "${OUT_DIR}/perf_record.log" 2>&1 || true
perf report --stdio > "${OUT_DIR}/perf_report.txt" 2>&1 || true
fi
find "${BASE_DIR}" -maxdepth 1 -type d -mtime +7 -exec rm -rf {} \; || true
echo"[INFO] snapshot completed"
部署建议:
sudo install -o root -g root -m 0750 cpu_hotspot_snapshot.sh /usr/local/bin/cpu_hotspot_snapshot.sh
echo'*/5 * * * * root /usr/local/bin/cpu_hotspot_snapshot.sh >/dev/null 2>&1' | sudo tee /etc/cron.d/cpu-hotspot
如果机器数量多,别每 5 分钟全量抓。正确做法是只在告警触发时抓,或者给脚本加条件判断,例如最近 1 分钟 CPU 大于 85% 才采样。
3.2 实际应用案例
案例一:Java 线程死循环把单核打满
场景描述:某订单服务发布后 RT 从 30 ms 拉高到 800 ms,监控上整机 CPU 只有 45%,但 1 个核心长期 100%,业务侧以为是数据库慢,实际上根因在应用线程。
排查过程:
top -H -p 28461
ps -Lp 28461 -o pid,tid,pcpu,comm --sort=-pcpu | head
printf'0x%x\n' 28513
jstack 28461 | grep -A 20 6f61
sudo perf top -p 28461 -g
关键现象:
jstack 显示线程卡在业务规则引擎的循环判断里
实现代码:
publicvoidcalculateDiscount(List<String> rules){
while (true) {
for (String rule : rules) {
if (rule.matches(".*VIP.*")) {
doSomething(rule.split(":")[1]);
}
}
}
}
这段代码在测试环境不容易暴露问题,因为规则量小、线程少。线上规则列表扩到 3 万条之后,死循环加正则匹配直接把单核打穿。
修复动作:
运行结果:
修复前:单核 99%,实例 P99 RT 1.2s,订单超时率 8.7%
修复后:热点线程消失,整机 CPU 下降到 31%,P99 RT 恢复到 85ms
案例二:软中断堆积导致网关节点系统态 CPU 飙升
场景描述:某 API 网关节点在晚高峰出现 %sys 70% 以上,Nginx Worker 进程 CPU 并不高,但机器整体不可用,请求连接超时明显增多。
排查过程:
top -b -n 1 | head -10
cat /proc/softirqs | column -t | egrep 'NET_RX|NET_TX'
sar -n DEV,EDEV,TCP,ETCP 1 5
ss -s
ethtool -S eth0 | egrep 'drop|miss|queue'
关键现象:
NET_RX 软中断在 CPU0、CPU1 上远高于其他核心ksoftirqd/0、ksoftirqd/1 持续占用 CPU- 网卡多队列开了,但中断绑核不均,流量基本都压在前两个核心
处理脚本:
#!/usr/bin/env bash
# 文件名:rebalance_irq.sh
set -euo pipefail
SERVICE="irqbalance"
NIC="eth0"
sudo systemctl enable --now "${SERVICE}"
sudo ethtool -L "${NIC}" combined 8
for irq in $(grep "${NIC}" /proc/interrupts | awk -F: '{print $1}'); do
echo 0f | sudo tee "/proc/irq/${irq}/smp_affinity" >/dev/null
done
运行结果:
调整前:sys 72%,NET_RX 在 CPU0/1 明显倾斜,请求超时率 5.4%
调整后:sys 下降到 28%,各核心软中断分布趋于均衡,请求超时率降到 0.3%
这个案例里,如果只盯业务进程,基本看不出问题。CPU 真正被打满的是内核网络收包路径。
四、最佳实践和注意事项
4.1 最佳实践
4.1.1 性能优化
优化点一:常驻历史采样,别等出事再装工具
sar 历史数据对 CPU 问题非常关键,尤其是那些“现在已经恢复,但昨晚 21:10 出过抖动”的故障。生产环境建议至少保留 28 天数据。
sudo sed -i 's/^HISTORY=.*/HISTORY=28/' /etc/sysconfig/sysstat
sudo systemctl enable --now sysstat
sudo systemctl restart sysstat
优化点二:给热点业务做合理的线程上限
线程数不是越大越好。CPU 密集型业务线程池开太大,只会把上下文切换和锁竞争一起抬高。我们团队的经验值是:CPU 密集型服务先按 CPU 核数 * 1~2 起步,再压测修正。
nproc
pidstat -w -p <PID> 1 5
如果 cswch/s 和 nvcswch/s 持续很高,同时 CPU 利用率却上不去,线程数大概率已经过量。
优化点三:网络型节点优先处理软中断绑核
网关、L4/L7 代理、日志采集节点,CPU 打满很多时候不是应用逻辑,而是收包路径失衡。实测下来,双 10G 网卡机器如果中断集中在 1-2 个核上,业务峰值流量一来就会明显抖。
irqbalance --debug 2>/dev/null | head
cat /proc/interrupts
sudo systemctl enable --now irqbalance
4.1.2 安全加固
安全措施一:限制调试工具使用权限
perf、strace、gdb 都是排障利器,但也能带来信息泄露风险。生产环境建议只给运维值班组和 SRE 小范围授权。
getfacl /usr/bin/perf
sudo chmod 750 /usr/bin/perf
安全措施二:对 CPU 调优动作做审计
改 sysctl、改 IRQ 绑核、改容器 quota 都要进变更记录。没有审计的“临时优化”,一个月后基本没人记得动过什么。
sudo auditctl -w /etc/sysctl.conf -p wa -k sysctl_change
sudo auditctl -w /etc/security/limits.conf -p wa -k limits_change
安全措施三:对采样脚本做最小权限执行
快照脚本尽量只读,不要顺手把“自动 kill 高 CPU 进程”塞进去。自动止血可以做,但必须经过白名单和二次确认,否则误伤概率很高。
4.1.3 高可用配置
- HA 方案一:业务实例接入负载均衡健康检查,出现 CPU 热点时支持秒级摘流
- HA 方案二:关键服务采用多可用区部署,避免单台热点机器拖垮整个业务面
- 备份策略:所有 CPU 调优相关配置在修改前先做版本化备份,尤其是
sysctl、网卡参数、服务线程配置
建议把下面这些动作标准化:
sudo cp -a /etc/sysctl.conf /etc/sysctl.conf.$(date +%F_%H%M%S).bak
sudo sysctl -a > /var/backups/sysctl-all.$(date +%F_%H%M%S).txt
tar czf /var/backups/service-config-$(date +%F_%H%M%S).tar.gz \
/etc/systemd/system /etc/nginx /etc/myapp 2>/dev/null
4.2 注意事项
4.2.1 配置注意事项
警告:CPU 问题没有确认根因前,不要直接重启、扩容或改线程池。动作虽然能短时压住症状,但会把现场打散,后面再复盘就只能靠猜。
- 注意事项一:
load average 高不等于 CPU 高。很多人看到负载 30 就判定 CPU 打满,最后发现是磁盘卡住 - 注意事项二:容器 CPU 100% 先换算成限额视角再判断,
500m 配额下 100% 和整机 100% 不是一个量级 - 注意事项三:
strace -f -p 对高并发服务有侵入,优先用 perf、pidstat、线程栈做轻量定位
4.2.2 常见错误
| | |
|---|
| | |
| | 常驻 sysstat、保留 Prometheus 历史数据、告警触发自动快照 |
只抓进程 CPU,不看 %sys/%soft/%steal | | |
4.2.3 兼容性问题
- 版本兼容:
perf 最好和当前运行内核版本匹配,不匹配时符号解析会不准 - 平台兼容:在云厂商共享宿主机上,
%steal 的参考价值高于物理机 - 组件依赖:容器环境下查看 cgroup 指标时,需要区分 cgroup v1 和 cgroup v2 的文件路径
五、故障排查和监控
5.1 故障排查
5.1.1 日志查看
CPU 飙高虽然是资源问题,但日志仍然能给出触发时间点、请求类型和错误放大路径。日志至少看三类:系统日志、应用日志、内核日志。
# 查看系统日志
sudo journalctl -S -30min
# 查看应用日志
tail -f /var/log/myapp/app.log
# 查看错误日志
grep -E "ERROR|Timeout|RejectedExecution|GC overhead" /var/log/myapp/app.log | tail -50
如果是 Java 服务,再补 GC 日志:
grep -E "Pause Young|Pause Full|Full GC" /var/log/myapp/gc.log | tail -50
5.1.2 常见问题排查
问题一:CPU 100%,但 top 看不到明显高进程
top -b -n 1 | head -10
mpstat -P ALL 1 5
cat /proc/softirqs
cat /proc/interrupts
症状:整机 CPU 高,单个业务进程都不突出,%sys/%soft 偏高
诊断:多半是软中断、网卡队列、驱动或内核收包路径问题
解决:
问题二:应用进程 CPU 高,但业务量并不大
ps -eo pid,pcpu,pmem,cmd --sort=-pcpu | head
top -H -p <PID>
sudo perf record -F 99 -p <PID> -g -- sleep 20
sudo perf report --stdio | head -60
症状:请求量一般,实例仍持续吃满单核或多核
诊断:常见于代码死循环、热点 key、异常重试、自旋锁、正则或 JSON 开销过大
解决:
问题三:CPU 高伴随 RT 高,%steal 明显抬升
- 症状:业务 RT 波动明显,整机 CPU 看起来不算离谱,但
%steal 持续超过 5% - 排查:
mpstat -P ALL 1 5、云平台宿主机事件、节点资源争抢记录 - 解决:迁移实例、切换独享型规格、和云平台确认宿主机抖动
5.1.3 调试模式
线上调试优先轻量模式,不要一上来抓全量 heap dump 或 gcore。
# 实时查看热点函数
sudo perf top -p <PID> -g
# 20 秒采样
sudo perf record -F 49 -p <PID> -g -- sleep 20
# 查看调试信息
sudo perf report --stdio | head -80
如果是 Java 服务,补一个只读线程栈:
jstack -l <PID> > /tmp/jstack.$(date +%s).log
5.2 性能监控
5.2.1 关键指标监控
# CPU使用率
mpstat -P ALL 1 3
# 进程维度 CPU
pidstat -u -p ALL 1 3
# 网络连接
ss -s
# 磁盘IO
iostat -xz 1 3
建议重点盯这些指标:
node_cpu_seconds_total 按 mode 拆分后的 user/system/iowait/steal/softirq- 单机 1 分钟和 5 分钟
load average - 网络重传、丢包、
SYN backlog overflow - 容器
throttled_periods_total
5.2.2 监控指标说明
5.2.3 监控告警配置
# 文件路径:prometheus/rules/cpu_hotspot.yml
groups:
-name:cpu-hotspot
rules:
-alert:HostCpuHigh
expr:100-(avgby(instance)(rate(node_cpu_seconds_total{mode="idle"}[5m]))*100)>85
for:5m
labels:
severity:critical
annotations:
summary:"主机 CPU 持续过高"
description:"{{ $labels.instance }} CPU 连续 5 分钟高于 85%"
-alert:HostSoftIrqHigh
expr:avgby(instance)(rate(node_cpu_seconds_total{mode="softirq"}[5m]))*100>25
for:3m
labels:
severity:warning
annotations:
summary:"主机软中断占比过高"
description:"{{ $labels.instance }} 软中断 CPU 持续高于 25%"
-alert:ContainerCpuThrottlingHigh
expr:sumby(pod,namespace)(rate(container_cpu_cfs_throttled_periods_total[5m]))>20
for:5m
labels:
severity:warning
annotations:
summary:"容器 CPU 节流明显"
description:"{{ $labels.namespace }}/{{ $labels.pod }} 近 5 分钟 CPU throttling 持续升高"
5.3 备份与恢复
5.3.1 备份策略
任何 CPU 调优动作之前,先备份配置。尤其是 sysctl、IRQ 绑核、服务线程池、JVM 参数,改错了很容易引发更大的抖动。
#!/usr/bin/env bash
# 文件名:/usr/local/bin/backup_cpu_related_configs.sh
set -euo pipefail
BACKUP_DIR="/var/backups/cpu-tuning/$(date +%F_%H%M%S)"
mkdir -p "${BACKUP_DIR}"
cp -a /etc/sysctl.conf "${BACKUP_DIR}/"
cp -a /etc/sysctl.d "${BACKUP_DIR}/" 2>/dev/null || true
cp -a /etc/security/limits.conf "${BACKUP_DIR}/" 2>/dev/null || true
cp -a /etc/systemd/system "${BACKUP_DIR}/systemd" 2>/dev/null || true
sysctl -a > "${BACKUP_DIR}/sysctl-all.txt"
cat /proc/interrupts > "${BACKUP_DIR}/interrupts.txt"
cat /proc/softirqs > "${BACKUP_DIR}/softirqs.txt"
tar czf "${BACKUP_DIR}.tar.gz" -C "$(dirname "${BACKUP_DIR}")""$(basename "${BACKUP_DIR}")"
echo"backup saved to ${BACKUP_DIR}.tar.gz"
5.3.2 恢复流程
- 停止临时调优动作:回滚脚本、关闭异常定时任务、撤销临时限流或绑核
- 恢复配置文件:从最近一次备份还原
sysctl、服务配置、线程池参数 - 验证完整性:执行
sysctl --system,检查服务配置语法和启动状态 - 重启或热加载服务:在低峰期执行,并用监控观察至少 10 分钟
六、总结
6.1 技术要点回顾
- 先分层,再深入:整机、进程、线程、调用栈四层顺着走,判断不会跑偏
- 先分态,再取证:先区分
%usr/%sys/%soft/%steal,再决定抓进程还是抓内核路径 - 先保现场,再做动作:快照比重启更重要,没有现场很难闭环
- 线程级定位价值最高:很多 CPU 问题真正的根因都藏在热点线程和调用栈里
- 系统态 CPU 不能只盯应用:软中断、网卡队列、连接风暴经常才是真正的根因
- 容器环境要看节流和 steal:CPU 不够用不一定是程序写差,也可能是配额和宿主机资源争抢
6.2 进阶学习方向
- 学习资源:Linux kernel documentation、
sched 相关内核文档 - 实践建议:在测试环境复现 CPU quota 节流、绑核和调度延迟问题
- 学习资源:Brendan Gregg 的性能分析资料、
bcc-tools - 实践建议:先从
perf top、perf record、runqlat、profile 这类轻量工具入手
- 学习资源:Prometheus Alertmanager webhook、企业内巡检平台
- 实践建议:把 CPU、高负载、连接风暴的快照采集做成统一脚本和 SOP
6.3 参考资料
- Linux kernel CPU documentation - Linux 内核 CPU、调度与性能分析文档
- sysstat official site -
sar、mpstat、pidstat 工具说明 - perf wiki -
perf 使用方法和常见问题 - Brendan Gregg Blog - Linux 性能分析实战材料
附录
A. 命令速查表
top -H -p <PID> # 查看进程内线程 CPU
pidstat -u -t -p <PID> 1 5 # 观察线程级 CPU 趋势
mpstat -P ALL 1 3 # 查看每个核心使用率
sar -u ALL 1 5 # 查看 CPU 各 mode 分布
sar -n DEV,EDEV,TCP,ETCP 1 5 # 查看网络与 TCP 指标
iostat -xz 1 5 # 查看磁盘队列与 IO 延迟
cat /proc/softirqs # 查看软中断分布
cat /proc/interrupts # 查看硬中断分布
perf top -p <PID> -g # 实时看热点函数
perf record -F 99 -p <PID> -g -- sleep 20 # 采样留证据
B. 配置参数详解
kernel.perf_event_paranoid
- 建议:生产环境默认保持严格,排障时临时下调,结束后恢复
- 建议:不少于 28 天,双十一、618 这类业务建议保留 60 天
cpu.max / cpu.cfs_quota_us
- 建议:对低延迟业务慎用过小配额,避免频繁 throttling
- 建议:高流量网关节点关注这个参数,避免中断只压在少数核心
C. 术语表
| | |
|---|
| | |
| | CPU 在内核代码、系统调用、中断处理上消耗的时间 |
| | Linux 内核为网络、块设备等延后处理设计的轻量机制 |
| | |
| | 虚拟机本该获得 CPU,但被宿主机拿去服务其他虚机的时间 |