MySQL CPU飙高时应先执行SHOW FULL PROCESSLIST定位长时间运行且非Sleep状态的复杂SQL,再用EXPLAIN分析执行计划,重点关注type和Extra字段,并结合慢查询日志与锁信息综合排查。

直接看哪个线程在吃CPU
MySQL CPU飙到100%,第一反应不是改配置、不是加索引,而是先揪出“真凶”——到底是哪条SQL或哪个连接在疯狂占CPU。
登录MySQL后立刻执行:SHOW FULL PROCESSLIST;,重点关注三列:TIME(已执行秒数)、STATE(如 sending data、Sorting result、updating)、INFO(实际SQL)。长时间运行 + 非Sleep状态 + SQL明显复杂,基本就是它。
- 更精准的筛选命令:
SELECT id, user, host, db, command, time, state, LEFT(info, 500) AS sql_text FROM information_schema.PROCESSLIST WHERE command != 'Sleep' AND info IS NOT NULL ORDER BY time DESC LIMIT 10; - 别只盯着
TIME,STATE才是关键线索:比如Creating sort index大概率缺排序字段索引,Copying to tmp table说明内存临时表撑爆了,被迫落盘再排序 - 误杀风险:别一看到
time > 60就KILL,有些报表导出或ETL任务本就该跑久一点;先EXPLAIN确认SQL本身是否合理
查慢查询日志,别等重启才开
慢查询日志不是“事后诸葛亮”,而是唯一能回溯过去几小时谁在拖垮CPU的证据。它默认关闭,但完全支持运行时动态开启,不用重启MySQL。
紧急情况下执行:SET GLOBAL slow_query_log = 'ON'; SET GLOBAL long_query_time = 1; SET GLOBAL slow_query_log_file = '/tmp/slow.log';。之后用mysqldumpslow -s t /tmp/slow.log快速看最耗时的前几条。
- 阈值别设成
2就完事:long_query_time单位是秒,可设小数(如0.5),高并发OLTP系统建议从0.3起步,否则大量亚秒级低效查询会被漏掉 - 注意权限:写日志路径需MySQL进程有写权限,
/tmp/最安全;避免写到/var/log/mysql/却忘了SELinux或AppArmor拦截 - 日志开关是会话级还是全局?
SET GLOBAL生效,但MySQL重启后失效——真正要持久化,必须写进my.cnf并重启,这是运维交接最容易漏的点
用EXPLAIN看懂为什么慢,而不是猜
EXPLAIN输出里一行type: ALL或rows远超表总行数,基本等于告诉你是全表扫描在烧CPU。但很多人只扫一眼key列是否为NULL,忽略更致命的信号。
对问题SQL执行:EXPLAIN FORMAT=TRADITIONAL SELECT ...(加FORMAT=TRADITIONAL更清晰),重点盯:
-
type:从好到坏是const→ref→range→index→ALL;出现index也不代表健康——它可能是在遍历整个索引树做排序 -
Extra:含Using filesort或Using temporary是CPU大户;Using index condition是好信号,Using where; Using index才是覆盖索引理想态 - 别只看单表:多表JOIN时,
EXPLAIN顺序决定驱动表,rows乘积暴增往往因驱动表选错,而非缺索引
别忽视锁和事务,它们让CPU空转
CPU高 ≠ 一定在计算。有时候线程卡在等锁,top里mysqld进程CPU高,实际是大量线程在Waiting for table metadata lock或Locked状态白忙活。
查锁最直接:SELECT * FROM information_schema.INNODB_TRX ORDER BY trx_started DESC LIMIT 5; 看trx_state是否长期LOCK WAIT,trx_started时间是否异常久。
- 配合查等待链:
SELECT * FROM information_schema.INNODB_LOCK_WAITS;能直接看到谁在等谁、等什么资源 - 常见陷阱:开发习惯性在应用层开事务不提交,或者用
SELECT ... FOR UPDATE锁住大范围数据后做耗时业务逻辑,导致后续所有相关查询排队 - 临时缓解:
KILL掉trx_state = 'LOCK WAIT'的线程ID,但治标不治本;根因得查应用代码里事务边界是否失控
真正难的不是找到那条慢SQL,而是判断它是偶发负载尖峰、配置水土不服,还是索引设计与业务演进脱节。每次EXPLAIN结果变化、每条被KILL的线程背后,都藏着应用和数据库之间没对齐的契约。











