4、CPU瓶颈定位:CPU使用率分析、上下文切换、运行队列、软中断、硬中断
说到CPU瓶颈,很多人第一反应就是看使用率。但说实话,光看使用率远远不够。我见过太多案例,CPU使用率才30%,系统却慢得像蜗牛。为什么?因为真正的瓶颈往往藏在那些你看不见的角落里。
这一节,我带你把这几个关键指标掰开揉碎了讲清楚。你想想看,CPU就像一条高速公路,使用率只是告诉你路上有多少车,但真正影响通行效率的,是堵车、是频繁的并线、是突发的交通事故。
4.1 CPU使用率分析——别被数字骗了
CPU使用率,说白了就是CPU忙活的时间占比。但这里有个坑——使用率高不等于有问题,使用率低也不等于没问题。
我习惯把使用率拆成三块来看:
- 用户态(user):跑应用程序的时间。比如你的Java进程、Nginx进程。
- 系统态(sys):内核干活的时间。比如系统调用、内存分配。
- 等待I/O(iowait):CPU在等磁盘或网络数据。这个值高了,说明I/O才是真凶。
用top命令看一眼:
# 按1查看每个CPU核心
top - 09:15:33 up 10 days, 2:15, 3 users, load average: 2.50, 2.30, 2.10
%Cpu(s): 65.0 us, 20.0 sy, 0.0 ni, 10.0 id, 5.0 wa, 0.0 hi, 0.0 si, 0.0 st
你看这个例子,us 65%,sy 20%,wa 5%。系统态占了20%,这就不太正常了。我在项目中遇到过类似情况,最后发现是一个程序疯狂调用gettimeofday(),每秒几万次系统调用,直接把sys打爆了。
核心判断逻辑:
- us高 → 应用层瓶颈,优化代码或扩容
- sy高 → 系统调用频繁,检查锁、上下文切换
- wa高 → I/O瓶颈,先查磁盘或网络
- id低 + us低 + sy低 → 大概率在等锁
4.2 上下文切换——看不见的性能杀手
上下文切换,就是CPU从一个任务切到另一个任务。每次切换都要保存寄存器、加载新状态,这开销可不小。
用vmstat看上下文切换频率:
# 每秒输出一次
vmstat 1
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
2 0 0 1623456 1234 567890 0 0 10 20 1500 8000 30 20 50 0 0
注意cs这一列,表示每秒上下文切换次数。8000次/秒,这个值已经偏高了。一般来说:
- 单核CPU:cs < 5000 算正常
- 多核CPU:cs < 10000 算正常
- 超过20000,基本可以断定有问题
为什么会这么高?我总结了几种常见情况:
- 线程数过多:一个进程开了几百个线程,互相抢CPU
- 锁竞争激烈:线程频繁加锁解锁,导致被迫切换
- 定时器太多:高精度定时器会触发大量中断
我的排查习惯:先用pidstat -w 1看哪个进程的cswch(自愿切换)和nvcswch(非自愿切换)最高。自愿切换高,说明在等资源;非自愿切换高,说明时间片被抢了,线程太多了。
4.3 运行队列——CPU到底有多忙
运行队列(run queue)表示正在排队等CPU的任务数。这个指标比使用率更能反映CPU的真实压力。
用uptime或top看load average:
uptime
09:30:22 up 10 days, 2:30, 3 users, load average: 4.50, 3.80, 3.20
load average后面的三个数,分别是1分钟、5分钟、15分钟的平均运行队列长度。怎么判断是否过载?
一个简单的公式:load average / CPU核心数 > 0.7 就要警惕,> 1.0 说明CPU已经忙不过来了。
举个例子,4核CPU,load average 4.5,意味着平均有4.5个任务在排队。4核只能同时跑4个,多出来的0.5个在等。嗯,这已经有点挤了。
注意:load average高不一定就是CPU瓶颈。如果进程在等I/O(D状态),也会计入load。我曾经遇到一个案例,load飙到30,CPU使用率才10%,最后发现是NFS挂载点挂了,一堆进程卡在I/O等待上。
4.4 软中断与硬中断——打断CPU的“不速之客”
中断,就是硬件或软件突然打断CPU,让它去处理紧急事情。分两种:
- 硬中断(hi):硬件发起的,比如网卡收到数据包、磁盘完成读写
- 软中断(si):内核发起的,比如网络协议栈处理、定时器
用cat /proc/interrupts看硬中断分布:
cat /proc/interrupts
CPU0 CPU1 CPU2 CPU3
0: 123 0 0 0 IO-APIC 2-edge timer
1: 10 0 0 0 IO-APIC 1-edge i8042
27: 456789 123456 0 0 PCI-MSI 512000-edge eth0
你看,网卡中断(eth0)几乎都打在CPU0和CPU1上,CPU2和CPU3闲着。这就是典型的中断不均衡问题。
软中断可以用cat /proc/softirqs查看:
cat /proc/softirqs
CPU0 CPU1 CPU2 CPU3
HI: 0 0 0 0
TIMER: 1234567 1234567 1234567 1234567
NET_TX: 12345 12345 12345 12345
NET_RX: 987654 987654 987654 987654
BLOCK: 123 123 123 123
TASKLET: 0 0 0 0
SCHED: 2345678 2345678 2345678 2345678
重点关注NET_RX(网络接收)和BLOCK(块设备)。如果某个CPU的NET_RX特别高,说明网络流量都压在一个核上。
解决方案:
- 硬中断不均衡 → 设置
irqbalance服务,或手动绑定中断到不同CPU - 软中断过高 → 开启RPS(Receive Packet Steering),让软中断分散到多核
- 网络软中断打满单核 → 使用多队列网卡,配合RSS(Receive Side Scaling)
4.5 一张图看懂CPU瓶颈定位
下面这张图,是我自己总结的CPU瓶颈排查流程。你照着走一遍,基本不会漏掉关键点。
4.6 实战排查清单
最后,给你一份我平时用的排查清单。遇到CPU问题,按这个顺序来:
| 步骤 | 命令 | 看什么 | 阈值 |
|---|---|---|---|
| 1 | top |
us/sy/wa/id | sy > 30% 或 wa > 20% 需关注 |
| 2 | uptime |
load average | load/核心 > 0.7 |
| 3 | vmstat 1 |
r(运行队列)、cs(切换) | r > 核心数*2 或 cs > 10000 |
| 4 | pidstat -w 1 |
自愿/非自愿切换 | 非自愿切换占比高 → 线程过多 |
| 5 | cat /proc/interrupts |
中断分布是否均衡 | 单个CPU处理80%以上中断 |
| 6 | cat /proc/softirqs |
NET_RX、BLOCK | 单个CPU软中断异常高 |
一个小技巧:我习惯把top的显示模式改成按CPU使用率排序(按P键),然后按1展开所有核心。一眼就能看出是单核打满还是多核均衡。如果是单核打满,多半是中断不均衡或者有单线程瓶颈。
嗯,CPU瓶颈定位这块,说白了就是四个字——望闻问切。望,看使用率和load;闻,听上下文切换和中断;问,查进程状态和锁竞争;切,用perf等工具深挖热点。把这套流程走熟了,大部分CPU问题都能快速定位。
想要更多资料,联系可可