@@ -174,9 +174,9 @@ MySQL 8.x 中实现的索引新特性:
174174
175175## 二级索引
176176
177- ** 二级索引(Secondary Index)又称为辅助索引,是因为二级索引的叶子节点存储的数据是主键。 也就是说,通过二级索引,可以定位主键的位置。 **
177+ 二级索引(Secondary Index)的叶子节点存储的数据是主键的值, 也就是说,通过二级索引可以定位主键的位置,二级索引又称为辅助索引/非主键索引。
178178
179- 唯一索引,普通索引,前缀索引等索引属于二级索引 。
179+ 唯一索引,普通索引,前缀索引等索引都属于二级索引 。
180180
181181PS: 不懂的同学可以暂存疑,慢慢往下看,后面会有答案的,也可以自行搜索。
182182
@@ -195,7 +195,7 @@ PS: 不懂的同学可以暂存疑,慢慢往下看,后面会有答案的,
195195
196196#### 聚簇索引介绍
197197
198- ** 聚簇索引(Clustered Index)即索引结构和数据一起存放的索引,并不是一种单独的索引类型。InnoDB 中的主键索引就属于聚簇索引。**
198+ 聚簇索引(Clustered Index)即索引结构和数据一起存放的索引,并不是一种单独的索引类型。InnoDB 中的主键索引就属于聚簇索引。
199199
200200在 MySQL 中,InnoDB 引擎的表的 ` .ibd ` 文件就包含了该表的索引和数据,对于 InnoDB 引擎表来说,该表的索引(B+树)的每个非叶子节点存储索引,叶子节点存储索引和索引对应的数据。
201201
@@ -215,7 +215,7 @@ PS: 不懂的同学可以暂存疑,慢慢往下看,后面会有答案的,
215215
216216#### 非聚簇索引介绍
217217
218- ** 非聚簇索引(Non-Clustered Index)即索引结构和数据分开存放的索引,并不是一种单独的索引类型。二级索引(辅助索引)就属于非聚簇索引。MySQL 的 MyISAM 引擎,不管主键还是非主键,使用的都是非聚簇索引。**
218+ 非聚簇索引(Non-Clustered Index)即索引结构和数据分开存放的索引,并不是一种单独的索引类型。二级索引(辅助索引)就属于非聚簇索引。MySQL 的 MyISAM 引擎,不管主键还是非主键,使用的都是非聚簇索引。
219219
220220非聚簇索引的叶子节点并不一定存放数据的指针,因为二级索引的叶子节点就存放的是主键,根据主键再回表查数据。
221221
@@ -262,7 +262,9 @@ SELECT id FROM table WHERE id=1;
262262
263263### 覆盖索引
264264
265- 如果一个索引包含(或者说覆盖)所有需要查询的字段的值,我们就称之为 ** 覆盖索引(Covering Index)** 。我们知道在 InnoDB 存储引擎中,如果不是主键索引,叶子节点存储的是主键+列值。最终还是要“回表”,也就是要通过主键再查找一次,这样就会比较慢。而覆盖索引就是把要查询出的列和索引是对应的,不做回表操作!
265+ 如果一个索引包含(或者说覆盖)所有需要查询的字段的值,我们就称之为 ** 覆盖索引(Covering Index)** 。
266+
267+ 在 InnoDB 存储引擎中,非主键索引的叶子节点包含的是主键的值。这意味着,当使用非主键索引进行查询时,数据库会先找到对应的主键值,然后再通过主键索引来定位和检索完整的行数据。这个过程被称为“回表”。
266268
267269** 覆盖索引即需要查询的字段正好是索引的字段,那么直接根据该索引,就可以查到数据了,而无需回表查询。**
268270
@@ -313,7 +315,8 @@ CALL BatchinsertDataToCusOder(1, 1000000); # 插入100w+的随机数据
313315为了能够对这 100w 数据按照 ` score ` 进行排序,我们需要执行下面的 SQL 语句。
314316
315317``` sql
316- SELECT ` score` ,` name` FROM ` cus_order` ORDER BY ` score` DESC ;# 降序排序
318+ # 降序排序
319+ SELECT ` score` ,` name` FROM ` cus_order` ORDER BY ` score` DESC ;
317320```
318321
319322使用 ` EXPLAIN ` 命令分析这条 SQL 语句,通过 ` Extra ` 这一列的 ` Using filesort ` ,我们发现是没有用到覆盖索引的。
@@ -348,13 +351,89 @@ ALTER TABLE `cus_order` ADD INDEX id_score_name(score, name);
348351
349352### 最左前缀匹配原则
350353
351- 最左前缀匹配原则指的是,在使用联合索引时,** MySQL** 会根据联合索引中的字段顺序,从左到右依次到查询条件中去匹配,如果查询条件中存在与联合索引中最左侧字段相匹配的字段,则就会使用该字段过滤一批数据,直至联合索引中全部字段匹配完成,或者在执行过程中遇到范围查询(如 ** ` > ` ** 、** ` < ` ** )才会停止匹配。对于 ** ` >= ` ** 、** ` <= ` ** 、** ` BETWEEN ` ** 、** ` like ` ** 前缀匹配的范围查询,并不会停止匹配。所以,我们在使用联合索引时,可以将区分度高的字段放在最左边,这也可以过滤更多数据。
354+ 最左前缀匹配原则指的是在使用联合索引时,MySQL 会根据索引中的字段顺序,从左到右依次匹配查询条件中的字段。如果查询条件与索引中的最左侧字段相匹配,那么 MySQL 就会使用索引来过滤数据,这样可以提高查询效率。
355+
356+ 最左匹配原则会一直向右匹配,直到遇到范围查询(如 >、<)为止。对于 >=、<=、BETWEEN 以及前缀匹配 LIKE 的范围查询,不会停止匹配(相关阅读:[ 联合索引的最左匹配原则全网都在说的一个错误结论] ( https://mp.weixin.qq.com/s/8qemhRg5MgXs1So5YCv0fQ ) )。
357+
358+ 假设有一个联合索引` (column1, column2, column3) ` ,其从左到右的所有前缀为` (column1) ` 、` (column1, column2) ` 、` (column1, column2, column3) ` (创建 1 个联合索引相当于创建了 3 个索引),包含这些列的所有查询都会走索引而不会全表扫描。
359+
360+ 我们在使用联合索引时,可以将区分度高的字段放在最左边,这也可以过滤更多数据。
361+
362+ 我们这里简单演示一下最左前缀匹配的效果。
363+
364+ 1、创建一个名为 ` student ` 的表,这张表只有 ` id ` 、` name ` 、` class ` 这 3 个字段。
365+
366+ ``` sql
367+ CREATE TABLE `student ` (
368+ ` id` int NOT NULL ,
369+ ` name` varchar (100 ) DEFAULT NULL ,
370+ ` class` varchar (100 ) DEFAULT NULL ,
371+ PRIMARY KEY (` id` ),
372+ KEY ` name_class_idx` (` name` ,` class` )
373+ ) ENGINE= InnoDB DEFAULT CHARSET= utf8mb4;
374+ ```
375+
376+ 2、下面我们分别测试三条不同的 SQL 语句。
377+
378+ ![ ] ( https://oss.javaguide.cn/github/javaguide/database/mysql/leftmost-prefix-matching-rule.png )
379+
380+ ``` sql
381+ # 可以命中索引
382+ SELECT * FROM student WHERE name = ' Anne Henry' ;
383+ EXPLAIN SELECT * FROM student WHERE name = ' Anne Henry' AND class = ' lIrm08RYVk' ;
384+ # 无法命中索引
385+ SELECT * FROM student WHERE class = ' lIrm08RYVk' ;
386+ ```
352387
353- 相关阅读: [ 联合索引的最左匹配原则全网都在说的一个错误结论 ] ( https://mp.weixin.qq. com/s/8qemhRg5MgXs1So5YCv0fQ ) 。
388+ MySQL 8.0.13 版本引入了索引跳跃扫描(Index Skip Scan,简称 ISS),它可以在某些索引查询场景下提高查询效率。在没有 ISS 之前,不满足最左前缀匹配原则的联合索引查询中会执行全表扫描。而 ISS 允许 MySQL 在某些情况下避免全表扫描,即使查询条件不符合最左前缀。不过,这个功能比较鸡肋, 和 Oracle 中的没法比,MySQL 8.0.31 还报告了一个 bug: [ Bug # 109145 Using index for skip scan cause incorrect result ] ( https://bugs.mysql. com/bug.php?id=109145 ) (后续版本已经修复)。个人建议知道有这个东西就好,不需要深究,实际项目也不一定能用上 。
354389
355390## 索引下推
356391
357- ** 索引下推(Index Condition Pushdown)** 是 ** MySQL 5.6** 版本中提供的一项索引优化功能,可以在非聚簇索引遍历过程中,对索引中包含的字段先做判断,过滤掉不符合条件的记录,减少回表次数。
392+ ** 索引下推(Index Condition Pushdown,简称 ICP)** 是 ** MySQL 5.6** 版本中提供的一项索引优化功能,它允许存储引擎在索引遍历过程中,执行部分 ` WHERE ` 字句的判断条件,直接过滤掉不满足条件的记录,从而减少回表次数,提高查询效率。
393+
394+ 假设我们有一个名为 ` usr ` 的表,其中包含 ` id ` , ` name ` , 和 ` age ` 3 个字段,` name ` 字段上创建了索引。
395+
396+ ``` sql
397+ # 查询名字以"Aoki"开头且年龄为30岁的用户
398+ EXPLAIN SELECT * FROM usr
399+ WHERE name LIKE ' Aoki%' AND age = 30 ;
400+ ```
401+
402+ - 没有索引下推之前,即使 ` name ` 字段建立的索引可以帮助我们快速定位到了以“张”开头的用户,但我们仍然需要对每一个找到的以“张”开头的用户进行回表操作,获取完整的用户数据,再判断 ` age ` 字段是否等于 30。
403+ - 有了索引下推之后,存储引擎会在使用 ` name ` 索引查找以"张"开头的记录时,同时检查 ` age ` 字段是否等于 30。这样,只有同时满足 ` name ` 和 ` age ` 条件的记录才会被返回,减少了回表次数。
404+
405+ ![ ] ( https://oss.javaguide.cn/github/javaguide/database/mysql/index-condition-pushdown.png )
406+
407+ 再来讲讲索引下推的具体原理,先看下面这张 MySQL 简要架构图。
408+
409+ ![ ] ( https://oss.javaguide.cn/javaguide/13526879-3037b144ed09eb88.png )
410+
411+ MySQL 可以简单分为 Server 层和存储引擎层这两层。Server 层处理查询解析、分析、优化、缓存以及与客户端的交互等操作,而存储引擎层负责数据的存储和读取,MySQL 支持 InnoDB、MyISAM、Memory 等多种存储引擎。
412+
413+ 索引下推的** 下推** 其实就是指将部分上层(Server 层)负责的事情,交给了下层(存储引擎层)去处理。
414+
415+ 我们这里结合索引下推原理再对上面提到的例子进行解释。
416+
417+ 没有索引下推之前:
418+
419+ - 存储引擎层先根据 ` name ` 索引字段找到所有以“张”开头用户的主键 ID,然后二次回表查询,获取完整的用户数据。
420+ - 存储引擎层把所有以“张”开头的用户数据全部交给 Server 层,Server 层根据` age ` 字段是否等于 30 这一条件再进一步做筛选。
421+
422+ 有了索引下推之后:
423+
424+ - 存储引擎层先根据 ` name ` 索引字段找到所有以“张”开头的用户,然后直接判断` age ` 字段是否等于 30,筛选出符合条件的 主键 ID。
425+ - 二次回表查询,根据符合条件的主键 ID 去获取完整的用户数据,
426+ - 存储引擎层把符合条件的用户数据全部交给 Server 层。
427+
428+ 可以看出,** 除了可以减少回表次数之外,索引下推还可以减少存储引擎层和 Server 层的数据传输量。**
429+
430+ 最后,总结一下索引下推应用范围:
431+
432+ 1 . 适用于 InnoDB 引擎和 MyISAM 引擎的查询。
433+ 2 . 适用于执行计划是 range, ref, eq_ref, ref_or_null 的范围查询。
434+ 3 . 对于 InnoDB 表,仅用于非聚簇索引。索引下推的目标是减少全行读取次数,从而减少 I/O 操作。对于 InnoDB 聚集索引,完整的记录已经读入 InnoDB 缓冲区。在这种情况下使用索引下推 不会减少 I/O。
435+ 4 . 子查询不能使用索引下推,因为子查询通常会创建临时表来处理结果,而这些临时表是没有索引的。
436+ 5 . 存储过程不能使用索引下推,因为存储引擎无法调用存储函数。
358437
359438## 正确使用索引的一些建议
360439
0 commit comments