mysql cpu飙升90%以上源于全表扫描sql,应先通过show processlist查state为sending data/copying to tmp table/sorting result且time长的语句,再用explain确认type=all、key=null、rows远超返回行数或extra含using filesort/temporary,最后按最左前缀等原则建联合索引或改写sql。

MySQL CPU 飙升,90% 以上是某条 SQL 在全表扫描——EXPLAIN 中 type=ALL 或 rows 值远超返回行数,就是明确信号。别急着调参或扩容,先揪出它。
怎么一眼从 SHOW PROCESSLIST 里抓出全表扫描的 SQL
执行 SHOW FULL PROCESSLIST 后,重点盯这三类状态和耗时:
-
State是Sending data且Time持续增长:说明 MySQL 正在遍历大量行,极可能是全表扫描 -
State是Copying to tmp table或Sorting result:没走索引排序/分组,中间结果集膨胀,CPU 和内存双吃紧 -
Info字段里带WHERE条件但没出现ORDER BY字段、GROUP BY字段或JOIN关联字段:大概率这些字段没建索引
注意:FULL 必须加,否则 Info 可能被截断;若连接数太多,用 SELECT * FROM information_schema.PROCESSLIST WHERE COMMAND != 'Sleep' AND TIME > 30 ORDER BY TIME DESC LIMIT 10 更精准。
EXPLAIN 看出全表扫描的硬指标有哪些
EXPLAIN 输出里,只要出现以下任意一项,基本可判定为低效扫描:
-
type列值为ALL(全表)或index(索引全扫):比ALL略好,但仍是灾难 -
key列为NULL:压根没命中任何索引 -
rows值是实际返回行数的 10 倍以上:比如查 5 行却扫了 50 万行,说明索引没过滤掉数据 -
Extra出现Using filesort或Using temporary:排序/分组被迫落地磁盘或内存临时表,CPU 直接拉满
示例:EXPLAIN SELECT * FROM order WHERE status = 'paid' AND created_at > '2024-01-01' 若 key 为 NULL,说明单列 status 或 created_at 索引都无效,必须建联合索引。
建什么索引才能真正干掉全表扫描
不是“有索引就行”,得按查询条件和 SELECT 字段反推索引结构:
- 联合索引顺序必须匹配
WHERE条件的最左前缀:如查询是WHERE a = ? AND b = ? AND c > ?,索引应为(a, b, c),而非(c, b, a) - 范围条件(
>、、<code>BETWEEN)后面的字段无法走索引:所以(a, b, c)中若b是范围查询,c就失效 - 覆盖索引优先:把
SELECT中的字段也塞进索引末尾,避免回表。例如SELECT id, name, email FROM user WHERE dept_id = ?,索引建为(dept_id, name, email),而不是只建(dept_id) - 区分度高的列放左边:性别、状态这类低区分度字段(如只有
'paid'/'pending')放在联合索引第一位,效果极差
别碰 SELECT *:它让覆盖索引几乎不可能,强制回表,放大扫描代价。
为什么加了索引 CPU 还高?这几个坑最常被忽略
索引建了,EXPLAIN 显示用了,但 CPU 没降——问题往往藏在细节里:
- 隐式类型转换:比如字段是
VARCHAR,但查询写成WHERE id = 123(数字),MySQL 自动转类型导致索引失效;必须写成WHERE id = '123' - 在 WHERE 里对字段用函数:
WHERE DATE(created_at) = '2024-01-01'→ 改成WHERE created_at >= '2024-01-01' AND created_at - 统计信息过期:
ANALYZE TABLE没跑过,优化器误判索引效率,选错执行计划 - 索引碎片严重:大表频繁删改后,
OPTIMIZE TABLE或重建索引能恢复性能
最隐蔽的一点:应用层拼 SQL 时,条件字段顺序不固定(比如有时 WHERE a=? AND b=?,有时 WHERE b=? AND a=?),导致联合索引无法复用——得统一条件顺序,或建多个方向的索引。











