MySQL 索引覆盖与回表面试考点

4次阅读

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

MySQL 索引覆盖与回表面试考点

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 whereUsing index condition 但没 Using index,大概率要回表

注意:Using index condition(ICP)是“索引条件下推”,它优化了 WHERE 过滤位置(下推到存储引擎层),但不等于覆盖;只有同时出现 Using index 才代表真正覆盖。

实战优化建议

让查询走覆盖索引,本质是“把 SELECT 和 WHERE 需要的列,尽量打包进一个联合索引”:

  • 把高频查询的 SELECT 列,按区分度从高到低,追加到 WHERE 条件索引末尾(遵循最左前缀+覆盖原则)
  • 避免 SELECT *,尤其在有二级索引的场景下——它几乎必然触发回表
  • 如需排序或分组,把 ORDER BYGROUP BY 列也纳入索引,避免额外排序(filesort)
  • 注意索引长度:过宽的覆盖索引会增大索引体积、降低缓存效率,权衡字段必要性

不复杂但容易忽略。

text=ZqhQzanResources