一、CPU飙高问题的常见表现
当服务器CPU使用率突然飙升时,通常会有一些明显的症状。首先,你会发现系统响应变得异常缓慢,就像老牛拉破车一样。命令行操作会有明显延迟,甚至简单的ls命令都要等上好几秒。其次,监控系统会发出告警,CPU使用率曲线图上会出现一个明显的"山峰"。
我曾经遇到过这样一个案例:某电商网站在大促期间突然变得卡顿,页面加载需要10多秒。登录服务器一看,CPU使用率已经达到98%,8核CPU全部跑满。用户投诉像雪花一样飞来,情况十分紧急。
二、快速诊断的基本步骤
1. 快速登录问题服务器
当CPU飙高时,首先要做的是登录服务器。这里有个小技巧:如果SSH连接很慢,可以尝试使用ssh -T user@host来禁用伪终端分配,这样连接会快一些。
# 使用以下命令快速登录服务器(Linux示例)
ssh -T root@192.168.1.100
2. 查看整体CPU使用情况
登录后,立即使用top命令查看整体CPU使用情况。重点关注以下几点:
- 平均负载(load average)
- 各个CPU核心的使用率
- 占用CPU最高的进程
# 查看CPU整体使用情况
top -c
# 输出示例:
# top - 14:30:45 up 30 days, 2:15, 2 users, load average: 8.23, 7.89, 6.45
# Tasks: 215 total, 2 running, 213 sleeping, 0 stopped, 0 zombie
# %Cpu0 : 98.3 us, 1.7 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
3. 识别问题进程
使用ps命令结合排序,快速找出CPU占用最高的进程:
# 按CPU使用率排序显示进程(Linux示例)
ps -eo pid,ppid,cmd,%cpu,%mem --sort=-%cpu | head -n 10
# 输出示例:
# PID PPID CMD %CPU %MEM
# 1234 1 /usr/bin/java -Xmx4g 98.5 25.3
# 5678 1234 /usr/bin/python3 app.py 45.2 12.1
三、深入分析CPU使用情况
1. 使用htop进行更直观的查看
htop比top更直观,可以彩色显示CPU使用情况,还支持鼠标操作:
# 安装htop(如果尚未安装)
yum install htop -y # CentOS/RHEL
apt-get install htop # Ubuntu/Debian
# 运行htop
htop
2. 分析Java应用的CPU问题
如果是Java应用导致CPU飙高,可以使用jstack获取线程堆栈:
# 首先找到Java进程ID
jps -l
# 然后获取线程堆栈
jstack -l <pid> > thread_dump.log
# 也可以使用jstat查看GC情况
jstat -gcutil <pid> 1000 10
3. 使用perf进行性能分析
对于更深入的性能分析,Linux的perf工具非常强大:
# 记录CPU使用情况
perf record -F 99 -ag -p <pid> -- sleep 30
# 生成报告
perf report -n --stdio
四、常见问题场景及解决方案
1. 无限循环或递归
这是最常见的CPU飙高原因之一。比如下面这个有问题的Python代码:
# 有问题的递归函数示例
def factorial(n):
# 缺少终止条件,会导致无限递归
return n * factorial(n-1)
# 正确写法应该加上终止条件
def factorial(n):
if n == 1:
return 1
return n * factorial(n-1)
2. 数据库查询未使用索引
慢SQL查询是另一个常见原因。比如下面这个MySQL查询:
-- 没有使用索引的查询
SELECT * FROM users WHERE username LIKE '%john%';
-- 应该为username字段添加索引
ALTER TABLE users ADD INDEX idx_username (username);
-- 优化后的查询
SELECT * FROM users WHERE username LIKE 'john%';
3. 线程死锁
线程死锁虽然通常会导致程序挂起,但有时也会表现为CPU飙高。下面是一个Java死锁示例:
// 死锁示例代码
public class DeadlockDemo {
private static Object lock1 = new Object();
private static Object lock2 = new Object();
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
synchronized (lock1) {
try { Thread.sleep(100); }
catch (InterruptedException e) {}
synchronized (lock2) {
System.out.println("Thread1 got both locks");
}
}
});
Thread thread2 = new Thread(() -> {
synchronized (lock2) {
try { Thread.sleep(100); }
catch (InterruptedException e) {}
synchronized (lock1) {
System.out.println("Thread2 got both locks");
}
}
});
thread1.start();
thread2.start();
}
}
五、预防CPU飙高的最佳实践
1. 实施完善的监控系统
部署像Prometheus+Grafana这样的监控系统,设置合理的告警阈值:
# Prometheus告警规则示例
groups:
- name: cpu-usage
rules:
- alert: HighCPUUsage
expr: 100 - (avg by(instance) (irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 85
for: 5m
labels:
severity: warning
annotations:
summary: "High CPU usage on {{ $labels.instance }}"
description: "CPU usage is {{ $value }}% on {{ $labels.instance }}"
2. 定期进行性能测试
使用JMeter等工具进行压力测试,提前发现性能瓶颈:
# 运行JMeter测试
jmeter -n -t test_plan.jmx -l result.jtl
3. 代码审查和性能优化
建立代码审查制度,特别注意以下几点:
- 避免不必要的循环
- 合理使用缓存
- 优化数据库查询
- 限制资源使用
六、总结与建议
CPU飙高问题是运维工作中的常见挑战,快速诊断和解决这类问题需要系统化的方法和丰富的经验。通过本文介绍的工具和技术栈,你可以建立一个完整的诊断流程:
- 快速识别问题:使用top/htop等工具
- 深入分析:结合jstack/perf等专业工具
- 针对性解决:根据问题类型采取不同措施
- 预防为主:建立监控和性能测试体系
记住,预防胜于治疗。一个完善的监控系统可以让你在用户发现问题之前就察觉到异常。同时,定期的性能测试和代码审查也能大大降低生产环境出现CPU飙高的风险。
最后,建议运维团队建立自己的诊断手册,记录常见问题的解决方案,这样当下次CPU飙高时,就能更快地找到问题所在并解决它。
评论