一、紧急定位:找出消耗CPU的MySQL线程
首先要快速找到是哪些SQL或连接在消耗CPU,这一步能帮你快速锁定问题。
1. 登录MySQL,查看当前运行的线程
sql
-- 查看所有活跃线程,按CPU使用时间排序(最耗CPU的排在前面)
SHOW FULL PROCESSLIST;
-- 更详细的查询(推荐),包含执行时间、锁等待等关键信息
SELECT
id,
user,
host,
db,
command,
time,
state,
LEFT(info, 500) AS sql_text, -- 显示SQL语句前500个字符
EXECUTION_TIME,
LOCK_TIME
FROM INFORMATION_SCHEMA.PROCESSLIST
WHERE command != 'Sleep' -- 排除休眠连接
AND info IS NOT NULL -- 排除无SQL的线程
ORDER BY time DESC; -- 按执行时间降序
关键字段解读:
time:SQL执行的秒数(长时间运行的SQL优先排查)state:线程状态(如Sending data、Sorting result、Updating等,代表SQL执行阶段)sql_text:具体执行的SQL语句(核心排查对象)
2. 查看MySQL全局状态和CPU相关指标
sql
-- 查看MySQL自启动以来的统计信息(重点关注CPU相关)
SHOW GLOBAL STATUS LIKE '%CPU%';
SHOW GLOBAL STATUS LIKE 'Threads%'; -- 线程数(过多会导致CPU高)
SHOW GLOBAL STATUS LIKE 'Queries'; -- 总查询数
SHOW GLOBAL STATUS LIKE 'Slow_queries'; -- 慢查询数
3. 临时处理:终止耗CPU的线程(紧急情况)
如果发现某个SQL长时间运行且消耗大量CPU,可临时终止:
sql
-- 替换为实际的线程ID(从PROCESSLIST中获取)
KILL [线程ID];
二、深入分析:找出CPU高的根本原因
定位到耗CPU的SQL后,需要分析为什么这些SQL会消耗大量CPU,常见原因包括:索引缺失、SQL写法差、配置不合理、锁竞争等。
1. 检查慢查询日志(最核心的排查手段)
慢查询日志会记录执行时间超过long_query_time的SQL,是排查CPU高的核心工具。
(1)开启慢查询日志(临时生效,无需重启)
sql
-- 设置慢查询阈值(如1秒,执行超过1秒的SQL会被记录)
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 1;
-- 记录未使用索引的SQL(即使执行时间短,无索引也可能导致CPU高)
SET GLOBAL log_queries_not_using_indexes = ON;
-- 查看慢查询日志路径
SHOW VARIABLES LIKE 'slow_query_log_file';
(2)分析慢查询日志
使用mysqldumpslow工具(MySQL自带)分析慢查询日志:
bash
# 查看慢查询日志路径
mysql -uroot -p -e "SHOW VARIABLES LIKE 'slow_query_log_file'"
# 分析慢查询日志(替换为实际路径)
mysqldumpslow -s t -t 10 /var/lib/mysql/xxx-slow.log
参数说明:
-s t:按执行时间排序-t 10:显示前10条最慢的SQL-s c:按执行次数排序(高频低耗的SQL也可能导致CPU高)
2. 分析耗CPU SQL的执行计划
对找到的耗CPU SQL,使用EXPLAIN分析执行计划,看是否使用了索引、是否全表扫描:
sql
-- 替换为实际的耗CPU SQL
EXPLAIN SELECT * FROM table_name WHERE condition;
-- 更详细的执行计划(包含执行成本)
EXPLAIN ANALYZE SELECT * FROM table_name WHERE condition;
重点关注:
type列:如果是ALL(全表扫描),说明未使用索引,是CPU高的常见原因key列:显示使用的索引,如果为NULL,说明未使用索引rows列:预估扫描的行数,行数越多,CPU消耗越大
3. 检查MySQL配置是否合理
不合理的配置也会导致CPU高,重点检查以下参数:
sql
SHOW VARIABLES LIKE 'innodb_buffer_pool_size'; -- 缓冲池大小(过小会导致磁盘IO高,间接引发CPU高)
SHOW VARIABLES LIKE 'max_connections'; -- 最大连接数(过多会导致线程切换消耗CPU)
SHOW VARIABLES LIKE 'query_cache%'; -- 查询缓存(MySQL8.0已移除,开启后可能导致CPU高)
SHOW VARIABLES LIKE 'sort_buffer_size'; -- 排序缓冲区(过大导致内存不足,频繁换页)
SHOW VARIABLES LIKE 'join_buffer_size'; -- 连接缓冲区(同上)
4. 检查系统层面的资源竞争
-
CPU上下文切换 :MySQL线程过多会导致CPU频繁切换上下文,消耗CPU
bash# 查看上下文切换次数(数值过高说明有问题) vmstat 1 # 查看MySQL进程的线程数 pstree -p 1972827 | wc -l -
磁盘IO :磁盘IO高会导致MySQL等待,间接拉高CPU(如swap使用、磁盘满)
bash# 查看磁盘IO iostat -x 1 # 查看swap使用情况 free -h
三、常见原因及解决方案
| 常见原因 | 解决方案 |
|---|---|
| 无索引/索引失效 | 为查询字段添加合适的索引;检查索引是否因数据类型不匹配、函数操作失效 |
| SQL写法差(如SELECT *) | 优化SQL,只查询需要的字段;避免子查询、笛卡尔积;使用JOIN代替子查询 |
| 高频小查询 | 优化SQL执行次数;增加缓存(如Redis);使用批量操作 |
| 连接数过多 | 调整max_connections;排查应用侧是否有连接泄漏;使用连接池 |
| 配置不合理 | 调整innodb_buffer_pool_size(建议为物理内存的50%-70%);关闭查询缓存 |
| 锁竞争(行锁/表锁) | 优化事务,缩短锁持有时间;避免长事务;使用行锁而非表锁 |
| MySQL版本问题 | 升级到稳定版本(如5.7/8.0的最新小版本),修复已知的CPU高bug |
四、长期监控:避免CPU高问题复发
-
开启慢查询日志(永久生效) :
修改
my.cnf/my.ini:inislow_query_log = ON slow_query_log_file = /var/lib/mysql/mysql-slow.log long_query_time = 1 log_queries_not_using_indexes = ON重启MySQL生效:
systemctl restart mysqld -
使用监控工具:
- Percona Toolkit:
pt-query-digest分析慢查询日志,pt-stalk捕获MySQL性能数据 - Prometheus + Grafana:监控MySQL CPU、内存、QPS、慢查询等指标
- MySQL自带工具:
mysqladmin status、SHOW ENGINE INNODB STATUS
- Percona Toolkit:
-
定期优化:
- 定期分析慢查询日志,优化SQL和索引
- 定期维护表(
ANALYZE TABLE更新统计信息,OPTIMIZE TABLE整理碎片) - 监控数据库增长,及时扩容或分库分表
总结
- 紧急排查 :通过
SHOW FULL PROCESSLIST定位耗CPU的MySQL线程和SQL,紧急情况下可KILL长时间运行的线程。 - 核心分析 :开启慢查询日志,用
mysqldumpslow和EXPLAIN分析SQL执行计划,重点排查无索引、全表扫描、高频查询等问题。 - 长期优化:优化SQL和索引、调整MySQL配置、开启监控,避免CPU高问题复发。