MySQL CPU占用过高排查指南
MySQL CPU 占用过高时,排查具体占用资源的表需结合系统监控、数据库分析工具和 SQL 诊断命令。
🔍 一、快速定位问题根源
确认 MySQL 进程占用 CPU
- 使用
top
或htop
命令查看系统进程,确认是否为mysqld
进程导致 CPU 飙升。 - 若 MySQL 进程持续占用 90% 以上 CPU,需深入分析数据库内部操作。
- 使用
区分负载类型:QPS 激增 vs. 慢查询
- QPS 激增:对比 CPU 曲线与 QPS(每秒查询量)曲线是否同步波动。若同步,说明高并发导致 CPU 压力。
- 计算 QPS:
SHOW GLOBAL STATUS LIKE 'Questions'; -- 获取总查询量 SHOW GLOBAL STATUS LIKE 'Uptime'; -- 获取运行时间(秒) -- QPS = Questions / Uptime
- 计算 QPS:
- 慢查询为主:若 CPU 飙高而 QPS 未明显上升,大概率是慢 SQL 或锁竞争导致。
- QPS 激增:对比 CPU 曲线与 QPS(每秒查询量)曲线是否同步波动。若同步,说明高并发导致 CPU 压力。
⚙️ 二、定位高资源消耗的表
方法 1:实时分析活跃线程
通过 SHOW FULL PROCESSLIST
或系统表查询当前执行的 SQL 及操作的表:
-- 查看所有活跃线程(非 Sleep 状态)
SELECT * FROM information_schema.PROCESSLIST
WHERE COMMAND != 'Sleep' AND TIME > 10 -- 筛选执行时间>10秒的线程
ORDER BY TIME DESC;
- 关键字段:
STATE
:若为Sending data
、Sorting result
或Creating tmp table
,表示可能涉及全表扫描或复杂计算。INFO
:显示正在执行的 SQL,从中提取操作的表名。
方法 2:分析慢查询日志
- 开启慢查询日志:
SET GLOBAL slow_query_log = 'ON'; SET GLOBAL long_query_time = 1; -- 记录超过1秒的查询 SET GLOBAL slow_query_log_file = '/var/log/mysql/slow.log';
- 使用工具分析日志:
- **
pt-query-digest
(Percona Toolkit)**:pt-query-digest /var/log/mysql/slow.log --limit 10 -- 输出消耗最高的前10个查询
- 输出结果关注点:
Table
:被频繁操作的表名。Rows_examined
:扫描行数过大(如百万级)的表。Query_time
:单次执行耗时长的 SQL。
- **
方法 3:通过 Performance Schema 定位表级操作
-- 查看消耗 CPU 最高的 SQL 及其操作的表
SELECT DIGEST_TEXT AS query,SCHEMA_NAME AS db,COUNT_STAR AS exec_count,SUM_TIMER_WAIT/1e9 AS total_time_sec,SUM_ROWS_EXAMINED AS rows_examined
FROM performance_schema.events_statements_summary_by_digest
ORDER BY total_time_sec DESC
LIMIT 10;
- 关键信息:
query
字段可直接看到 SQL 操作的表(如SELECT * FROM orders
)。- exec_count 该SQL模式被执行的次数
- total_time_sec 该SQL模式所有执行的总耗时(单位:皮秒,除以1e9转换为秒;例如:
SUM_TIMER_WAIT=1234567890000
→1.23456789
秒;识别最耗时的SQL模式 rows_examined 该SQL模式所有执行中检查的总行数;例如:
1000000
(表示这个SQL模式总共扫描了100万行),用于识别全表扫描或索引效率低下的查询
方法 4:检查表大小与索引状态
查询表空间占用:
SELECT TABLE_NAME,ROUND((DATA_LENGTH + INDEX_LENGTH)/1024/1024, 2) AS size_mb,TABLE_ROWS FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'your_database' ORDER BY size_mb DESC;
- 大表(GB 级) 更容易因全表扫描导致 CPU 飙升。
- 小表但高扫描频次:可能索引缺失或统计信息过期。
检查索引有效性:
-- 查看表的索引情况 SHOW INDEX FROM your_table;
- 若
Cardinality
(基数)远小于实际行数,说明索引可能失效,需更新统计信息:ANALYZE TABLE your_table;
- 若
🛠️ 三、针对性优化措施
紧急处理:
- 终止高消耗线程:
KILL <thread_id>; -- 从 PROCESSLIST 获取 thread_id
- 终止高消耗线程:
索引优化:
- 为高频查询的
WHERE
、JOIN
、ORDER BY
字段添加索引。 - 避免索引失效:
- 禁止对索引列使用函数(如
WHERE DATE(create_time) = ...
)。 - 避免隐式类型转换(如字符串字段用数字查询)。
- 禁止对索引列使用函数(如
- 为高频查询的
SQL 重写:
- 拆分复杂查询(如将子查询改为 JOIN)。
- 减少
SELECT *
,仅返回必要字段。 - 分页查询优化:用
WHERE id > last_id LIMIT n
替代OFFSET
。
配置调整:
- 增加临时表大小,避免磁盘临时表:
tmp_table_size = 256M max_heap_table_size = 256M
- 调整 InnoDB 缓冲池(通常设为物理内存的 70%):
innodb_buffer_pool_size = 8G
- 增加临时表大小,避免磁盘临时表:
架构扩展:
- 读写分离:将查询分流到只读副本。
- 分库分表:对亿级大表按业务拆分。
📊 排查工具推荐
工具类型 | 推荐工具 | 用途 |
---|---|---|
系统监控 | top , htop , vmstat | 定位进程及线程级 CPU 占用 |
SQL 分析 | pt-query-digest , EXPLAIN | 分析慢查询及执行计划 |
实时诊断 | SHOW PROCESSLIST , sys.schema | 查看活跃线程与资源消耗 |
可视化监控 | Prometheus + Grafana, PMM | 长期追踪性能指标(QPS/CPU/锁) |
⚠️ 注意
- 锁竞争问题:若
SHOW PROCESSLIST
显示大量线程状态为Waiting for table lock
,需检查长事务或死锁(information_schema.INNODB_TRX
)。 - 外部因素:备份任务、批量数据维护也可能导致 CPU 短暂飙高,需结合操作日志排查。