查出哪些 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 爆表”的查询 —— 它们往往缺少合适的复合索引,或事务持有锁时间过长。
