不懂索引覆盖和索引下推?看完这篇秒懂!

503 阅读6分钟

B+ 树中的索引覆盖和索引下推

123.png

1. 什么是索引覆盖?

索引覆盖是指数据库查询时,所有查询需要的字段都可以从索引中直接获取,不需要回表查找数据。通俗点说,就是数据库的索引本身已经包含了你需要的“答案”。

举个例子:

假设有一张表 student,包含以下字段:

  • id (主键)
  • name (名字)
  • age (年龄)
  • score (成绩)

现在你在 age 字段上创建了一个联合索引:(age, score)

如果你执行如下查询:

SELECT score FROM student WHERE age = 18;

这个时候,数据库发现:

  • 你查询的 score 字段和条件 age=18 都包含在索引 (age, score) 里。
  • 不需要再去表里面读取其他字段的数据

这就叫“索引覆盖”,因为索引已经完全覆盖了查询需要的字段。

为什么索引覆盖很快? 因为不需要来回从磁盘中读取数据表,只需要从索引(比较小且高效的数据存储结构)中读取数据,大大节省了时间。

即使索引覆盖具备高效的查询优势,但在某些情况下,索引覆盖也无法实际被使用


1. 查询条件不匹配索引设计

如果查询条件和索引的字段顺序或类型不匹配,索引覆盖就无法生效。

举个例子:

假设我们在 student 表上创建了一个联合索引 (age, score),用于优化查询。

现在执行以下 SQL:

SELECT score FROM student WHERE score = 90;

为什么索引覆盖无法使用?

  • 联合索引 (age, score) 是按照字段顺序建立的。
  • 在联合索引中,必须首先根据 age 字段进行查找,score 的条件无法单独利用这个索引。
  • 换句话说,查询需要依赖索引的“最左前缀原则”,否则就会失效。

2. 查询字段中有不包含在索引内的字段

如果查询的字段不完全被索引覆盖,即使查询条件使用了索引,数据库仍然需要“回表”查询其他字段,这种情况下无法实现索引覆盖。

举个例子:

还是 student 表,联合索引 (age, score) 已经建立。

现在执行以下 SQL:

SELECT name FROM student WHERE age = 18;

问题在哪里?

  • 查询的字段 name 并不在 (age, score) 索引中。
  • 数据库无法通过索引直接获取 name,所以必须“回表”查询原表中的数据。
  • 因此,即使索引可以筛选 age = 18 的条件,也无法实现索引覆盖

3. 查询字段使用了函数或运算

如果查询字段经过了某些函数或运算操作,即使索引中有这些字段,索引仍然无法被有效利用。

举个例子:

假设在 age 字段上有一个单独的索引。

执行以下 SQL:

SELECT age FROM student WHERE age + 1 = 19;

为什么索引覆盖失效?

  • 条件 age + 1 = 19 需要经过计算(即变成 age = 18),这让索引失去了直接匹配的能力。
  • 数据库无法利用索引直接筛选出符合条件的记录。

类似情况:

  • 使用了函数,例如 WHERE UPPER(name) = 'TOM'
  • 使用了范围查询,但涉及到复杂条件,例如 WHERE age BETWEEN 10 AND score + 5

4. 查询条件中的数据类型不一致

如果查询条件中的字段和索引字段的数据类型不一致,即使索引存在,也可能无法被有效利用。

举个例子:

假设在 age 字段上有个索引,而 age 是一个整数(INT)字段。

执行以下 SQL:

SELECT score FROM student WHERE age = '18';

为什么索引覆盖失效?

  • 在查询中,age = '18' 中的 18 是字符串类型,而索引中的 age 是整数类型。
  • 数据库可能需要做隐式的数据类型转换,这会导致索引失效,无法实现索引覆盖。

5. 查询条件过于复杂

如果查询条件非常复杂(例如包含多个 OR 条件、子查询等),即使某些字段有索引,也可能无法实现索引覆盖。

举个例子:

SELECT score FROM student WHERE age = 18 OR name = 'Tom';

为什么索引覆盖失效?

  • age 有索引,但 name 没有索引。
  • OR 条件会导致数据库需要扫描两个不同的范围,可能无法充分利用索引。

6. 索引不适合特定存储引擎

有些存储引擎(例如 MyISAM 和 InnoDB)对索引覆盖的支持可能存在差异。如果使用的存储引擎不支持“覆盖索引”,即使查询条件完全匹配索引,也可能无法使用索引覆盖。


总结

以下是索引覆盖无法使用的常见情况:

  1. 查询条件不符合索引的字段顺序(违反最左前缀原则)。
  2. 查询字段中有不包含在索引中的字段。
  3. 查询字段涉及函数或运算(导致索引失效)。
  4. 查询条件的数据类型和索引字段不一致。
  5. 查询条件过于复杂(例如多个 OR 条件)。
  6. 存储引擎或索引设计不支持索引覆盖。

什么是最左前缀原则

从最左前缀匹配到索引跳跃扫描:揭开MySQL索引的工作原理

在实际使用中,合理设计索引结构,并优化查询条件,可以最大化利用索引覆盖的性能优势~ 😊


索引覆盖(Index Covering)的查找流程

234.png

2. 什么是索引下推?

索引下推是优化查询效率的一种策略,把条件过滤的工作尽量在索引扫描阶段完成,减少不必要的数据读取。

举个例子:

还是这张 student 表,我们在 age 字段上建立了普通索引。

如果你执行如下查询:

SELECT * FROM student WHERE age > 18 AND score > 80;

索引下推是怎么回事?

  1. 数据库会先通过 age 的索引,找到所有 age > 18 的记录。
  2. 在扫描索引时,数据库会直接检查 score > 80 这个条件(在索引中能看到 score 的值)。
  3. 只有满足两个条件的记录,才会被“回表”去数据表中读取完整的内容

什么是回表

从索引到数据表:回表操作的实战解析和优化策略

没有索引下推时的情况:

  1. 数据库先通过 age > 18 找到所有可能的记录。
  2. 然后从表中把所有这些记录拉出来,再检查 score > 80

可以看出:

  • 有索引下推时,数据表的访问次数减少,查询效率更高。
  • 索引下推就像门卫,在大门口就筛选掉无关的记录,只有合格的才放进去。

索引下推(Index Pushdown)的查找流程

345.png

总结:

  • 索引覆盖:索引能完全满足查询需求,不需要回表,效率高。
  • 索引下推:过滤条件尽量在索引阶段完成,减少不必要的回表,提高查询效率。

综合流程图

678.png