首页 > Technology > 正文

服务器 CPU 跑满 100% 怎么查?五步定位根因

fenij 2026-08-14 39 Technology

告警短信半夜响,登上去 top 一看,load average 三个数字全在 30 以上。这时候最忌讳的就是上去先 kill -9。杀错了业务直接断,而且下次照样复发。下面这五步是我平时用的顺序,从「哪个进程」一路挖到「哪一行代码」。

第一步:先看 CPU 烧在哪个维度

top,按 1 展开每个核心,重点看第三行那几个百分比。不同的高点意味着完全不同的方向。

指标 含义 高了说明什么
us 用户态耗时 业务代码在算,死循环或正则回溯
sy 内核态耗时 系统调用太频繁,上下文切换过多
wa 等 IO 的时间 磁盘或网络是瓶颈,CPU 其实在闲等
si 软中断 网络包量大,可能正被打
st 被宿主机偷走 虚拟机超卖,你自己改不了

wa 高但 us 不高的时候,别去优化代码,去查磁盘。iostat -x 1%utilawait,接近 100% 就是磁盘顶不住了。

st 持续超过 10,基本可以确定是共享宿主机上的邻居在抢资源。这种情况优化自己没用,找服务商或者换机型。

第二步:锁定进程

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

或者用 pidstat,能看到采样区间内的平均值,比 top 的瞬时值更可信:

pidstat -u 1 5

这一步要留意一个坑:如果 CPU 是被几十个短命进程轮番占满的,ps 抓不到。这时用 execsnoop(bcc-tools)或者土办法循环采样:

for i in $(seq 1 20); do ps -eo pid,comm,%cpu --sort=-%cpu | sed -n '2p'; sleep 0.5; done

输出里反复出现的进程名就是元凶,常见的是失控的 cron 脚本或者备份任务。

第三步:钻到线程级

进程定位了,但一个 Java 或 Node 进程里几百个线程,还得再往下一层。

1
top -H -p <PID> 找出最耗 CPU 的线程 TID

2
printf "%x\n" <TID> 把十进制转成十六进制

3
jstack <PID> | grep -A 30 nid=0x<hex> 拿到调用栈

Java 服务这套组合拳基本一击必中,栈顶那几行就是问题代码。Node 换成 node --prof 或者线上直接 kill -USR1 <PID> 开 profiler。Python 用 py-spy dump --pid <PID>,不用改代码不用重启,特别适合线上救火。

第四步:分清在算还是在等

看不出来栈的时候,统计一下系统调用分布:

strace -c -f -p <PID> -o /tmp/strace.txt
# 跑 10 秒后 Ctrl+C,然后看 /tmp/strace.txt

如果 futexepoll_wait 占了绝大多数调用次数,那 CPU 时间花在锁竞争和等待上,不是在算。strace 本身会明显拖慢目标进程,线上核心服务慎用,用完立刻退出。

更轻量的选择是 perf,开销小得多:

perf top -p <PID>
# 或者采样 30 秒生成报告
perf record -F 99 -p <PID> -g -- sleep 30
perf report

第五步:常见元凶速查

📊
核数 × 1
load 的健康上限

99Hz
perf 采样推荐频率

🔧
10%
st 超过此值即超卖

按出现频率排,我遇到最多的是这几类:

MySQL 全表扫描。某个没走索引的查询被高频调用,mysqld 单进程吃满几个核。SHOW FULL PROCESSLIST 看有没有长时间 Sending data 的语句,再 EXPLAIN 一遍。

正则回溯爆炸。用户输入触发了灾难性回溯,一条请求能跑几十秒。PHP、Node、Java 都中过招。栈里会明显停在正则相关的帧上。

日志写爆。某个循环里疯狂打 debug 日志,磁盘 IO 拉满,wa 跟着上去。lsof -p <PID> | grep log 看它在写哪个文件,文件大小涨得多快。

被爬虫或 CC 打。si 偏高,配合 ss -s 看连接数暴涨。这时候先在 Nginx 层限流,别急着扩容。

容器 CPU limit 太低。Kubernetes 里 cpu.limit 卡得死,进程被节流。宿主机看 CPU 不高,容器里却卡到不行。cat /sys/fs/cgroup/cpu.statthrottled_time 在涨就是这个原因。

常见问题

问:load average 多少算高?
拿 load 除以核心数。nproc 输出 4,load 在 4 以内算正常,超过 8 就该看了。持续高于核数两倍,队列已经在积压。单看数字没意义,一定要对着核数算。

问:CPU 100% 但找不到高占用进程,怎么办?
两种可能。一是短命进程轮番上,用上面第二步的循环采样法抓。二是内核线程在忙,ps -eo pid,comm | grep '\[' 看有没有 kswapd0(内存吃紧在换页)或 ksoftirqd(软中断压力)在跑。

问:能不能直接 kill 掉最占 CPU 的进程?
应急可以,但先用 kill -3(Java)或 kill -QUIT 让它输出栈信息再杀,不然重启后你还是不知道原因。真要抢救优先用 renice +19 -p <PID> 降优先级,比直接杀温和得多。

你在排查 CPU 打满时踩过什么反直觉的坑,欢迎到 fenij.com 留言分享,这类经验往往比工具本身更值钱。

相关完整手册

系统化的排查与配置思路,建议顺手收藏这几篇完整手册: