面试官:什么是覆盖索引?

MySQL 索引里有一个经常被问到的问题:

明明已经走了索引,为什么还需要回表?

之前讲过,回表就是在二级索引查到主键之后,再拿主键去聚簇索引把整行数据捞回来的那一步。

它本质上是在二级索引中找到主键后,再根据主键到聚簇索引中查询完整记录,因此会增加额外的索引访问和数据页读取成本。

那问题来了------有没有可能走了索引,但不需要回表?

有,这就是覆盖索引。

覆盖索引是什么

覆盖索引,指的是:

查询语句需要的所有字段,都已经在某个索引里找到了,不需要再回聚簇索引拿数据。

一次索引查询就拿到了所有需要的数据,没有二次查找。

为什么能实现?

InnoDB 的二级索引叶子节点,存的不只是索引字段的值,还有对应的主键值。

所以当查询的字段都在这个索引里,这条路径就被"覆盖"了。

举个容易理解的例子。

二级索引像图书馆的目录卡,上面写着书名和对应的书架号(主键)。

聚簇索引像书架上摆好的整本书(整行数据)。

如果目录卡上已经印着作者、出版年份这些你需要的信息,你就不必跑回书架去翻书------目录卡自己就够了。

这就是覆盖索引。

一个实际场景

假设有一张订单表:

sql 复制代码
CREATE TABLE `order` (
  order_id INT PRIMARY KEY,
  user_id INT,
  create_time DATETIME,
  status INT,
  amount DECIMAL(10, 2),
  INDEX idx_user_id (user_id)
);

order_id 是主键,对应聚簇索引。

user_id 是普通索引,叶子节点存的是 (user_id, order_id)。

现在有这么一条查询:

sql 复制代码
SELECT order_id, create_time
FROM `order`
WHERE user_id = 12345;

这条 SQL 需要的是 order_id 和 create_time 两个字段。

如果走 idx_user_id 索引,二级索引叶子节点里虽然有 user_id 和 order_id,但 没有 create_time。

拿到 order_id 之后,还得拿着它去聚簇索引把 create_time 捞回来。

这中间,就多了一次回表。

怎么改成覆盖

思路很直接:

让索引自己把结果给全,不需要再去聚簇索引里捞。

怎么做到?

在 user_id 上加一个复合索引,把 create_time 也包含进去:

sql 复制代码
ALTER TABLE `order` ADD INDEX idx_user_time (user_id, create_time);

加了索引之后,查询走 idx_user_time 这棵 B+ 树,叶子节点保存了 user_id 和 create_time,同时 InnoDB 的二级索引叶子节点还会保存对应的主键值 order_id。

查到 user_id = 12345 时,直接返回 order_id 和 create_time,不需要再回聚簇索引。

这就是覆盖索引的优化效果:

查询所需的数据都能直接从二级索引中获取,不需要再回表。

覆盖索引的边界

不是所有查询都能被覆盖。

有几个判断标准,值得在心里过一遍。

查询的字段必须全在索引里。

如果 SQL 里还选了个 status 字段,而索引里没有 status,那还是得回表。

覆盖是有条件的,不能无限扩大。

索引要贴合查询需求。

在满足查询条件的基础上,如果查询结果、排序或过滤所需的字段能够合理地包含在索引中,就有机会减少回表、排序等额外操作。

多列索引会占用更多存储空间,写入时也要维护索引,成本会上升。

因此,要结合查询收益、写入压力和索引大小综合判断,别为了覆盖而乱加索引。

最后串一下

覆盖索引的核心,就是"索引自己能把结果给全,不用再去聚簇索引里捞数据"。

它的前提是:

查询所需的字段都能够从索引中获取。

对于 InnoDB 的二级索引来说,主键值本身也会存储在二级索引叶子节点中,因此查询主键通常也可以被覆盖。

覆盖索引减少了从二级索引回到聚簇索引的访问,因此通常可以减少额外的页访问和 CPU 开销。

建索引不是越多越好,覆盖索引只是优化手段之一。

相关推荐
创新技术阁5 小时前
FastapiAdmin代码生成详细解释
前端·后端·fastapi
杨利杰YJlio5 小时前
ITSK 万能驱动 26V5:新驱动批量更新
前端·javascript·后端
imDwAaY5 小时前
6篇文章讲清楚Git:Git 团队开发实践:从 Pull Request 到版本发布 (6/6)
git·后端
DevUp5 小时前
老站往哪搬:织梦、帝国、PHPCMS 的出路
后端·php·cms
王中阳Go5 小时前
甲骨文裁3万、DeepSeek却招150人:后端程序员往哪走,我把这批JD拆了一遍
后端
打工仔折腾 AI5 小时前
蓝耘元生代实测:用WorkBuddy与TextIn xParse拆解43页建模论文
人工智能·后端·python·数学建模·langchain·ai agent 实战
隔窗听雨眠5 小时前
FunProxy用Rust构建跨平台全链路测试抓包代理工具
开发语言·后端·rust
geovindu5 小时前
rust: search
开发语言·后端·rust
the局外人5 小时前
读懂LangGraph 的分支执行逻辑
后端·langchain·llm
SimonKing5 小时前
Stream-Nexus:我的轻量级推送中间件(sse、websocket)终于定下来了
java·后端·程序员