type=index表示MySQL正在全量扫描索引的叶子节点,而非数据行本身;虽同为O(N)扫描,但因索引顺序存储且体积通常更小,IO开销低于ALL。
Navicat执行计划里 type = index 是什么
它表示 mysql 正在**全量扫描索引的叶子节点**,而不是查数据行本身。和 all(全表扫描)一样,它也要遍历全部索引项,但比 all 略快——因为索引通常比数据行小,且顺序存储,io 更少。
常见于以下场景:
- 查询只用到了索引覆盖的字段(比如
SELECT id FROM users WHERE status = 'active',而id和status都在联合索引里) - 没有合适索引可用,但优化器发现扫一遍索引比扫全表便宜(尤其当索引很窄、表很宽时)
-
ORDER BY字段有索引但不满足最左前缀,或WHERE条件太弱导致回表代价高,优化器放弃使用索引查找,改走索引有序性直接扫
注意:type = index 不等于“用了索引就高效”,它本质仍是 O(N) 扫描,只是换了个更轻的结构扫。如果 rows 值接近表总行数,说明没起到过滤作用,该加等值条件或调整索引。
key 列非空但 type = index 说明什么
说明索引确实被选中了,但不是用来做快速定位(ref 或 range),而是当成一个有序列表来遍历。此时关键要看 Extra 列:
- 出现
Using index:恭喜,这是“覆盖索引”,不用回表,性能尚可 - 出现
Using where; Using index:索引部分过滤,但仍有剩余条件要检查行数据 - 出现
Using index condition:用了 ICP(Index Condition Pushdown),MySQL 5.6+ 支持,在引擎层就做过滤,比老版本省 IO - 没这些提示,只有
Using where:很可能走了索引但最终仍要逐行回表判断,效率可能还不如ALL
别只盯着 key 是否为空——key 有值但 type = index,往往意味着索引设计或查询写法没对上,比如建了 (a,b) 却只查 WHERE b = ?,只能扫索引,无法跳到目标位置。
如何区分 index 扫描和 range 扫描
range 是真正利用索引做范围跳转,只访问符合条件的索引片段;index 是从头到尾扫整个索引。区别核心在 key_len 和 rows:
-
key_len明显小于索引总长度(比如索引是(user_id, created_at)共 12 字节,key_len = 4),说明只用了第一个字段,大概率是range或ref -
key_len等于索引全长,且rows接近表总行数 → 几乎肯定是index扫描 -
Extra里有没有Using index:有,说明走的是覆盖索引路径;没有,基本就是扫完再过滤
典型误判点:写成 WHERE date_col > '2023-01-01' OR status = 'done' 这种 OR 条件,即使两个字段都有索引,MySQL 也常退化为 index 或 ALL,因为无法合并索引扫描。
type = index 时要不要优化
要看实际 rows 和业务吞吐。如果表只有几千行,type = index 可能完全没问题;但若 rows 超过 10 万,就得动手了:
- 检查
WHERE条件是否漏了最左字段(比如索引是(org_id, status, created_at),但查询只写了WHERE status = ?) - 确认是否真需要查所有匹配行——加
LIMIT有时能让优化器转向range - 避免在索引字段上用函数:
WHERE YEAR(created_at) = 2023会强制index扫描;改成created_at >= '2023-01-01' AND created_at 才可能触发 <code>range - 如果查询固定返回少量字段,把它们全包含进联合索引,形成覆盖索引,让
Using index稳定出现
最隐蔽的问题是:你以为加了索引就万事大吉,结果执行计划里 type = index + rows = 500000,其实和全表扫描耗时差不多——这时候看 key_len 和 Extra 比看 key 是否为空重要得多。











