MySQL CPU飙到680%:一次「僵尸查询」引发的雪崩

0 阅读5分钟

一、事故现场

下午3点,运维监控大屏突然变红。

[严重告警] MySQL CPU使用率:60%

[严重告警] MySQL CPU使用率:280%

[紧急告警] MySQL CPU使用率:680%

从60%到680%,只用了5-10分钟。业务系统开始出现明显卡顿。

• 用户反馈:页面加载变慢,点击无响应

• 运维反馈:数据库连接堆积,慢SQL告警不断

• 应用日志:大量超时错误,线程池打满

图1:MySQL CPU飙升趋势(60% → 680%,约10分钟)

二、排查思路:先止血,再定位根因

这次CPU飙升和普通的慢查询不一样——增长速度太快,而且越往后越卡。直觉告诉我,这不是单一慢查询的问题。

排查原则:先止血保命,再全面排查,最后聚焦根因。

图2:MySQL CPU飙升排查流程图

2.1 第一步:止血——确认影响范围

① 确认业务影响

先判断是全部业务受影响还是部分接口:

查看当前连接数

mysql -e "SHOW STATUS LIKE 'Threads_connected';"

+-----------------+-------+

| Variable_name | Value |

+-----------------+-------+

| Threads_connected | 487 | <-- 正常值应该在50-100左右

+-----------------+-------+

② 查看是否有大量慢查询

查看慢查询数量

mysql -e "SHOW GLOBAL STATUS LIKE 'Slow_queries';"

+---------------+-------+

| Variable_name | Value |

+---------------+-------+

| Slow_queries | 1247 | <-- 异常偏高

+---------------+-------+

图3:慢SQL堆积趋势(系统越来越卡)

2.2 第二步:系统层排查

系统层资源充足,CPU飙升是MySQL自身造成的。

2.3 第三步:MySQL层深度排查

① 查看当前所有连接

mysql -e "SHOW PROCESSLIST\G"

图4:SHOW PROCESSLIST输出(大量长时间运行查询)

发现异常:

多个查询运行时间超过30分钟,而且State显示各种锁等待。

② 用AI帮忙分析PROCESSLIST

PROCESSLIST有几百行,人工看太费劲。让AI帮忙分析:

【当前症状】

  • MySQL CPU从60%飙升到680%,5-10分钟内

  • Threads_connected: 487(正常50-100)

  • Slow_queries: 1247

  • 多个查询Time超过1800秒,状态为Locked/metadata lock

【请分析】

  1. 这些长时间运行的查询,最可能的原因是什么?

  2. "Locked" 和 "Waiting for table metadata lock" 是什么关系?

  3. CPU飙升和这些锁等待有什么关联?

③ 查看InnoDB状态

mysql -e "SHOW ENGINE INNODB STATUS\G"

关注以下关键信息:

2.4 第四步:抽丝剥茧——定位根因

图5:根因链条——大查询被KILL后的雪崩效应

经过完整的排查,根因链条清晰了:

① 最初的触发点:大查询被KILL但进程未退出

-- 查看是否有过被KILL的查询

SELECT * FROM information_schema.INNODB_TRX WHERE TRX_STATE = 'RUNNING';

-- 查看事务运行时间

SELECT id, time, state, info FROM information_schema.PROCESSLIST WHERE time > 300;

发现:有一个查询运行超过1小时(Time=3600+),这个查询在之前被手动KILL了,但进程清理未完成。

② 连锁反应:工作流阻塞 + 新请求堆积

大查询被KILL后,清理进程仍在消耗CPU。同时:

• 工作流依赖该查询结果,无法继续执行

• 工作流线程被阻塞在表锁

• 新的工作流请求不断堆积

• 数据库连接数不断攀升(487个)

③ 雪崩效应:系统越来越卡

① 大查询被KILL,清理进程占用CPU

② 表锁未释放,工作流无法提交

③ 新请求堆积,连接数暴涨

④ 新请求也变成慢查询

⑤ CPU从60% → 280% → 680%

⑥ 系统越来越卡 → 雪崩

三、解决方案:分步处理

① 第一步:KILL僵尸查询

先清理掉那些长时间运行且无意义的查询:

-- 找出运行超过30分钟的查询

SELECT CONCAT('KILL ', id, ';') FROM information_schema.PROCESSLIST

WHERE Command != 'Binlog Dump' AND Time > 1800;

-- 或者直接KILL特定ID

KILL 15234; -- Time=2847秒的查询

KILL 15235; -- Time=2156秒的查询

KILL 15240; -- Time=1890秒的查询

② 第二步:释放表锁

如果KILL后锁仍未释放,需要强制解锁:

-- 查看当前锁状态

SHOW OPEN TABLES WHERE In_use > 0;

-- 查看元数据锁

SELECT * FROM information_schema.INNODB_METRICS WHERE NAME LIKE 'metadata_lock%';

-- 等待锁超时自动释放

SET GLOBAL innodb_lock_wait_timeout = 50; -- 调整为50秒

③ 第三步:扩容临时应对

如果CPU仍然高,可以临时扩容:

在负载均衡层限制流量

或者临时增加MySQL服务器资源

监控CPU下降情况

④ 第四步:索引优化(根因解决)

找到那条运行1小时的查询,分析为什么会这么慢:

-- 查看慢查询日志

SHOW VARIABLES LIKE 'slow_query_log%';

-- 查看执行计划

EXPLAIN SELECT * FROM order_logs WHERE create_time > '2024-01-01' ...;

可能的问题:

四、效果验证

五、预防措施

六、AI辅助排查prompt模板

遇到MySQL CPU飙升时,这个prompt可以直接用:

【事故描述】

MySQL CPU从正常值飙升到XX%,系统变卡/无响应

【已有的排查信息】

  • SHOW PROCESSLIST结果:[粘贴关键行]

  • SHOW ENGINE INNODB STATUS结果:[粘贴关键段落]

  • Threads_connected: XX

  • Slow_queries: XX

【请分析】

  1. 最可能的3个根因方向

  2. 长时间运行的查询最可能是什么问题?

  3. "Locked"/"metadata lock" 和CPU飙升有什么关系?

  4. 应该优先KILL哪些查询?

七、经验总结

这次MySQL CPU飙升的特殊之处在于——表面上是要优化慢查询,实际上是一个被KILL的查询引发的连锁反应。

这类问题的排查要点:

• 不要只看慢查询数量,要看PROCESSLIST中Time最长的那些

• 长时间运行的查询即使被KILL,清理过程也可能消耗大量资源

• 表锁/元数据锁会阻塞后续请求,形成雪崩效应

• 监控要关注连接数变化趋势,比CPU告警更早发现问题

排查顺序建议:

① SHOW PROCESSLIST → 找Time最长的查询

② SHOW ENGINE INNODB STATUS → 看锁等待情况

③ information_schema表 → 查事务和锁详情

④ KILL僵尸查询 → 止血

⑤ EXPLAIN慢查询 → 找根因

⑥ 加索引/优化SQL → 治本