慢查询往往是首要元凶,尤其当qps不高、内存压力不大时,说明单条sql在反复“烧”cpu;需结合slow_query_log与show processlist定位,用explain分析执行计划,重点优化索引失效、隐式转换、临时表等硬伤。

MySQL CPU 占用 100% 时,**慢查询往往是首要元凶**,尤其当 QPS 并不高、内存压力也不大时——这说明不是业务量压垮了 CPU,而是单条 SQL 在反复“烧” CPU。直接杀进程或重启服务只能缓解几秒,关键得定位并干掉那几条高成本 SQL。
怎么确认是慢查询导致的 CPU 高?
别猜,先看监控趋势:如果 CPU 使用率 曲线和 慢查询数量 曲线明显同步上扬(比如某次发布后慢查激增 5 倍,CPU 紧跟着拉满),基本可锁定。更直接的是检查 long_query_time 设置是否合理——默认 10 秒太宽松,生产环境建议设为 0.5 或 1,否则大量执行 2~3 秒的全表扫描 SQL 根本进不了慢日志。
执行以下命令确认状态:
SHOW VARIABLES LIKE 'slow_query_log';
若返回 OFF,先启用:
SET GLOBAL slow_query_log = ON;
再设阈值(临时生效):
SET GLOBAL long_query_time = 0.5;
注意:long_query_time 是浮点数,不能写成 '0.5' 字符串,否则会静默失败。
如何快速从慢日志里揪出 Top SQL?
慢日志文件路径由 slow_query_log_file 变量决定,通常在 /var/lib/mysql/xxx-slow.log。但别手动翻——用 mysqldumpslow 工具:
mysqldumpslow -s c -t 10 /var/lib/mysql/xxx-slow.log
其中 -s c 按出现次数排序,-t 10 取前 10 条。重点关注:
-
Count高 +Time平均值也高的 SQL —— 高频且低效 -
Rows_examined远大于Rows_sent的 SQL —— 典型的“扫十万行,只返一条” - 含
ORDER BY、GROUP BY、JOIN但没走索引的语句
拿到 SQL 后,用 EXPLAIN 看执行计划,重点盯 type(是否 ALL)、key(是否 NULL)、rows(预估扫描行数)。
show processlist 能看到什么?为什么有时看不到慢 SQL?
SHOW PROCESSLIST 是实时快照,只显示“此刻正在跑”的线程。它对两类场景特别有用:
- SQL 正卡在
Sending data(数据传输中)或Sorting result(排序中)—— 这类状态持续超 5 秒,基本就是 CPU 密集型操作 - 大量线程卡在
Locked或Waiting for table metadata lock—— 可能是 DDL 阻塞了查询,间接推高 CPU
但它**看不到已执行完但耗时很长的慢 SQL**——这些只留在慢日志里。所以必须两者结合:processlist 抓活口,slow log 查历史。
另外,SHOW FULL PROCESSLIST 才能显示完整 SQL(INFO 列不被截断),权限不足时可能看不到其他用户线程,务必用 root 或有 PROCESS 权限的账号执行。
优化时最容易忽略的三个硬伤
很多同学加了索引就以为万事大吉,结果 CPU 还是下不来。真正卡点常在这三处:
-
WHERE条件用了函数,比如WHERE DATE(create_time) = '2026-05-09'—— 索引失效,强制全表扫描 -
JOIN字段类型不一致,比如user_id VARCHAR(32)关联order.user_id BIGINT—— 隐式转换导致索引无法使用 - 临时表撑爆内存,
tmp_table_size和max_heap_table_size过小,导致频繁落磁盘排序,CPU 在反复读写 IO 缓冲区
最后提醒一句:改完 SQL 或索引后,一定要观察 Rows_examined 是否显著下降。如果只是把 10 万行扫描变成 8 万行,CPU 很可能还是 100%——真正的优化目标是让这个数字降到几百甚至个位数。











