什么是覆盖索引?如何利用覆盖索引进行SQL语句优化?

本文深入解析数据库索引原理,包括B+树结构、主键与非主键索引的区别,以及如何通过覆盖索引优化查询性能,避免回表操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在这里插入图片描述


1. 什么是索引?

索引(在 MySQL 中也叫“键key”)是存储引擎快速找到记录的一种数据结构,通俗来说类似书本的目录,这个比方虽然被用的最多但是也是最恰如其当的,在查询书本中的某个知识点不借助目录的情况下,往往都找的够呛,那么索引相较于数据库的重要性也可见一斑。

2. 索引的有哪些种类?

索引的种类这里只罗列出InnoDB支持的索引:主键索引(PRIMARY),普通索引(INDEX),唯一索引(UNIQUE),组合索引,总体划分为两类,主键索引也被称为聚簇索引(clustered index),其余都称呼为非主键索引也被称为二级索引(secondary index)。

3. InnoDB的不同的索引组织结构是怎样的呢?

众所周知在InnoDB引用的是B+树索引模型,这里对B+树结构暂时不做过多阐述,很多文章都有描述,在第二问中我们对索引的种类划分为两大类主键索引和非主键索引,那么问题就在于比较两种索引的区别了,我们这里建立一张学生表,其中包含字段id设置主键索引、name设置普通索引、age(无处理),并向数据库中插入4条数据:(“小赵”, 10)(“小王”, 11)(“小李”, 12)(“小陈”, 13)

CREATE TABLE `student` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增主键',
  `name` varchar(32) COLLATE utf8_bin NOT NULL COMMENT '名称',
  `age` int(3) unsigned NOT NULL DEFAULT '1' COMMENT '年龄',
  PRIMARY KEY (`id`),
  KEY `I_name` (`name`)
) ENGINE=InnoDB;

INSERT INTO student (name, age) VALUES("小赵", 10),("小王", 11),("小李", 12),("小陈", 13);

复制代码这里我们设置了主键为自增,那么此时数据库里数据为

每一个索引在 InnoDB 里面对应一棵B+树,那么此时就存着两棵B+树。

img

可以发现区别在与叶子节点中,主键索引存储了整行数据,而非主键索引中存储的值为主键id, 在我们执行如下sql后

SELECT age FROM student WHERE name = '小李';

复制代码流程为:

  1. 在name索引树上找到名称为小李的节点 id为03
  2. 从id索引树上找到id为03的节点 获取所有数据
  3. 从数据中获取字段命为age的值返回 12

在流程中从非主键索引树搜索回到主键索引树搜索的过程称为:回表,在本次查询中因为查询结果只存在主键索引树中,我们必须回表才能查询到结果,那么如何优化这个过程呢?引入正文覆盖索引

4. 什么是覆盖索引?

覆盖索引(covering index ,或称为索引覆盖)即从非主键索引中就能查到的记录,而不需要查询主键索引中的记录,避免了回表的产生减少了树的搜索次数,显著提升性能。

5. 如何使用是覆盖索引?

之前我们已经建立了表student,那么现在出现的业务需求中要求根据名称获取学生的年龄,并且该搜索场景非常频繁,那么先在我们删除掉之前以字段name建立的普通索引,以name和age两个字段建立联合索引,sql命令与建立后的索引树结构如下

ALTER TABLE student DROP INDEX I_name;
ALTER TABLE student ADD INDEX I_name_age(name, age);

复制代码
那在我们再次执行如下sql后

SELECT age FROM student WHERE name = '小李';

复制代码流程为:

  1. 在name,age联合索引树上找到名称为小李的节
  2. 此时节点索引里包含信息age 直接返回 12

6. 如何确定数据库成功使用了覆盖索引呢?

当发起一个索引覆盖查询时,在explain的extra列可以看到using index的信息

这里我们很清楚的看到Extra中Using index表明我们成功使用了覆盖索引

总结:覆盖索引避免了回表现象的产生,从而减少树的搜索次数,显著提升查询性能,所以使用覆盖索引是性能优化的一种手段


真实场景

表结构

img

150多万的数据,这么一个简单的语句:

img

慢查询日志里居然很多用了1秒的,Explain的结果是:

img

从Explain的结果可以看出,查询已经使用了索引,但为什么还这么慢?

分析:首先,该语句ORDER BY 使用了Using filesort文件排序,查询效率低;其次,查询字段不在索引上,没有使用覆盖索引,需要通过索引回表查询;也有数据分布的原因。

知道了原因,那么问题就好解决了。

解决方案:由于只需查询uid字段,添加一个联合索引便可以避免回表和文件排序,利用覆盖索引提升查询速度,同时利用索引完成排序。

img

覆盖索引:SQL只需要通过索引就可以返回查询所需要的数据,而不必通过二级索引查到主键之后再去查询数据。

我们再Explain看一次:

img

Extra信息已经有’Using Index’,表示已经使用了覆盖索引。经过索引优化之后,线上的查询基本不超过0.001秒。

总结:

当一条查询语句符合覆盖索引条件时,sql只需要通过索引就可以返回查询所需要的数据,这样避免了查到索引后再返回表操作,减少I/O提高效率。
使用覆盖索引Innodb比MyISAM效果更好----InnoDB使用聚集索引组织数据,如果二级索引中包含查询所需的数据,就不再需要在聚集索引中查找了

注:遇到以下情况,执行计划不会选择覆盖查询
1.select选择的字段中含有不在索引中的字段 ,即索引没有覆盖全部的列。
2.where条件中不能含有对索引进行like的操作。

<think>嗯,用户想了解覆盖索引的概念以及如何用它来优化查询性能。首先,我需要先回忆覆盖索引的定义。覆盖索引指的是一个索引包含了查询中所有需要访问的字段,这样数据库引擎可以直接从索引中获取数据,而不需要回表查询数据行。这样可以减少I/O操作,提升查询速度。 接下来,我得想用户可能遇到的场景。比如,当执行SELECT语句时,如果索引覆盖了所有需要的列,引擎就不用再去主键索引或者数据页查找,减少了磁盘访问。比如有一个用户表,有id、name、age三个字段,如果查询只需要name和age,而建立了一个包含这两个字段的索引,那么查询就可以直接使用这个索引,避免回表。 然后,用户可能想知道如何利用覆盖索引优化查询。首先需要确认查询的字段是否都被索引包含,可以通过执行计划查看Extra列是否有“Using index”的提示。另外,可能需要调整索引的列顺序,或者创建组合索引覆盖更多查询场景。比如,组合索引(a, b)可以覆盖只查询a和b的情况,但如果查询需要a、b、c,则需要包含c的索引。 还要考虑索引的大小,覆盖索引虽然快,但如果包含太多字段,会导致索引体积过大,反而影响性能。这时候可能需要权衡,选择最常用的查询字段来建立覆盖索引。 用户可能还会问到覆盖索引和前缀索引的关系。比如,引用中提到的使用前缀索引可能无法利用覆盖索引,因为前缀索引只包含字段的一部分,可能无法满足查询需要的所有数据,导致还是要回表。所以,在设计前缀索引时要考虑这个因素。 另外,用户可能想知道如何在实际操作中应用覆盖索引。比如,避免使用SELECT *,只选择需要的列,这样更容易让优化器使用覆盖索引。同时,检查现有的查询语句,分析执行计划,确认是否有回表操作,从而决定是否需要调整索引结构。 可能还需要提到索引下推(ICP),虽然这和覆盖索引不同,但也是优化查询的一种技术。不过用户的问题主要集中在覆盖索引上,所以暂时可以专注于覆盖索引的解释。 最后,总结覆盖索引的优缺点和适用场景,帮助用户理解何时使用覆盖索引最有效,以及需要注意的地方,比如索引维护成本和存储开销。</think>### 覆盖索引的定义与优化方法 #### 一、覆盖索引的定义 覆盖索引(Covering Index)指**索引本身包含查询所需的所有字段**,使得数据库引擎无需回表(即无需访问主键索引或数据页)即可完成查询。例如,若查询需要字段 $a$ 和 $b$,而索引 $(a, b, c)$ 包含这两个字段,则可以直接通过索引返回结果[^2]。 #### 二、覆盖索引优化原理 1. **减少I/O操作** 直接从索引树读取数据,无需访问数据页,减少磁盘访问次数[^4]。 2. **避免回表开销** 传统查询需先查索引再回表取数据,覆盖索引省略了回表步骤。 3. **降低CPU和内存消耗** 减少数据页加载到内存的规模,提升缓存效率[^4]。 #### 三、优化查询性能的方法 1. **设计合理的组合索引** - 将查询中高频使用的字段组合成索引,例如: ```sql CREATE INDEX idx_covering ON table (col1, col2, col3); ``` - 需遵循**最左匹配原则**,索引顺序需匹配查询条件。 2. **避免使用`SELECT *`** 明确指定所需字段,减少索引覆盖字段导致回表的可能性。 3. **利用执行计划验证** 在SQL语句前添加`EXPLAIN`,观察`Extra`列: - 若显示`Using index`,说明使用了覆盖索引[^3]。 - 若显示`Using intersect`或`Using union`,可能需优化索引结构[^3]。 4. **权衡索引字段数量** 覆盖索引可能增加索引体积,需平衡查询性能与存储成本[^1]。 #### 四、案例分析 假设表`users`包含字段`id, name, age, address`,索引为`(name, age)`: - **优化前查询**: ```sql SELECT id, name, age FROM users WHERE name = 'Alice'; ``` 需要回表获取`id`,若索引未包含`id`。 - **优化索引**: ```sql CREATE INDEX idx_covering ON users (name, age, id); ``` 查询可直接通过索引完成,无需回表[^2]。 #### 五、限制与注意事项 1. **前缀索引不适用** 若使用前缀索引(如`name(10)`),可能无法完全覆盖字段值,仍需回表[^1]。 2. **更新代价较高** 覆盖索引字段越多,数据写入和更新时维护索引的开销越大。 --- ###
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值