4、CPU性能调优:从指标到实战
CPU调优这事儿,说难不难,说简单也不简单。我见过太多人一上来就盯着CPU使用率看,觉得使用率高了就是有问题。其实不然。今天咱们就把CPU调优的五个核心模块掰开揉碎,一个个讲清楚。
4.1 CPU性能指标解读
先说说怎么看CPU的“体检报告”。我个人习惯用top、vmstat、mpstat这三个工具打底。
| 指标 | 含义 | 正常范围 |
|---|---|---|
| %user | 用户态CPU时间占比 | 通常<70% |
| %sys | 内核态CPU时间占比 | 通常<30% |
| %iowait | 等待I/O的CPU时间占比 | 通常<5% |
| %steal | 虚拟机偷取时间 | 通常<10% |
| load average | 系统平均负载 | 不超过CPU核数 |
这里有个坑,我踩过。有一次线上告警,CPU使用率才30%,但系统响应慢得像蜗牛。查了半天,发现是%iowait飙到了40%。说白了,CPU在等磁盘干活,它闲着呢。所以别只看使用率,%iowait和load average要一起看。
核心原则:CPU使用率低 + 负载高 = 大概率在等I/O。CPU使用率高 + 负载高 = 计算密集型任务。
4.2 上下文切换优化
上下文切换,说白了就是CPU从一个任务切到另一个任务。每次切换都有开销,大概几微秒。频繁切换,性能就崩了。
怎么查?用vmstat 1看cs列(context switch)。如果每秒切换超过几万次,就得注意了。
# 查看上下文切换
vmstat 1 5
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 123456 1234 56789 0 0 10 20 500 3000 15 5 80 0 0
你看,cs是3000,还算正常。如果超过10000,就要排查了。
优化的思路其实就三条:
- 减少线程数:线程太多,切换自然多。我建议用线程池控制并发数。
- 减少锁竞争:锁会导致线程阻塞,阻塞了就切换。用无锁数据结构或者读写锁优化。
- 调整时间片:内核参数
kernel.sched_min_granularity_ns可以调,但一般不动。
我的经验:有一次一个Java服务,线程数开到500,上下文切换每秒8万次。后来用线程池限制到50,切换降到5000,吞吐量翻了一倍。你想想看,这差距多大。
4.3 CPU亲和性绑定
CPU亲和性,就是把进程或线程绑定到特定CPU核上。好处是减少缓存失效和上下文切换。
为什么有效?因为CPU有L1/L2缓存,如果进程老在不同核上跑,缓存就白费了。绑定后,缓存命中率能提高不少。
# 使用taskset绑定进程到CPU 0-3
taskset -c 0-3 ./myapp
# 查看进程当前绑定
taskset -p 1234
我建议对关键业务进程做绑定。比如数据库、消息队列这些。但别绑太死,留几个核给系统用。
注意:别把所有核都绑上。系统进程也需要CPU,否则会出问题。我曾经把16核全绑给应用,结果SSH都连不上,尴尬了。
4.4 中断处理优化
中断,就是硬件通知CPU“我有事”。比如网卡收到数据包,会触发中断。中断处理不好,CPU就忙着响应中断,正经事干不了。
怎么查中断?用cat /proc/interrupts。你会看到每个CPU处理了多少中断。
# 查看中断分布
cat /proc/interrupts | grep eth0
CPU0 CPU1 CPU2 CPU3
0: 1000 2000 1500 1800 IR-IO-APIC 2-edge eth0
如果中断都集中在CPU0上,那CPU0就累死了。解决办法是中断亲和性绑定,把中断分散到多个核。
# 设置中断亲和性(以eth0为例)
echo 0f > /proc/irq/24/smp_affinity
这里0f是位掩码,表示CPU 0-3。嗯,要注意,这个值要写十六进制。
避坑指南:我曾经在网卡中断没做均衡的机器上跑高并发服务,CPU0使用率100%,其他核闲着。做了中断亲和性后,CPU使用率均匀了,吞吐量提升了30%。
4.5 NUMA架构下的CPU调优
NUMA(非统一内存访问)架构下,CPU访问本地内存快,访问远端内存慢。差距可能达到2-3倍。
怎么查NUMA拓扑?用numactl --hardware。
# 查看NUMA节点
numactl --hardware
available: 2 nodes (0-1)
node 0 cpus: 0 1 2 3
node 0 size: 16384 MB
node 1 cpus: 4 5 6 7
node 1 size: 16384 MB
你看,两个节点,每个节点4核16G内存。如果进程在node0上跑,但内存分配在node1上,性能就差了。
优化策略:
- 绑定进程到同一NUMA节点:用
numactl --cpunodebind=0 --membind=0 ./myapp - 使用interleave策略:如果内存访问均匀,可以用
--interleave=all - 监控跨节点访问:用
numastat看numa_hit和numa_miss
我的习惯:对于内存密集型应用,比如Redis、数据库,我一般会绑定到同一个NUMA节点。对于计算密集型,可以跨节点,但要注意内存分配策略。
知识体系总览
下面这张图,把CPU调优的五个模块串起来了。你可以把它当作一个检查清单。
调优这事儿,没有银弹。每个场景都不一样。但只要你把指标看懂了,工具用熟了,再结合我上面说的这些经验,大部分问题都能搞定。
最后说一句:别一上来就调参数。先监控,再分析,最后动手。这是我从无数次翻车中总结出来的教训。
想要更多资料,联系可可