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 的叶子节点已包含 categoryprice,且无需回表。
  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(*) 优化器会选择最小的二级索引来优化,但不是所有场景都绝对。

相关推荐
程序员-Benothing22 分钟前
MySQL 中的 MVCC 是什么?如果没有 MVCC,会有什么影响?
数据库·mysql
Zenova EdgeOS22 分钟前
工业网关心跳机制:从 Keepalive 到健康判定的工程实战
大数据·网络·数据库·边缘计算·工业网关
疯狂打码的少年26 分钟前
【数据库技术】关系完整性约束(实体/参照/用户定义)
运维·服务器·数据库·笔记
派小汤1 小时前
Harmony2.2.0通过RdbStore实现通用类操作本地SQLite数据库
数据库·sql·sqlite·鸿蒙·鸿蒙系统
程序员-Benothing2 小时前
MySQL 中的事务隔离级别有哪些?默认的事务隔离级别是什么?为什么选择这个级别?
数据库·mysql
lv__pf2 小时前
【TL mysql 3】
数据库
xiaomici3 小时前
Datasphere的数据merge
数据库
小林ixn4 小时前
从零设计一个博客系统的数据库:表结构、索引与约束的实战思考
数据库·后端·mysql
Yang96114 小时前
一台顶三台:鼎讯DLJ-1在不同故障类型中的模式切换数据复盘
服务器·网络·数据库
潘正翔4 小时前
Memcached构建缓存服务器
运维·服务器·数据库·缓存·云原生·memcached