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

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_idcreate_time 两个字段。

如果走 idx_user_id 索引,二级索引叶子节点里虽然有 user_idorder_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_idcreate_time,同时 InnoDB 的二级索引叶子节点还会保存对应的主键值 order_id

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

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

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

覆盖索引的边界

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

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

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

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

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

索引要贴合查询需求。

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

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

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

最后串一下

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

它的前提是:

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

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

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

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

相关推荐
BingoGo5 小时前
PHP clone 之后,为什么改副本会影响原对象?
后端·php
JaguarJack5 小时前
PHP clone 之后,为什么改副本会影响原对象?
后端·php·服务端
小灰灰搞电子5 小时前
Rust+Slint 实现动态消息提示框源码分享
开发语言·后端·rust
小奏技术6 小时前
10 MB 的 Postman 替代品,启动不到 1 秒
后端
东风破_6 小时前
Text2SQL :用自然语言操作 SQLite 数据库
人工智能·后端
IT_陈寒10 小时前
Python的多线程就是个假把式,我算是体验到了
前端·人工智能·后端
码事漫谈10 小时前
如果 AI 要圈养人类,它可能不需要笼子
后端
newerp10 小时前
Golang 调度循环:Go runtime 如何永不停歇地找人干活
后端·程序员·go
明月_清风11 小时前
AI 时代,为什么架构师又开始谈"本体论"?
人工智能·后端·agent