索引覆盖指查询所需所有列均被索引包含,无需回表;回表是通过二级索引获取主键后再查聚簇索引,引发多次随机i/o。判断依据:explain中extra出现using index即为覆盖,否则大概率回表。

MySQL 索引覆盖(Covering Index)和回表(Bookmark Lookup / Table Lookup)是面试中高频出现的执行计划优化类问题,核心在于理解“是否需要回到主键索引取数据”——这直接决定查询效率。
什么是索引覆盖?
当一条 SELECT 语句所需的所有列,全部被某个索引的字段完全包含(包括 WHERE、ORDER BY、GROUP BY、SELECT 列),且该索引能支撑查询条件和排序需求时,MySQL 就可以只扫描该索引(B+ 树叶子节点),无需访问聚簇索引(即主键索引)的行记录。这个索引就叫“覆盖索引”。
例如:
- 表
t(id PK, a, b, c),有联合索引idx_a_b_c(a,b,c) -
SELECT a,b FROM t WHERE a = 1 AND b > 2;→ 覆盖:WHERE 条件和 SELECT 字段都在索引中 -
SELECT a,b,c FROM t WHERE a = 1 ORDER BY b;→ 覆盖:WHERE + ORDER BY + SELECT 全部命中索引 -
SELECT a,b,id FROM t WHERE a = 1;→ 不覆盖:id 是主键,但未包含在idx_a_b_c中(除非显式把 id 加进索引)
什么是回表?为什么它慢?
回表指:MySQL 通过二级索引(如普通索引、联合索引)快速定位到满足 WHERE 条件的主键值后,还需拿着这些主键值,再回到聚簇索引(主键 B+ 树)中逐条查找完整行数据,才能返回 SELECT 的其他列。
回表代价高,因为:
- 二级索引叶子存的是(索引列值, 主键值),聚簇索引叶子存的是整行数据
- 一次回表 = 一次随机 I/O(主键通常无序,导致磁盘寻道)
- 若查出 1000 行,可能触发 1000 次回表,I/O 放大严重
如何判断是否发生回表?看执行计划 key 和 Extra
关键看 EXPLAIN 输出中的两个字段:
- key:实际使用的索引名 → 确认走的是哪个二级索引
-
Extra:若出现
Using index,说明走了覆盖索引;若只有Using where或Using index condition但没Using index,大概率要回表
注意:Using index condition(ICP)是“索引条件下推”,它优化了 WHERE 过滤位置(下推到存储引擎层),但不等于覆盖;只有同时出现 Using index 才代表真正覆盖。
实战优化建议
让查询走覆盖索引,本质是“把 SELECT 和 WHERE 需要的列,尽量打包进一个联合索引”:
- 把高频查询的 SELECT 列,按区分度从高到低,追加到 WHERE 条件索引末尾(遵循最左前缀+覆盖原则)
- 避免
SELECT *,尤其在有二级索引的场景下——它几乎必然触发回表 - 如需排序或分组,把
ORDER BY或GROUP BY列也纳入索引,避免额外排序(filesort) - 注意索引长度:过宽的覆盖索引会增大索引体积、降低缓存效率,权衡字段必要性
不复杂但容易忽略。










