MySQL type=ALL / index / ref / eq_ref / range 的实际含义对比

11次阅读

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

MySQL type=ALL / index / ref / eq_ref / range 的实际含义对比

type=ALL 表示全表扫描,性能最差但有时不可避免

EXPLaiN 输出中 typeALL,说明 mysql 正在逐行读取整张表来匹配条件——没有用上任何索引。这在小表或 select * + 无 WHERE 的场景下常见,但数据量稍大(比如 >1 万行)就会明显拖慢查询。

容易踩的坑:

  • 明明建了索引,但字段类型不一致(如 user_idVARCHAR,而 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 是普通索引 → refWHERE id = 123id 是主键 → eq_ref(单表查询中少见,多见于 JOIN)
  • ref 可能因 NULL 值或范围条件退化为 range,而 eq_ref 几乎只出现在主键/唯一键等值 JOIN 场景

type=range 对应索引上的范围扫描,边界清晰但未必高效

range 表示 MySQL 使用索引定位一个连续区间,比如 BETWEEN>INLIKE '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 万行,代价天壤之别。

text=ZqhQzanResources