服务器CPU飙升定位

技术背景:理解CPU使用率的本质

CPU使用率的构成

很多人认为CPU使用率就是一个简单的百分比,但实际上Linux系统的CPU使用率包含多个维度:

# 使用top命令查看CPU详细信息
%Cpu(s):  25.3 us,  5.2 sy,  0.0 ni, 68.1 id,  1.2 wa,  0.0 hi,  0.2 si,  0.0 st

各字段含义:

  • us (user): 用户空间进程消耗的CPU时间

  • sy (system): 内核空间消耗的CPU时间

  • ni (nice): 改变过优先级的进程消耗的CPU时间

  • id (idle): 空闲CPU时间

  • wa (iowait): 等待I/O完成的CPU时间

  • hi (hardware interrupt): 硬件中断消耗的CPU时间

  • si (software interrupt): 软件中断消耗的CPU时间

  • st (steal): 虚拟机被hypervisor偷走的CPU时间

CPU高占用的常见类型

根据不同指标的表现,CPU高占用可以分为几类:

  1. 1. 用户态CPU高(us高):通常是应用程序代码执行导致,如死循环、复杂计算、正则匹配等

  2. 2. 内核态CPU高(sy高):可能是系统调用过多、网络包处理、进程创建销毁频繁等

  3. 3. I/O等待高(wa高):磁盘I/O性能瓶颈,CPU在等待磁盘响应

  4. 4. 软中断高(si高):通常与网络流量大有关,如DDoS攻击、数据传输密集等

关键认知:CPU使用率100%并不代表CPU真正在"工作",wa高的情况下CPU实际是在等待,优化方向完全不同。

为什么说前3分钟最关键?

CPU突然飙高通常伴随着:

  • • 业务响应变慢,用户体验下降

  • • 队列堆积,雪崩效应可能随时发生

  • • 监控数据采集可能受影响,错过关键信息

  • • 黄金时间窗口,趁现场还在赶紧抓取证据

因此,快速定位比完美分析更重要——先止血,再根治。

核心内容:3招定位法详解

第一招:快速锁定问题进程(30秒)

使用top命令快速定位

重点关注的信息:

  1. 1. 进程PID和命令:记录占用CPU最高的进程ID和完整命令

  2. 2. CPU%:单个进程占用的CPU百分比(注意多核服务器可能超过100%)

  3. 3. TIME+:进程累计使用的CPU时间

  4. 4. S列(状态):R(运行中)或D(不可中断睡眠,通常在等I/O)

使用htop(如果已安装)

使用ps命令快速抓取

实战技巧:一键诊断脚本

案例分享:某次故障中,top显示一个Java进程CPU占用350%(4核服务器),但实际上是该进程启动了4个线程,每个线程都在满负荷运行。这种情况需要进一步查看线程级别的CPU占用。

第二招:精准定位到具体线程和代码(90秒)

找到问题进程只是第一步,还需要定位到具体是哪个线程、哪行代码导致的问题。

查看进程内的线程CPU占用

关键步骤:记录CPU占用最高的线程ID(LWP),并转换为16进制(因为堆栈信息中线程ID是16进制)

Java应用定位到具体代码

典型问题代码特征:

  • • 死循环:堆栈一直在同一个方法内

  • • 正则表达式性能问题:堆栈中有java.util.regex.Pattern.matcher

  • • 序列化/反序列化:大量ObjectOutputStreamJSON相关调用

  • • 复杂SQL或ORM:堆栈中有数据库驱动相关代码

Python/Node.js应用定位

Python应用:

Node.js应用:

Go应用定位

C/C++应用定位

实战案例:某次故障中,一个Python数据处理服务CPU突然飙升至400%。使用py-spy发现是pandas的merge操作在处理一个300万行的DataFrame。最终通过改为分批处理(chunking)解决,CPU降至30%左右。

第三招:分析系统级资源瓶颈(60秒)

有些情况下,CPU高占用并非应用代码问题,而是系统级资源瓶颈导致。

检查I/O等待

判断标准:

  • • 如果%wa (iowait)高(>20%),且%util接近100%,说明是磁盘I/O瓶颈

  • • 优化方向:检查是否有大量日志写入、数据库查询未命中索引、大文件操作等

检查网络软中断

判断标准:

  • • 如果%si (software interrupt)高(>10%),通常是网络流量大

  • • 常见原因:DDoS攻击、大量短连接、网络包处理不合理

检查进程创建销毁频率

判断标准:

  • • 如果%sy (system CPU)高(>30%),可能是系统调用过多

  • • 常见原因:CGI程序每次都fork新进程、shell脚本大量调用外部命令

检查上下文切换

判断标准:

  • • 上下文切换>100万次/秒,可能存在性能问题

  • • 非自愿切换过多,说明进程/线程数过多,CPU调度压力大

检查内存压力

判断标准:

  • • 如果内存不足导致频繁swap,CPU会消耗在内存页换入换出上

  • • MemAvailable < 10%总内存,且swap使用率高,需要关注

综合诊断命令

实践案例:真实故障的完整诊断流程

案例背景

某电商API网关服务,在正常业务流量下突然CPU飙升至98%,持续10分钟未恢复。API响应时间从平均50ms飙升到5000ms,触发大量超时告警。

完整诊断过程

14:32:15 - 收到告警,立即SSH登录服务器

第一招:快速锁定进程(用时25秒)

第二招:定位具体问题(用时90秒)

第三招:系统级分析(用时60秒)

问题定位(用时3分10秒)

定位到是/api/product/detail接口响应极慢,导致nginx worker被占满。进一步排查后端PHP-FPM:

根因确认:

  1. 1. 数据库连接超时,每个请求都在等待MySQL连接建立(默认超时30秒)

  2. 2. 同时有100个请求在等待,用户端不断重试,形成恶性循环

  3. 3. 进一步检查发现MySQL主库宕机,VIP未切换到从库

处理措施:

  1. 1. 紧急手动切换MySQL主从(3分钟)

  2. 2. 重启PHP-FPM清空阻塞的请求(1分钟)

  3. 3. 逐步放开流量,监控恢复情况(5分钟)

总耗时: 从告警到恢复,共12分钟

复盘总结

做得好的地方:

  • • ✅ 3招定位法帮助快速找到表层现象(nginx CPU高)

  • • ✅ 未局限于nginx本身,顺藤摸瓜找到后端PHP和数据库问题

  • • ✅ 保留了关键日志和strace输出,便于复盘

可改进之处:

  • • ⚠️ 数据库主从切换不应手动,应配置自动切换(MHA/Orchestrator)

  • • ⚠️ PHP-FPM应配置更合理的超时时间,避免长时间阻塞

  • • ⚠️ 应增加数据库连接失败的监控告警,更早发现问题

最佳实践与预防措施

日常监控配置

必备的CPU监控指标:

应用层优化建议

1. 合理设置超时时间

2. 避免CPU密集型操作阻塞主线程

3. 正则表达式优化

4. 限流和熔断

系统层优化建议

1. 调整CPU调度策略

2. 优化网络参数

3. I/O调度器优化

应急工具箱

建立一个快速诊断工具集:

使用方法:

总结

服务器CPU飙到100%是运维工作中最常见的性能问题之一。通过本文介绍的"3招3分钟定位法"——快速锁定问题进程、精准定位到具体线程和代码、分析系统级资源瓶颈——可以在大多数情况下快速找到根本原因。

核心要点

  1. 1. 分层定位:从进程→线程→代码,逐层深入

  2. 2. 全面分析:不仅看CPU,还要看I/O、网络、内存、上下文切换等

  3. 3. 保留证据:诊断过程中的输出要保存,便于复盘和深度分析

  4. 4. 工具先行:准备好一键诊断脚本,紧急时刻不慌乱

Last updated