一、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飙高问题是运维工作中的常见挑战,快速诊断和解决这类问题需要系统化的方法和丰富的经验。通过本文介绍的工具和技术栈,你可以建立一个完整的诊断流程:

  1. 快速识别问题:使用top/htop等工具
  2. 深入分析:结合jstack/perf等专业工具
  3. 针对性解决:根据问题类型采取不同措施
  4. 预防为主:建立监控和性能测试体系

记住,预防胜于治疗。一个完善的监控系统可以让你在用户发现问题之前就察觉到异常。同时,定期的性能测试和代码审查也能大大降低生产环境出现CPU飙高的风险。

最后,建议运维团队建立自己的诊断手册,记录常见问题的解决方案,这样当下次CPU飙高时,就能更快地找到问题所在并解决它。