MySQL 高 CPU 终极排查与优化方案
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. 分析慢日志(使用 mysqldumpslow 或 pt-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 |
🛠 常见优化手段:
- 添加复合索引
-- 示例:覆盖 WHERE + ORDER BY ALTER TABLE orders ADD INDEX idx_user_time (user_id, create_time); - 改写分页查询(避免 OFFSET 大)
-- ❌ 慢:SELECT * FROM t LIMIT 100000, 10; -- ✅ 快:SELECT * FROM t WHERE id > 100000 ORDER BY id LIMIT 10; - 避免 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。
坚持“先抓现场,再看日志,最后调优”的原则,避免盲目重启!
- 感谢你赐予我前进的力量
赞赏者名单
因为你们的支持让我意识到写文章的价值🙏
本文是原创文章,采用 CC BY-NC-ND 4.0 协议,完整转载请注明来自 龙羽

