type=all 表示全表扫描,即查询未使用索引,每次执行需扫描整张表;常见原因包括字段类型不匹配、隐式转换、函数操作、联合索引未满足最左前缀及低选择性导致优化器放弃索引。

EXPLAIN 显示 type=ALL 就是全表扫描,别犹豫
type=ALL 是 MySQL 明确告诉你“没走索引”的信号,不是警告,是确诊。只要看到它,说明这条查询每次执行都要扫完整张表——行数越多,慢得越明显。这不是配置问题,也不是缓存没刷,而是优化器压根没考虑用索引。
常见错误现象:EXPLAIN SELECT * FROM orders WHERE status = 'paid' 返回 type=ALL,哪怕 status 上明明建了索引;EXPLAIN SELECT * FROM users WHERE user_id = '123' 中 user_id 是 BIGINT,但传了字符串,也触发 type=ALL。
- 先确认字段类型:用
SHOW CREATE TABLE table_name看真实定义,别靠印象 - 检查 WHERE 条件是否对索引列做了函数操作,比如
YEAR(create_time)、UPPER(name) - 联合索引必须满足最左前缀,
INDEX(a, b, c)对WHERE b = ?无效 - 隐式转换很隐蔽:数值型字段传字符串、字符串字段传数字,都会让索引“消失”
key 字段为空 ≠ 没建索引,而是优化器主动放弃
key=NULL 不代表索引不存在,只代表这次查询优化器觉得走索引更慢。它背后可能是统计信息过期、数据倾斜,或者索引本身选择性太低。
典型场景:一张千万级订单表,status 只有 3 个值('pending'/'paid'/'failed'),单独为它建索引后,EXPLAIN 仍显示 key=NULL。因为优化器估算出查 'paid' 要返回 30% 以上数据,回表成本高于直接扫表。
- 运行
ANALYZE TABLE orders更新统计信息,让优化器重估代价 - 用
SELECT COUNT(DISTINCT status) / COUNT(*) FROM orders算选择性,低于 0.05 的字段慎单独建索引 - 低选择性字段更适合放在联合索引后位,比如
INDEX(user_id, status),而非独立索引 - 强制走索引(
FORCE INDEX)只是临时手段,掩盖了设计问题,上线前务必验证实际性能
Extra 出现 Using filesort 或 Using temporary 不等于索引失效,但暴露排序/分组瓶颈
Using filesort 表示 MySQL 要额外做排序;Using temporary 表示要建临时表——它们不直接导致 type=ALL,但会显著拖慢查询,尤其在大结果集上。
比如 SELECT * FROM orders WHERE user_id = 123 ORDER BY create_time DESC,即使 user_id 走了索引,若没覆盖 ORDER BY 字段,就会触发 Using filesort。
- ORDER BY 字段必须出现在联合索引最左前缀中,且顺序、方向(ASC/DESC)尽量匹配(MySQL 8.0+ 支持混合方向)
- 避免
SELECT *配合ORDER BY,只查必要字段能减少排序开销 - GROUP BY 同理,优先用索引字段分组;若必须按非索引字段分组,考虑先
CREATE TEMPORARY TABLE加索引再查 -
LIMIT不能拯救没索引的排序:如果rows预估远大于LIMIT值,说明排序成本已失控
rows 值严重失真,说明执行计划不可信
rows 是优化器预估扫描行数,不是真实值。当它和实际返回行数差 10 倍以上,比如预估 100 行,实际查出 10 万行,那整个执行计划就不可信——优化器可能因此选错索引甚至跳过索引。
原因通常是统计信息陈旧(ANALYZE TABLE 没跑)、数据分布极端倾斜(95% 订单状态是 'pending'),或用了分区表但各分区统计未更新。
- 定期在低峰期执行
ANALYZE TABLE,特别是大表数据批量导入后 - 对倾斜字段,可考虑生成列 + 索引,比如
ADD COLUMN status_group TINYINT AS (CASE WHEN status IN (0,1) THEN 0 ELSE 1 END) STORED - 分区表需对每个分区单独
ANALYZE TABLE table_name PARTITION(p0),否则优化器只看全局统计 - 别只看
EXPLAIN,加EXPLAIN FORMAT=JSON查看filtered字段,结合rows * filtered / 100估算真实连接行数
实际调优时最容易被忽略的点:EXPLAIN 看的是预估计划,不是执行快照。它不反映锁等待、磁盘 IO 延迟、并发争抢这些运行时因素。一条 type=range 的查询,在高并发下仍可能因大量回表而卡住。所以看到 type 达标,还得盯紧 rows 和 Extra 组合给出的真实压力信号。











