CPU 100% 不一定是故障:压测打满、业务饱和、突变打满的判断与排查

前言

"CPU 飙到 100% 怎么排查"是 SRE 面试的必考题,但十个候选人八个开口就是工具连招:top、vmstat、iostat、pidstat、perf------报得挺顺,方向全错。本文先拆掉"CPU 100% = 故障"这个隐含前提,给出三种性质的定性判断,再按 us/sy/wa/si 四分法展开每一条排查命令的实际用法,最后附一个最邪门的真实案例:sys 90%、strace/iostat/数据库视图全部干净,根因是大内存进程 fork 触发的内核页表锁竞争。

一、先定性:CPU 100% 有三种性质,只有一种算故障

性质 特征 该做什么
① 压测/容量测试故意打满 你自己打上去的 记录饱和点和 P99 拐点,这是容量规划的输入,不是告警
② 业务增长顶到边界 曲线跟着 QPS/连接数慢慢爬 走扩容、加缓存、拆读写------容量流程,不是救火
③ 突变打满、与业务量对不上 平时 30% 突然 100%;或半夜没人用反而满 只有这种才轮到 top 和下面的四分法

面试官追问一句"这 100% 是不是你想要的",答不上来就露馅------这是本题真正的分水岭。

还有一个口径陷阱:top 第一行 %Cpu(s) 是整机所有核汇总 ,进程列表里的 %CPU 是单核基准(可跑到核数×100)。"CPU 100%" 必须先问清是整机满还是单核满,两者给出的扩容建议完全相反。

二、us 高:CPU 真在替应用算(命令实操)

us 是用户态代码消耗,四档里唯一一种"加机器理论上真能缓解"的情况。标准动作链:

复制代码
# 1. 锁定进程(按 CPU 排序)
top                      # 大写 P 按 CPU 排序
# 2. 下钻线程
top -Hp <pid>            # 看哪个线程最烧
# 3. 线程号转十六进制(Java 栈对齐用)
printf "%x\n" <tid>
# 4. 快速看热点函数(30 秒出结论)
perf top -p <pid>
# 5. 留证据出火焰图
perf record -F 99 -g -p <pid> -- sleep 30
perf script > out.perf
stackcollapse-perf.pl out.perf > out.folded
flamegraph.pl out.folded > flame.svg

FlameGraph 工具安装:

复制代码
# perf 本体(Debian/Ubuntu 与 RHEL 系)
apt install linux-tools-common linux-tools-$(uname -r)   # 或 yum install perf
# 火焰图渲染脚本
git clone https://github.com/brendangregg/FlameGraph.git
export PATH=$PATH:$(pwd)/FlameGraph

火焰图只看一个东西:平顶。最宽的平台就是最烧 CPU 的函数。常见元凶:死循环、正则回溯、频繁序列化、GC 压力。Java 加一手 jstack,线程栈跟火焰图对上,能定位到代码行。

三、sy 高:几乎全是应用侧行为,先判诱因

CPU 不会自己进内核------sy 高背后全是应用在发系统调用。值钱的是先判是哪一类诱因,因为处理方向完全不同:

诱因 判别命令 看什么 处理方向
① 上下文切换风暴 vmstat 1、pidstat -wt 1 cs 列每秒几万次警惕、几十万次基本是风暴 减线程、降锁粒度、连接复用
② syscall 密集 strace -c -p <pid>(采样 10s) syscall 按次数/耗时排序,read/write/fsync 占比 合并 IO、批量写、降 fsync 频率
③ 单点堵塞连锁等待 pidstat -wt、应用侧锁/会话视图 大量线程卡在同一把锁/同一个等待事件 找到阻塞源头,其他都是治标
④ 大内存进程被高频 fork perf record -g(见第五节) 内核页表函数占大头 禁高频 fork / 进程池 / HugePage

两个实操注意:

  • strace 在生产要慎用 ------它会让目标进程每次系统调用都陷入停顿,高并发服务挂上 strace 本身就能把性能打崩。粗排用 strace -c(统计模式,开销小),细查可用 perf trace 或 bpftrace 这类低开销方案。
  • 关键判断:sy 高盲目扩容几乎一定是错的。锁竞争越扩越凶,线程越多切换越多,机器多只是把同一个问题复制更多份。

四、wa 高:八成是访问模式问题,不是磁盘问题

wa 的准确定义:"CPU 空闲、且至少有一个进程在等 IO"的占比。wa 高时 CPU 其实是闲着的------所以 wa 高和总使用率低,常常是同一件事的两种说法。

复制代码
# 磁盘层
iostat -x 1        # 看 %util / await / aqu-sz
# 进程层
iotop -oP          # 只看有 IO 的进程

组合读法(这是最容易翻车的陷阱):

  • %util 逼近 100 → 磁盘真被打满,考虑换盘/分流;
  • %util 还低但 await 已在涨 → 不是磁盘瓶颈,是访问模式问题:大查询、逐行提交、碎 IO、日志狂刷、归档抢占。落点是改访问模式(拆查询、加索引、合并碎 IO、批量写、调刷盘策略);
  • wa 要结合核数看:4 核机一个进程等 IO 就能顶 100%,64 核机同样负载可能只有 2%------单看 wa 会误判。

关键判断:wa 高加 CPU 一点用没有------CPU 现在是闲的,加多少都还是闲着等 IO。

五、si 高与一次 sys 90%、全部工具失效的排查

si 是软中断,主要来自网络收包:

复制代码
mpstat -P ALL 1            # si 集中在哪几颗核
cat /proc/softirqs         # 中断类型分布
sar -n DEV 1               # 收包速率
cat /proc/interrupts       # 各 IRQ 挂在哪些核

最常见根因不是流量大,是中断亲和没收好------NET_RX 全压一两颗核,那颗核 si 100%、整机才 5%。解法是网卡多队列 + RPS/RFS(跟应用代码无关):

复制代码
# 把某个 IRQ 绑到指定核(写掩码)
echo 2 > /proc/irq/24/smp_affinity
# RPS: 软件层把收包处理摊到多核(每队列掩码, f = 低 4 核)
for q in /sys/class/net/eth0/queues/rx-*; do echo f > $q/rps_cpus; done
# RFS: 让协议栈把包送到应用所在核
sysctl -w net.core.rps_sock_flow_entries=32768
for q in /sys/class/net/eth0/queues/rx-*; do echo 4096 > $q/rps_flow_cnt; done
# 32768 = 队列数 × 每队列 rps_flow_cnt

顺手看一眼 st(steal):那是宿主机抢走的 CPU,云上超卖常见,你修不了,只能报云平台。

真实案例:sys 90%,strace / iostat / AWR 全干净

MySQL 实例 CPU 跑满,sys 占 70%~90%、us 很低。套路查遍全部干净:慢查询日志干净、QPS 平稳、strace -c 看系统调用频次正常、iostat 磁盘不忙、内存充足。但 sys 就在那儿。

根因:定时备份脚本用 fork() 拉子进程跑 mysqldump,而 mysqld 占了 40GB 内存。fork 不拷贝物理内存,但内核必须遍历复制全部页表 ------几十万个页表条目每次 fork 都要过一次全局页表锁,CPU 全烧在 ptep_clear_flush 这类页表函数上。mysqld 自己没有高频系统调用,开销全在 fork 那一刻的内核遍历里。

验证:停掉定时 fork 任务,sys 十秒内回落。同一坑在 Oracle 上又踩一次(SGA 64G + fork 型监控 Agent),这次 AWR 完全看不到------瓶颈在 OS 内核层,数据库内部视图根本抓不到。

这类问题四个共同特征:sys 高 us 低 / 全部常规工具干净 / 机器上有几十 GB 大内存进程 / 有定时任务在 fork。唯一解法是 perf 开内核调用栈:

复制代码
perf record -g -a -- sleep 30    # -g 关键: 记录调用栈, 包括内核栈
perf report
# 会看到 ptep_clear_flush / copy_page_range 这类页表函数占大头

只开普通 perf 看函数名,你只会看到一堆看不懂的内核符号。修法三选一:LVM snapshot 替代 mysqldump 定时 fork / 缩小进程内存 / posix_spawn 替代 fork / 启用 HugePage 压页表数量。一句话教训:大内存进程不要高频 fork。

六、load 高 ≠ CPU 高:先抓 D 状态进程

最容易翻车的追问:load 50 但 CPU 才 20%,一堆人慌着加机器。

复制代码
ps -eo stat,pid,cmd | grep -E "^D"    # 抓不可中断睡眠进程

D 状态在等 IO 或等锁,这种负载加多少机器都下不来,瓶颈不在算力。真正 CPU 型的高 load,才回到上面四分法。

总结

先判性质(压测打满/业务饱和/突变打满,只有第三种算故障)→ 整机满还是单核满 → us 查应用(进程→线程→火焰图→代码行)→ sy 先判四类诱因(切换风暴/syscall 密集/堵塞连锁/fork 页表锁)再定处理 → wa 改访问模式不是加 CPU(%util vs await 组合读)→ si 查中断亲和与多队列 → load 高先抓 D 状态。工具谁都会背,能把"为什么是这个顺序"讲清楚,面试官就知道你在生产上真值过班。

相关推荐
Rabitebla2 小时前
【Linux】进程控制(十二) 自定义 Shell(二):内建命令与进程工作目录管理
linux·c++·笔记·学习·bash
时间的拾荒人2 小时前
Redis 主从复制详解:配置实操 + 原理机制
数据库·redis·面试
匠云创新4 小时前
读完一万段要两分钟:我们把标书读取从 WPS 对象模型搬到了 docx 解析
性能优化·node.js
坤坤子吖6 小时前
Linux网络编程:TCP 粘包与半包问题,如何正确处理 TCP 流式数据?
linux·服务器·网络·笔记·网络协议·学习
AI_Gump6 小时前
Harness篇|DeepSeek Harness 上手机:五条安卓移植路线与 App 内 Linux 拆解
android·linux·人工智能·智能手机·deepseek
星恒随风8 小时前
MySQL 从零入门(六):复合查询详解——多表查询、自连接、子查询与 UNION
linux·服务器·数据库·mysql
少司府8 小时前
嵌入式入门篇(二):初识STM32F103ZET6
linux·python·stm32·单片机·嵌入式硬件·mcu·嵌入式软件
无足鸟ICT9 小时前
【RHCA+】IO调度算法
linux·服务器
打工仔折腾 AI9 小时前
Changedetection.io 网页变动监控:Docker 部署、通知配置与公网暴露实践
运维·人工智能·后端·python·docker·容器·性能优化
wacsii9 小时前
【Linux进程(四)】命令行参数与环境变量
linux·服务器