覆盖索引能避免回表,是因为查询所需所有字段均可直接从索引叶子节点获取,无需再通过主键回聚簇索引查找整行数据;回表指二级索引查到主键后,需二次B+树搜索获取非索引列,开销大。

覆盖索引能避免回表,是因为它让查询所需的所有字段都直接从索引中获取,无需再回到主键索引(聚簇索引)的叶子节点去查数据行。
什么是回表?
在 InnoDB 中,二级索引的叶子节点只存储索引列 + 对应主键值。当查询语句用到了二级索引,但 SELECT 列或 WHERE 条件中包含非索引列 时,MySQL 就得拿着查到的主键值,再回到聚簇索引里把整行数据捞出来——这个过程就叫“回表”。它多了一次 B+ 树搜索,开销明显。
覆盖索引怎么做到不回表?
只要 SQL 中所有用到的列(包括 SELECT、WHERE、ORDER BY、GROUP BY 涉及的列)都包含在同一个索引里,优化器就会走该索引,并且直接从索引叶子节点返回结果,跳过回表。
- 例如:表有 (id, name, age, city),建联合索引 (city, age, name);执行 SELECT name, age FROM t WHERE city = '北京' —— 所有字段都在索引中,覆盖成立。
- 但如果写成 SELECT name, age, id FROM t WHERE city = '北京',虽然 id 是主键,但未显式包含在索引中(除非你把 id 加进索引),仍可能回表。
- 注意:SELECT * 几乎不可能被覆盖,除非索引包含全部列(不现实,也不推荐)。
如何判断是否用了覆盖索引?
看 EXPLAIN 结果中的 Extra 列:
- 出现 Using index → 覆盖索引生效,没回表。
- 只有 Using where 或 Using index condition,但没有 Using index → 没覆盖,大概率回表了。
- 如果 Extra 是 Using filesort 或 Using temporary,说明排序/分组没走索引,和覆盖无关,但性能也受影响。
设计覆盖索引的小建议
覆盖索引不是越多越好,要权衡维护成本和查询频次:
- 优先为高频、高代价的查询(如大分页、报表统计)设计覆盖索引。
- 把 WHERE 条件列放最左,再按 SELECT 需求补充其他列(顺序影响最左前缀匹配)。
- 避免冗余:如果已有索引 (a,b,c),而新查询只需 (a,b),就不必新建 (a,b) —— 原索引可覆盖。
- 注意索引长度:字符串列尽量加前缀(如 name(20)),防止索引过大拖慢写入和内存占用。










