1. 问题现象与初步判断
上周五凌晨2点,我正睡得迷迷糊糊,突然被一阵急促的报警短信惊醒——生产环境某台服务器的CPU使用率飙到了100%。这种半夜突发的性能问题最让人头疼,但也是最能锻炼排查能力的机会。今天就把这次完整的排查过程记录下来,分享给可能遇到类似问题的同行。
CPU满载通常表现为:
- 系统响应明显变慢,SSH登录延迟高
top命令显示单个或多个进程CPU占用异常- 负载平均值(load average)持续高于CPU核心数
- 可能伴随OOM killer日志或进程自动终止
重要提示:遇到CPU满载时不要立即重启服务器!这会导致问题现场丢失,可能掩盖真正的隐患。正确的做法是先保留现场数据,再针对性处理。
2. 排查工具与命令速查
2.1 基础诊断三板斧
bash复制# 1. 查看整体CPU使用情况(按1显示各核详情)
top -c
# 2. 按CPU占用排序进程
ps -eo pid,ppid,cmd,%mem,%cpu --sort=-%cpu | head -n 10
# 3. 查看系统负载和运行队列
uptime
vmstat 1 5
2.2 进阶诊断工具包
bash复制# 1. 进程级CPU热点分析
pidstat -u 1 5
# 2. 系统调用追踪
strace -cp <PID>
# 3. 性能快照(需提前安装)
perf top -p <PID>
3. 典型场景深度解析
3.1 案例一:Java应用线程死循环
现象特征:
top显示某个Java进程CPU持续99%ps -T -p <PID>可见单个线程占用异常
排查步骤:
- 获取线程堆栈
bash复制jstack <PID> > thread_dump.log
- 转换线程ID(将top中的16进制ID转为jstack中的10进制)
- 分析热点线程的调用栈
常见原因:
- 未正确处理的空集合迭代
- 递归调用缺少终止条件
- 同步锁竞争导致的忙等待
3.2 案例二:内核态CPU占用高
现象特征:
top显示%sy(系统CPU)异常偏高vmstat的cs(上下文切换)数值激增
诊断命令:
bash复制# 查看系统调用统计
sar -c 1 3
# 追踪内核函数
perf record -ag -p <PID>
perf report
典型问题:
- 频繁的进程创建/销毁(如错误的shell脚本循环)
- 不合理的线程池配置
- 大量短连接导致TCP状态切换
4. 实战问题排查流程
4.1 现场信息收集清单
- 系统快照:
bash复制# 保存当前进程列表
ps auxf > ps_$(date +%s).log
# 保存网络连接状态
ss -tulnp > netstat_$(date +%s).log
- 性能数据:
bash复制# 记录30秒的CPU使用趋势
sar -u 1 30 > cpu_usage.log
# 采集火焰图数据(需安装perf)
perf record -F 99 -ag -- sleep 30
4.2 分析方法论
五步定位法:
- 确认是用户态(%us)还是内核态(%sy)问题
- 定位具体进程/线程
- 分析进程的调用栈和资源使用
- 结合日志确认业务场景
- 复现验证修复效果
5. 预防与优化建议
5.1 监控体系建设
基础指标监控:
- CPU使用率(分用户态/内核态)
- 运行队列长度
- 上下文切换频率
- 每个核心的负载情况
推荐工具组合:
- Prometheus + Node Exporter(系统层)
- Arthas/JVM-Profiler(Java应用)
- eBPF工具链(内核级分析)
5.2 编码最佳实践
避免CPU爆满的代码技巧:
- 循环体内必须包含sleep或等待机制
- 使用
ThreadPoolExecutor时合理设置队列容量 - 避免在热路径上进行同步阻塞调用
- 正则表达式注意回溯问题
6. 疑难问题排查记录
最近遇到一个特别棘手的案例:某台服务器每天凌晨固定时间CPU飙升,但白天完全正常。最终发现是crontab中的日志清理脚本有问题:
bash复制# 错误写法(find没有限制深度)
find /app/logs -name "*.log" -exec gzip {} \;
# 正确写法(增加-maxdepth参数)
find /app/logs -maxdepth 3 -name "*.log" -exec gzip {} \;
这个案例给我的教训是:即使是最常见的运维命令,如果不注意细节也可能成为性能杀手。现在我的排查清单里又多了一条——定期检查计划任务脚本的资源使用情况。