MySQL 的覆盖索引是什么?

面试考点分析:

  • 是否理解覆盖索引的定义和与普通索引的区别
  • 能否说出覆盖索引的工作原理(避免回表)
  • 是否知道覆盖索引在真实开发中的优化场景
  • 是否了解联合索引中最左匹配原则对覆盖索引的影响
  • 能否结合执行计划 Extra 列(Using index)判断覆盖索引的使用情况

一、标准回答

覆盖索引(Covering Index)是指索引中已经包含了查询所需的所有字段,MySQL 只需要通过索引信息就能直接返回查询结果,而无需回表查询主键索引(聚簇索引)中的完整数据行。

其核心作用是:

  • 减少磁盘I/O: 避免随机 I/O 去读取数据行,只读取索引结构的有序数据。
  • 提升查询性能: 特别是在宽表(字段很多)中,回表数据量大的场景下效果显著。
  • 优化排序和分组: 如果索引覆盖且满足最左匹配,可以利用索引的有序性避免额外排序。

在 MySQL 中可以通过 EXPLAIN 观察到 Extra 列出现 Using index 时,即为使用了覆盖索引。

二、核心原理

当执行一条 SQL 查询时:

  1. SELECT * 或非索引字段: 需要先扫描二级索引找到主键,然后到聚簇索引中查找完整行数据(随机磁盘 I/O)。
  2. SELECT 被索引覆盖的字段: 直接从二级索引的叶子节点就能拿到所有需要的值,无需回表。

以表 user(id, name, age, city),建立联合索引 idx_name_age(name, age) 为例:

sql 复制代码
-- 该查询可以走覆盖索引,因为查询字段 name,age 均在索引中
EXPLAIN SELECT name, age FROM user WHERE name = 'Jack';

结果中 Extra: Using index 即表示覆盖索引生效。

三、应用场景

  • 高并发查询的热点 SQL: 比如订单列表查询只需展示订单号、状态、金额,将这些字段建立联合索引,减少 IO 争用。
  • 分页查询优化: 配合延迟关联技巧,例如 SELECT ... FROM t1 INNER JOIN (SELECT id FROM t1 LIMIT 10000,10) AS tmp USING(id),内部查询使用覆盖索引。
  • 统计查询: 如 SELECT COUNT(*),若存在覆盖索引则直接统计索引记录数(基于二级索引比聚簇索引小)。
  • 避免排序开销: 如 ORDER BY create_time LIMIT 100,若索引为 idx_create_time(create_time),且 SELECT 只取索引包含字段,则无需额外排序。

四、使用方式(Java 示例)

通过 Spring Boot + MyBatis 演示,首先建立表结构:

sql 复制代码
CREATE TABLE product (
  id BIGINT AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(200),
  category VARCHAR(100),
  price DECIMAL(10,2),
  stock INT,
  KEY idx_category_price (category, price)
);

Mapper 接口定义:

java 复制代码
@Mapper
public interface ProductMapper {
    /**
     * 只查询 category 和 price,MySQL 优化器会选择覆盖索引
     */
    @Select("SELECT category, price FROM product WHERE category = #{cat}")
    List<ProductDTO> listByCategory(@Param("cat") String category);
}

执行流程:

  1. SQL 传入 category = '电子'。
  2. 查询优化器发现 idx_category_price 的叶子节点已包含 category 和 price,且无需回表。
  3. 直接扫描联合索引的 B+Tree,逐条返回结果。

注意事项:

  • 如果查询中增加了 id 字段(SELECT category, price, id),不影响覆盖索引,因为二级索引叶子节点本身就包含主键值(id),无需回表。
  • 如果查询字段超出索引范围,如增加了 stock(SELECT category, price, stock),则必须回表。
  • 联合索引遵循最左匹配原则,如果 WHERE 条件不包含最左列(category),则无法使用该索引。

五、扩展延伸

对比维度 覆盖索引 非覆盖索引
查询流程 仅扫描索引结构 索引 + 回表
磁盘 I/O 顺序读多 随机读多
适用场景 查询字段少、高频 需要完整行数据

优缺点:

  • 优点: 显著减少 I/O 和 CPU 消耗,提升吞吐量。
  • 缺点: 增加索引维护成本和存储空间;如果索引字段过多,B+Tree 高度可能增加。

实际开发注意事项:

  • 不要为了"覆盖"而把所有字段塞进索引,导致索引过于庞大。
  • 利用 EXPLAIN 确认是否真正使用了覆盖索引,关注 Extra 列。
  • 针对排序 + 分页场景,优先设计覆盖索引。

六、面试追问

面试官追问: 如果我查询的是 SELECT name, age FROM user WHERE age > 25,索引是 idx_name_age(name, age),能不能用到覆盖索引?

回答思路:

  1. 该查询跳过了最左列 name,不符合最左匹配原则,无法使用该联合索引进行范围扫描,更谈不上覆盖索引。
  2. 如果想要覆盖,需要单独建立 idx_age_name(age, name) 或者包含需要字段的索引。
  3. 延伸:如果表数据量极大,可能需要考虑调整索引结构或利用延迟关联。

面试官追问: 覆盖索引是否一定能提升性能?有没有反例?

回答思路:

覆盖索引并非银弹。当索引过大,维护和扫描成本增加;另外,在某些情况下,如果走覆盖索引的全索引扫描比通过普通索引回表(因为数据在缓冲池命中率极高)还慢,需要结合具体场景测试。另外,COUNT(*) 优化器会选择最小的二级索引来优化,但不是所有场景都绝对。

相关推荐
李兆龙的博客3 小时前
问津集 #26:Lakebase——Postgres 的版本化页面存储、数据库分支与计算弹性
数据库
倔强的石头_6 小时前
聊聊金仓KFS:一款把数据同步软件做扎实的产品
数据库
闲云野鹤在人间6 小时前
MySQL|从理论、安装、备份到主从复制、MHA高可用详解
linux·运维·数据库·mysql·云计算
禾小西6 小时前
Redis:从两大维度和三大主线建立知识体系
数据库·redis·缓存
禾小西7 小时前
Redis 数据结构:快速的 Redis 有哪些慢操作?
数据结构·数据库·redis
数据库小学妹7 小时前
数据共享交换平台选型:交换方式对比与避坑指南
数据库·信创·数据同步·数据交换平台·政务数据共享·数据共享交换平台·数据库底座
要开心吖ZSH7 小时前
MySQL 慢 SQL 排查操作手册-个人笔记版
java·笔记·sql·mysql·慢查询
loong_XL9 小时前
决策模型做内容安全检测:从踩坑到上线
数据库·安全·jev·决策模型
这个DBA有点耶9 小时前
数据库双轨并行实战:全量并行策略、增量延迟控制、双向回切与一致性校验
数据库·架构·dba
Gl�ria9 小时前
MySQL 单机版 vs 高可用版:宕机排查 + 故障处理
mysql·adb·架构