type=ALL表示全表扫描,性能最差;type=index是全索引扫描,略优于ALL;type=ref用于非唯一索引等值匹配,可能返回多行;type=eq_ref用于唯一索引等值匹配,每次最多返回1行;type=range用于索引范围扫描,如BETWEEN、>、IN等。

type=ALL 表示全表扫描,性能最差但有时不可避免
当 EXPLAIN 输出中 type 为 ALL,说明 MySQL 正在逐行读取整张表来匹配条件——没有用上任何索引。这在小表或 SELECT * + 无 WHERE 的场景下常见,但数据量稍大(比如 >1 万行)就会明显拖慢查询。
容易踩的坑:
- 明明建了索引,但字段类型不一致(如
user_id是VARCHAR,而WHERE里写成数字WHERE user_id = 123),触发隐式转换导致索引失效 - 使用了函数或表达式:
WHERE YEAR(created_at) = 2024,无法走created_at索引 - 联合索引顺序错位:对
(a,b,c)建了索引,却只查WHERE c = ?,无法命中
type=index 和 type=ALL 的区别在于“是否按索引顺序遍历”
type=index 不是“用了索引”,而是“全索引扫描”:MySQL 会遍历整个索引的 B+ 树叶子节点,而不是聚簇索引的数据页。它比 ALL 略快(尤其当索引比表小很多时),但仍是 O(n) 复杂度。
典型场景:
-
SELECT count(*) FROM t(无WHERE),且存在非空索引(如主键) -
SELECT a FROM t ORDER BY a,而a上有索引但没WHERE条件 - 覆盖索引查询中,MySQL 决定扫完整个索引而非回表(比如
SELECT a,b FROM t WHERE a > 10,(a,b)是联合索引)
注意:type=index 并不等于“高效”,它只是比 ALL 少读部分数据页;如果索引本身很大(例如长文本前缀索引),性能可能和 ALL 接近。
type=ref / eq_ref 的核心差异在“是否能唯一确定一行”
ref 表示使用了非唯一索引(或唯一索引的非前导列),返回多行可能;eq_ref 则表示使用了唯一索引(主键或 UNIQUE),且等值匹配,每次最多返回 1 行——这是连接查询中最理想的类型之一。
常见对比:
-
SELECT * FROM orders o JOIN users u ON o.user_id = u.id:若u.id是主键,则对users表是eq_ref;若o.user_id只有普通索引,则对orders表是ref -
WHERE status = 'paid',status是普通索引 →ref;WHERE id = 123,id是主键 →eq_ref(单表查询中少见,多见于 JOIN) -
ref可能因NULL值或范围条件退化为range,而eq_ref几乎只出现在主键/唯一键等值 JOIN 场景
type=range 对应索引上的范围扫描,边界清晰但未必高效
range 表示 MySQL 使用索引定位一个连续区间,比如 BETWEEN、>、IN、LIKE 'abc%'。它比 ref 多读若干索引节点,但仍是“有界扫描”。
关键细节:
-
IN列表过长(如几百项)可能导致优化器放弃走索引,降级为ALL -
LIKE以通配符开头('%abc')无法用索引,不会出现range - 联合索引中,只有最左前缀满足等值时,后续列才能用于
range:对(a,b,c),WHERE a = 1 AND b > 2 AND c = 3中,c实际不参与索引查找(b后已断开最左前缀) - 某些
OR条件会被重写为UNION,各分支单独判断type,整体EXPLAIN可能显示多个range
真正影响性能的往往不是 type 字面值,而是实际扫描的索引行数(rows)和是否回表(Extra 里有没有 Using index)。同一个 range,查 10 行和查 10 万行,代价天壤之别。










