MySQL CPU 飙高如何定位并解决?面试必问排查思路

CPU 飙高是 MySQL 运维和面试中的高频问题。很多人第一反应是“加索引”,但真实场景远没有这么简单。本文从面试实战角度出发,给出一套可落地的排查路径和解决方案。

一、先确认:真的是 MySQL 导致的吗?

别急着登进数据库。第一步是在操作系统层面确认 CPU 消耗的来源。

top -c

关注几个关键信息:

  • 哪个进程占 CPU 最高:确认是 mysqld 还是其他进程(比如备份脚本、爬虫、PHP-FPM)。
  • us 高还是 sy 高us(用户态)高通常是 SQL 执行消耗;sy(内核态)高可能是锁竞争、上下文切换频繁。
  • 负载与 CPU 核数关系load average 持续超过核数,说明有大量线程在排队。

如果确认是 mysqld 占用过高,再进入数据库内部排查。

二、定位:是哪些 SQL 在消耗 CPU

2.1 查看当前正在执行的线程

SHOW PROCESSLIST;
-- 或
SELECT * FROM information_schema.PROCESSLIST 
WHERE COMMAND != 'Sleep' ORDER BY TIME DESC;

重点看 StateInfo 字段。常见的高 CPU 状态包括:

  • Sending data:大量数据扫描或返回
  • Sorting result:排序操作消耗大
  • Copying to tmp table:临时表操作
  • Creating sort index:索引排序

2.2 开启慢查询日志

如果问题不是持续性的,而是间歇性飙高,慢查询日志是首选工具:

SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 1;
SET GLOBAL log_queries_not_using_indexes = ON;

mysqldumpslowpt-query-digest 分析:

pt-query-digest /var/log/mysql/slow.log

2.3 使用 performance_schema 精准定位

MySQL 5.7+ 推荐用 performance_schema 做实时分析:

-- 按总延迟排序,找出最耗 CPU 的 SQL 模板
SELECT DIGEST_TEXT, COUNT_STAR, 
       SUM_TIMER_WAIT/1000000000000 AS total_sec,
       AVG_TIMER_WAIT/1000000000 AS avg_ms
FROM performance_schema.events_statements_summary_by_digest
ORDER BY SUM_TIMER_WAIT DESC LIMIT 10;

这个查询能直接告诉你:哪类 SQL 模板累计消耗时间最多,是定位问题的核心手段。

2.4 查看当前线程的 CPU 消耗

SELECT thread_id, thread_os_id 
FROM performance_schema.threads 
WHERE processlist_id = <连接ID>;

拿到 thread_os_id 后,在操作系统层面用 top -H -p <mysqld_pid> 找到对应线程,再用 perf topgdb 进一步分析。

三、常见原因分类与解决方案

3.1 缺少索引或索引失效

表现:全表扫描,Handler_read_rnd_next 飙升。

排查

EXPLAIN SELECT ...;
SHOW STATUS LIKE 'Handler_read%';

解决:为 WHERE、JOIN、ORDER BY 涉及的列添加合适索引。注意避免索引失效场景:隐式类型转换、函数操作列、前导模糊匹配。

3.2 锁竞争严重

表现:大量线程处于 Waiting for table metadata lockWaiting for row lock,CPU 的 sy 占比高。

排查

SELECT * FROM performance_schema.data_locks;
SELECT * FROM performance_schema.data_lock_waits;
-- MySQL 5.7 及以下
SELECT * FROM information_schema.INNODB_LOCKS;
SELECT * FROM information_schema.INNODB_LOCK_WAITS;

解决:缩短事务,避免大事务;DDL 操作尽量在低峰期执行;必要时 kill 掉阻塞源。

3.3 全表扫描 + 大结果集排序

表现Sorting result 状态多,临时表落盘。

排查

SHOW STATUS LIKE 'Created_tmp%';
SHOW STATUS LIKE 'Sort%';

解决:用索引覆盖排序,减少 filesort;分页查询避免大 OFFSET;必要时在应用层做排序。

3.4 高并发短连接

表现Threads_created 快速增长,CPU 消耗在连接建立和销毁上。

排查

SHOW STATUS LIKE 'Threads_%';
SHOW VARIABLES LIKE 'thread_cache_size';

解决:使用连接池;适当增大 thread_cache_size;排查是否有连接泄漏。

3.5 慢 SQL 批量并发

表现:某条 SQL 单次执行不慢,但 QPS 极高,累计 CPU 消耗大。

排查events_statements_summary_by_digestCOUNT_STAR 高但 AVG_TIMER_WAIT 低。

解决:在应用层加缓存(Redis);合并请求;限流。

四、应急处理手段

当 CPU 已经打满、业务受影响时,先止血再根治:

  1. Kill 掉问题线程

    KILL <thread_id>;
    

    注意:kill 事务会回滚,大事务回滚可能更慢,需评估。

  2. 临时限流:在代理层(如 ProxySQL)限制问题 SQL 的并发。

  3. 降级非核心业务:暂停报表、统计类查询。

  4. 紧急加索引:MySQL 5.6+ 支持 Online DDL,但大表加索引仍需谨慎,建议用 pt-online-schema-changegh-ost

五、面试回答框架

如果面试中被问到这个问题,建议按以下结构回答:

  1. 先分层:OS 层 → MySQL 层 → SQL 层,逐层缩小范围。
  2. 再分类:是 SQL 本身慢、锁竞争、连接风暴,还是硬件瓶颈。
  3. 给工具topSHOW PROCESSLIST、慢查询日志、performance_schema
  4. 给方案:索引优化、SQL 改写、架构调整(缓存/读写分离/分库分表)。
  5. 提应急:kill 线程、限流、降级。

六、总结

MySQL CPU 飙高的排查核心是“先定位、再分类、后解决”。不要一上来就加索引,而是通过 performance_schema 和慢查询日志找到真正的消耗源。常见原因无非是慢 SQL、锁竞争、高并发连接这几类,对应手段也比较成熟。真正体现水平的,是在应急场景下能快速止血,同时在事后能通过架构和规范避免问题复发。

未经允许不得转载:任鹏个人博客 » MySQL CPU 飙高如何定位并解决?面试必问排查思路

赞 (0) 打赏

评论 0

取消
  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏