一次线上 CPU 飙高的排查过程
记录一次典型的 CPU 飙高排查。过程比结论更有价值——命令都会用,但顺序和取舍才是经验。
背景
下午三点告警:CPU 使用率 92%,持续 5 分钟。业务响应变慢但未完全不可用。
第一步:谁在吃 CPU
top -c # 按 P 排序,看进程与完整命令行
前几名全是 php-fpm worker。说明是业务代码在烧 CPU,不是系统任务。如果这里是 mysqld,路径就完全不同(转去查慢查询)。
第二步:在干什么
pidstat -u -p <pid> 1 5 # 确认持续吃满
strace -p <pid> -c -f # 汇总系统调用 10 秒
strace 显示大量 read(),来自同一个文件——一个每请求都会加载的大配置文件被反复解析。配合 FPM 慢日志(request_slowlog_timeout=2)抓到的栈,指向刚上线的一个改动:把一个 3MB 的 JSON 规则文件改成了每次请求重新解析。
第三步:确认影响面
# FPM 状态页
curl -s localhost/fpm-status
# active = max_children,listen queue 在涨 —— worker 被慢请求占满
请求变慢的本质:每个请求都变重,worker 周转率下降,排队开始。
第四步:止血
两条路:回滚该次发布;或热修复(把解析结果做进程内缓存)。当时选择回滚——变更记录完整、回滚脚本现成,5 分钟完成。CPU 回落到 18%。
第五步:修复与复盘
修复方案是「文件 mtime 未变时复用 opcache 缓存的解析结果」,并用 ab 压测对比:修复后 QPS 提升 2.4 倍。复盘产出两条规则:
- 涉及文件 IO 或外部调用的改动,必须提供压测数据;
- FPM 的
request_slowlog_timeout从 5 秒收紧到 2 秒——这次如果不是 strace 抓巧,定位还会更久。
复用这个模板
任何 CPU 飙高的排查都可以走这个顺序:
- top 定位进程(业务进程还是系统进程);
- strace/pidstat/perf 看它具体在干什么;
- 查该组件的「慢日志/状态页」确认影响面;
- 止血优先于根因(回滚、扩容、降级);
- 修复后必须复盘,把个案变成规则。
最后一条提醒:没有变更就没有故障。排查第一句永远是「最近改了什么」——这次答案是提前准备好的,因为发布记录完整。