MySQL 高 CPU 终极排查与优化方案

适用场景:MySQL 服务器 CPU 使用率持续 >80%,系统响应变慢,应用超时。
核心原则先定位源头,再分析原因,最后针对性优化


🔍 一、确认是否为 MySQL 进程导致高 CPU

✅ 操作步骤:

# 1. 查看整体 CPU 使用情况
top

# 2. 按 'P'(大写)按 CPU 使用率排序
# 3. 观察是否有 mysqld 进程占用大量 CPU(如 >90%)

💡 若 mysqld 不是主因,则排查其他进程(如备份脚本、监控代理等)。


🕵️ 二、查看当前活跃线程(实时抓取“罪魁祸首”)

✅ 操作命令:

-- 登录 MySQL,执行:
SHOW FULL PROCESSLIST;

🔍 关注字段:

字段 重点关注
Id 线程 ID(可用于 kill)
Time 执行时长(秒),>30 秒需警惕
State 状态(如 Sending data, Sorting result, Copying to tmp table
Info 实际 SQL 语句

🚨 典型高 CPU 状态:

  • Sending data:正在处理大量行(可能全表扫描)
  • Sorting result:内存/磁盘排序
  • Copying to tmp table:使用临时表(常因 GROUP BY / DISTINCT)

⚠️ 快速终止问题会话(谨慎使用):

KILL <thread_id>;  -- 替换为实际 Id

💡 建议连续执行 2~3 次 SHOW FULL PROCESSLIST,观察是否同一类 SQL 反复出现。


📊 三、分析慢查询日志(历史高频问题)

✅ 1. 确认慢查询日志是否开启

SHOW VARIABLES LIKE 'slow_query_log';        -- 应为 ON
SHOW VARIABLES LIKE 'long_query_time';       -- 默认 10 秒,建议设为 1
SHOW VARIABLES LIKE 'slow_query_log_file';   -- 日志路径

✅ 2. 临时开启慢日志(若未开启)

SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 1;  -- 记录 >1 秒的查询

✅ 3. 分析慢日志(使用 mysqldumpslowpt-query-digest

# 方法1:简单统计(按次数排序)
mysqldumpslow -s c /var/log/mysql/slow.log | head -20

# 方法2:专业分析(推荐 Percona Toolkit)
pt-query-digest /var/log/mysql/slow.log > slow_report.txt

🔍 重点关注:

  • 执行次数高(高频小查询累积 CPU)
  • 扫描行数多Rows_examined 大)
  • 未使用索引Rows_sent 小但 Rows_examined 极大)

🔬 四、分析 SQL 执行计划(EXPLAIN)

对可疑 SQL 执行 EXPLAIN

EXPLAIN FORMAT=JSON
SELECT * FROM orders 
WHERE user_id = 12345 
ORDER BY create_time DESC 
LIMIT 10;

✅ 关键检查点:

指标 问题表现 优化方向
type ALL(全表扫描) 加索引
key NULL 检查索引是否可用
rows 数值极大(如 >10万) 优化 WHERE 条件或分页
Extra Using filesort / Using temporary 优化 ORDER BY / GROUP BY

🛠 常见优化手段:

  1. 添加复合索引
    -- 示例:覆盖 WHERE + ORDER BY
    ALTER TABLE orders ADD INDEX idx_user_time (user_id, create_time);
    
  2. 改写分页查询(避免 OFFSET 大)
    -- ❌ 慢:SELECT * FROM t LIMIT 100000, 10;
    -- ✅ 快:SELECT * FROM t WHERE id > 100000 ORDER BY id LIMIT 10;
    
  3. 避免 SELECT *,只取必要字段

⚠️ 五、易忽略的深层原因

1. 连接池配置不合理

  • 现象:大量短连接频繁创建/销毁。
  • 影响:MySQL 线程创建开销 + 上下文切换 → CPU 升高。
  • 排查
    SHOW GLOBAL STATUS LIKE 'Connections';      -- 总连接数
    SHOW GLOBAL STATUS LIKE 'Threads_created';  -- 创建线程数(应远小于 Connections)
    
  • 优化
    • 应用层使用 连接池(如 HikariCP、Druid)
    • 调整 MySQL 参数:
      thread_cache_size = 64  # 缓存空闲线程,减少创建开销
      

2. 长事务堆积

  • 现象:事务长时间未提交,阻塞 purge 线程。
  • 影响
    • undo 日志无法清理
    • MVCC 版本链过长 → 查询需遍历大量历史版本
    • 可能引发锁等待连锁反应
  • 排查
    SELECT * FROM information_schema.innodb_trx 
    ORDER BY trx_started;
    
    • 关注 trx_state = 'RUNNING'trx_started 很早的事务
  • 解决
    • 联系应用团队 及时提交或回滚
    • 设置事务超时(应用层控制)

3. 锁竞争与死锁检测

  • 现象:高并发下频繁死锁。
  • 影响:InnoDB 死锁检测算法复杂度 O(n²),CPU 暴涨。
  • 排查
    SHOW ENGINE INNODB STATUS\G
    
    • 查看 LATEST DETECTED DEADLOCK 部分
  • 优化
    • 减少事务粒度(快进快出)
    • 按固定顺序访问表(避免交叉加锁)
    • 极端情况:临时关闭死锁检测(不推荐生产):
      innodb_deadlock_detect = OFF
      innodb_lock_wait_timeout = 5  # 配合使用
      

🧠 六、本质原因总结

MySQL CPU 高的本质是 做了过多不必要的计算,主要包括:

原因 表现 解决方向
SQL 未走索引 全表扫描 + 排序 + 临时表 加索引、改写 SQL
高频低效查询 慢日志中重复出现的小查询 应用缓存、合并请求
连接风暴 Threads_created 飙升 连接池 + thread_cache_size
长事务/锁竞争 InnoDB trx 堆积、死锁频发 优化事务设计、缩短持有时间

🛠 七、完整排查流程图(推荐顺序)

graph TD
A[CPU 高] --> B{是 mysqld 吗?}
B -- 否 --> C[排查其他进程]
B -- 是 --> D[SHOW FULL PROCESSLIST]
D --> E{有长期运行 SQL?}
E -- 是 --> F[KILL + EXPLAIN 分析]
E -- 否 --> G[检查慢查询日志]
G --> H[pt-query-digest 分析]
H --> I[优化 TOP SQL]
I --> J[检查连接/事务/锁]
J --> K[调整配置或应用逻辑]

✅ 八、附录:关键配置建议

参数 推荐值 说明
long_query_time 1 捕获更多慢 SQL
slow_query_log ON 开启慢日志
thread_cache_size 32~64 减少线程创建开销
innodb_buffer_pool_size 物理内存 70~80% 减少磁盘 IO(间接降 CPU)
max_connections 按需设置 避免连接耗尽

💡 终极建议
80% 的高 CPU 问题可通过“加索引 + 改写 SQL”解决,15% 源于连接/事务设计,5% 为 MySQL 内部 Bug。
坚持“先抓现场,再看日志,最后调优”的原则,避免盲目重启!