优先查 PROCESSLIST 中 TIME>10 且 STATE 为 Copying to tmp table/Sending data 的查询;再启用 slow_query_log 并用 EXPLAIN 分析全表扫描、Using filesort 等高 CPU 操作;最后排查隐式类型转换和函数导致的索引失效。

查出哪些 SQL 正在吃 CPU
MySQL 本身不直接暴露“CPU 占用”到每个查询,但高 CPU 往往对应大量执行、全表扫描、锁等待或排序/分组操作。优先看 SHOW PROCESSLIST 和 information_schema.PROCESSLIST,筛选 Command 为 Query 且 Time 较长的线程:
SELECT ID, USER, HOST, DB, COMMAND, TIME, STATE, INFO FROM information_schema.PROCESSLIST WHERE COMMAND = 'Query' AND TIME > 10 ORDER BY TIME DESC LIMIT 10;
注意:INFO 列可能被截断,可搭配 SHOW FULL PROCESSLIST(需有 PROCESS 权限);若看到大量 Copying to tmp table、Sorting result、Sending data 状态,基本就是 CPU 或内存瓶颈点。
检查慢查询和执行计划
开启并分析 slow_query_log 是最直接的方式。确认是否已启用:
SHOW VARIABLES LIKE 'slow_query_log';
若关闭,临时启用(重启不丢失需写入配置文件):
SET GLOBAL slow_query_log = ON; SET GLOBAL long_query_time = 1; -- 记录超过 1 秒的查询
然后查日志路径:
SHOW VARIABLES LIKE 'slow_query_log_file';
拿到慢 SQL 后,用 EXPLAIN 看执行计划,重点关注:
-
type是ALL(全表扫描)或index(全索引扫描) -
rows预估扫描行数远超实际返回行数 -
Extra出现Using filesort、Using temporary
这类语句极易推高 CPU —— 尤其当 ORDER BY 或 GROUP BY 没走索引时。
识别隐式类型转换和函数索引失效
这类问题不会报错,但会让索引完全失效,导致本该毫秒级的查询变成全表扫描。典型例子:
SELECT * FROM users WHERE mobile = 13800138000; -- mobile 是 VARCHAR,却传了数字
MySQL 会把每行 mobile 转成数字再比对,索引失效。同理:
SELECT * FROM orders WHERE DATE(create_time) = '2024-01-01'; -- DATE() 函数使 create_time 索引失效
应改写为:
SELECT * FROM orders WHERE create_time >= '2024-01-01' AND create_time < '2024-01-02';
其他常见陷阱包括:LIKE '%abc'(左模糊)、OR 连接非同索引字段、对索引字段做运算(如 WHERE id + 1 = 100)。
临时降载和连接数控制
如果 CPU 已飙到 95%+ 且业务卡死,先止损再排查:
- 用
KILL干掉明确的坏查询(如上面查出的长时间Query):KILL 12345; - 限制新连接:设
max_connections临时降低(如从 500 改为 100),避免雪崩:SET GLOBAL max_connections = 100; - 关掉非关键功能:比如停掉监控采集、报表导出等定时任务
- 确认没有应用端连接泄漏 —— 查
Threads_connected是否持续增长却不释放
这些是应急手段,不能替代根因优化。真正麻烦的往往是那些“单条不慢、但并发一高就 CPU 爆表”的查询 —— 它们往往缺少合适的复合索引,或事务持有锁时间过长。










