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 状态。工具谁都会背,能把"为什么是这个顺序"讲清楚,面试官就知道你在生产上真值过班。

相关推荐
9624565 小时前
一次 Dell PowerEdge R750xs 启动故障复盘:从 iDRAC 带外管理到 Linux 多内核回退与 initramfs 重建
linux·运维·服务器
Zevalin爱灰灰5 小时前
Linux入门第一部分(Ubuntu入门篇) 第二章 浅尝Ubuntu(二)
linux·嵌入式·1024程序员节
Random Glow6 小时前
聚簇索引 vs 二级索引:回表、覆盖索引、索引下推
数据库·mysql·性能优化·innodb·1024程序员节·索引优化
北京中科新远科技6 小时前
高速接口兼容排查实战:OSFP、QSFP112与QSFP56如何逐层验证
linux·运维·网络
Ruiery6 小时前
Linux 6.6内核 CPU 深度解析(零):x86 内核引导协议 — 从 bootloader 到 main()
linux·运维·服务器
Ruiery6 小时前
Linux 6.6内核 CPU 深度解析(十六):x86 保护机制 — 从 ring 到页表的权限防线
linux·运维·服务器
xiangyun616 小时前
C++ priority_queue 底层:手撕二叉堆与 Top K 问题
c++·算法·面试·stl·1024程序员节
qetfw6 小时前
Linux 文件清理:find、mtime 与安全传参验证
linux·shell·1024程序员节
可乐_不渴7 小时前
【Linux】 项目自动化构建工具makefile&& 版本管理Git&&第一个程序
linux·git·自动化·1024程序员节
莫叫石榴姐8 小时前
半导体业务数仓建模: Lot、Wafer、Die、Recipe、OEE、YMS 建模要点
数据仓库·sql·面试·职场和发展·矩阵·1024程序员节