MySQL 创建索引,MySQL深度剖析之索引专题(2021)

 2023-09-24 阅读 18 评论 0

摘要:9.1 为什么需要使用索引 第一:减少了MySQL需要扫描的数据量,尤其是全表扫描 第二:随机IO变成顺序IO,提升查询速度 可以快速匹配where子句、排序和分组也可以使用索引,匹配原则最左匹配、特定索引列的min和max计算、 9.2 MySQL 使用了哪些索

9.1 为什么需要使用索引

第一:减少了MySQL需要扫描的数据量,尤其是全表扫描

第二:随机IO变成顺序IO,提升查询速度

可以快速匹配where子句、排序和分组也可以使用索引,匹配原则最左匹配、特定索引列的min和max计算、

9.2 MySQL 使用了哪些索引

MySQL 提供了BTree和Hash索引,一般使用的是BTree索引,Hash索引很少使用或者几乎不使用

9.3 MySQL使用的BTree索引是基于什么数据结构的,为什么不使用B-Tree而是B+Tree呢?这两者有何相似点和不同点?

MySQL 使用的BTree索引是基于B+树的,B+树是在B-树基础上改变而来。那什么是B-树呢,它有哪些特点?

9.3.1 什么是B-树? 它有哪些特点? 增加、删除和查询过程是怎么样的?

9.3.1.1  什么是B-树?

B-树就是多路平衡查找树或者多路平衡树,也叫作多叉平衡树,如果包含最多M个子节点,那么就叫做M阶多路平衡树,相比较其他二叉树而言,它可以降低

树的高度,从而减少查找时间。

MySQL 创建索引。9.3.1.2 它有哪些特点

#1 根节点要么没有子节点,要么有[2,M]个子节点,根节点包含[1,M-1]个关键字

#2 分支节点的子节点个数是[M/2,M], 分支节点包含[M/2-1, M-1]个关键字

分支节点的子节点个数取决于父节点,比如5阶B树,父节点有5个关键字和6个子节点才开始分裂,增加高度,那么父节点分裂成2个子节点,每一个子节点2个关键字,子节点的子节点每一个分支子节点就是3个。

#3 所有节点的关键字是有顺序的

#4 叶子节点都在同一层

#5 父节点中的关键字>= 左子节点的最大值;关键字<= 右子节点的最小值

#6 每一个节点都是有数据的,即有data域

9.3.1.3 查询和增加、删除过程是怎么样的?

9.3.1.3.1 查询

#1 需要将要插入的关键字从根节点的关键字开始,按顺序比较,如果比当前的大就往后面移动,直到遇到比自己大的;如果遇到相等的则表示找到了

#2 根据对应的子节点指针,获取下一个节点,又继续开始根据关键字顺序比较,如果比当前的大就往后面移动,直到遇到比自己大的;如果遇到相等的则表示找到了

9.3.1.3.2 插入

#1 需要根据要插入的元素从根节点开始查找,找到合适插入的叶子节点

#2 根据节点内的顺序,按照顺序插入该元素

#3 插入后需要判断是不是已经数量达到M-1,如果已经是M-1,则表示需要分裂这个叶子节点

#4 分裂过程则是从该节点的关键字中选择中间的那个移到父节点中,然后当前节点分裂为两个子节点

#5 如果父节点因为子节点的向上移动元素,那么也有可能分裂,从而也需要分裂为两个子节点,根据这个规律一直到根节点都是这样

9.3.1.3.3 删除

删除元素,主要分为删除叶子结点和分支节点,删除的逻辑不一样

场景一:删除叶子节点

第一: 叶子节点的关键字个数>(M/2-1)

直接删除这个元素或者关键字即可

第二:叶子节点的关键字个数=(M/2-1)

Case1: 相邻的兄弟节点个数>(M/2-1)

#1 删除当前节点元素

#2 将父节点对应的元素移到当前节点

#3 将兄弟节点对应元素移到父节点

Case2: 相邻的兄弟节点个数=(M/2-1),父节点个数>(M/2-1)

#1 删除当前节点元素

#2 将父节点的对应的关键字和兄弟节点元素合并

Case3: 相邻的兄弟节点个数=(M/2-1),父节点个数=(M/2-1)

#1 删除当前节点元素

#2 将父节点的对应的关键字和兄弟节点元素合并

#3 父节点因为没有元素了,所以需要降低高度,父节点的父节点和兄弟节点合并

场景二:删除分支节点

#1 要查找要删除元素所在的分支节点

#2 其次是从该节点子树中找到最小的节点来替换要删除的元素

#3 最后判断最小节点被移动之后是否需要平衡,不平衡的需要进行合并或者旋转操作

9.3.2 什么是B+树? 它有哪些特点? 增加、删除和查询过程是怎么样的?  B-树和B+树比较异同点?

9.3.2.1 什么是B+树?

B+树是在B-树基础上一种变体,它也属于多路平衡查找树,如果一个B+树最多有M个子节点,那就叫做M阶B+树。

9.3.2.2 它有哪些特点?

#1 根节点有[2,M]个子节点;可以有[2,M]个关键字

#2 分支节点有[M/2,M]个分支节点;可以有[M/2,M]个关键字

#3 每一个节点内关键字是有顺序的

#4 叶子节点位于同一层

#5 根节点和分支节点没有数据,即没有data域,只有叶子节点有data域

#6 父节点的关键字一定会出现在子节点中,并且是子节点最大值,因此每一个父节点的最右边的元素一定是子树中最大值;根节点最右边的元素一定是全局

最大的

#7 叶子节点会包含根节点和所有分支节点的数据

#7 叶子节点会被链接起来,形成一个有序链表,这样可以方便范围查找

9.3.3.3 B-树和B+树比较异同点?

9.3.3.3.1 相同点

#1 每一个节点内关键字是有顺序的

#2 叶子节点位于同一层

#3 根节点的子节点数量不为空的话,可以是[2,M]个

9.3.3.3.2 不同点

#1 根节点关键字数量不一样

#2 分支节点子节点数量和关键字数量不一样

#3 根节点和分支节点是否存在data域

#4 父节点的关键字是否出现在子节点中

#5 叶子节点是否会包含根节点和所有分支节点的数据

#6 叶子节点是否是有序链表

9.4 MySQL为什么不大量使用hash数据结构? 为什么不使用二叉树(红黑树)? 为什么使用多叉平衡树但是不使用B-树,而是B+树呢?

9.4.1 MySQL为什么不大量使用hash数据结构?

MySQL是提供了hash索引的,只是用的不多,主要原因在于以下几点:

第一: hash作为索引,根据字段计算hash值,以及该行数据的磁盘文件地址进行对应,查询时间复杂度是O(1), 所以特别快

第二: 但是我们知道,hash结构存在hash冲突的问题

第三: 但是最根本的原因是无法进行范围查询,因为hash只能找到某一个记录,比它小或者大的记录无法通过hash索引实现

9.4.2 为什么不使用二叉树?

二叉树是肯定可以作为索引的,时间复杂度是O(logN), 速度也还可以,但是MySQL为什么不使用二叉树作为索引呢,比如红黑树,为什么?

二叉树因为只有2个子节点,而且每一个节点只能存储1个关键字,这就会导致高度可能会很高,所以需要进行磁盘IO的次数就多,从而性能不好

为什么不使用红黑树作为索引

红黑树是二叉树,所以存在二叉树的高度问题带来的问题;并且红黑树因为考虑平衡,所以经常需要改变颜色,进行旋转等自平衡操作,所以对性能是不友好的

9.4.3 为什么不使用B-树呢?

#1 B-树每一个节点都会包含数据域,如果一个节点就是一个页的话(16K), 那么这个页可以存放的数据就会少,那么势必就有可能创建很多的页来存储数据,

势必增加B-树高度,从而查询需要更多的磁盘IO,对性能不友好

#2 B-树要查找的数据可能在第一层,也可能在最后一层,所以查询是不稳定的;B+树查询都会落到最后一层,查询路径是一样的,所以更加稳定

#3 B-树对于范围查询,可能需要更多的磁盘IO;但是B+树叶子节点是有序链表,方便顺序扫描,更加方便范围查询

9.5 MySQL中什么是聚簇索引和非聚簇索引?什么是普通索引和联合索引?

9.5.1什么是聚簇索引和非聚簇索引

9.5.1.1 聚簇索引

索引和数据存储在一起就是聚簇索引,比如InnoDB引擎主键索引索引和数据存储在同一个树上,索引InnoDB的主键索引就是聚簇索引。索引和数据存储在IBD文件中。

9.5.1.2 非聚簇索引

索引和数据没有存储在一起或者说分开存储的就是非聚簇索引,比如MyISAM引擎的索引存储在一个树上,叶子节点只是存储的数据所在磁盘地址,而真正存储数据在另一个地方。索引存储在MYI文件中,数据存储在MYD文件中。

9.5.2 什么是普通索引和联合索引

9.5.2.1 普通索引

我们把非聚簇索引也叫作普通索引,或者二级索引,也就是索引和数据不是存储在一起的。通常而言,普通索引会但单独构建一棵B+树,在叶子节点会存储所有索引以及该记录对应的主键值。

当要针对非聚簇索引查询的时候,首先会从非聚簇索引查询,定位到主键信息,然后根据主键信息,进行回表查询,也就是根据聚簇索引查找到真正的数据。

9.5.2.2 联合索引

联合索引就是将多个字段联合起来作为索引,比如上图的班级+姓名+科目联合作为索引,构建流程和非聚簇索引是一样的。再查询的时候只不过先比较班级,班级相同再比较姓名,姓名相同再比较科目。假设查询,1班belly的英语成绩:

SELECT * FROM student WHERE class='1班' AND name='belly' AND subject='英语'

现在数据页通过二分查找找到班级,班级一样则比较姓名,姓名一样则比较科目,找到对应的记录,获取主键,然后回表查询具体的记录。

9.6 MySQL为什么需要主键,如果没有主键,MySQL怎么处理?

我们知道MySQL InnoDB存储引擎聚集索引会将索引和数据在一起存储,如果是分开存储,那么还需要通过磁盘地址到到对应的磁盘加载数据页;如果是存储在一起那么可以直接加载这个数据页,磁盘IO次数会少一些。另外普通索引也是需要存储索引字段和主键值,通过回表查询记录。综合上述,MySQL需要主键。

但是,就是没有主键呢?难道MySQL就无法构建聚集索引和非聚集索引了吗?不是的,工作流程是这样:

首先:如果用户定义了表的主键,则使用用户定义的

其次:如果用户没有定义,则MySQL从表第一列开始判断当前列的值是不是唯一的,如果找得到这样的列,则MySQL使用这个列作为默认的主键列

最后:如果从表中没有找到唯一值列,那么MySQL自己就会每一个记录隐藏添加一个rowid唯一值列,这个列由MySQL自己维护

9.7 索引有哪些使用规则? 排序和分组是如何才能使用索引的,索引什么时候会失效?

当创建了索引之后,在查询的时候要使用到索引才可以提升查询速度,那怎么样才能使用到索引呢,这里有一些索引使用规则

9.7.1 where 条件使用索引

9.7.1.1 等值规则

在where语句中通过索引字段过滤,且索引字段是等值操作,比如:

select * from cloud_orders where order_no = 9873211911095250

9.7.1.2 前缀规则

在where语句中通过索引字段过滤,且索引字段是like操作,并且like 操作是前缀匹配。如果是后缀匹配等,比如 %男士% 或者 %届等。

select * from products where grade like 'A%';

9.7.1.3 范围查找

即对索引字段进行范围查找,比如between and 或者 > < 以及主键下的!=

select user_id,order_id,total_amount,payment_amount from cloud_orders where order_no > 5025321911095352 and order_no < 5633221911095335;

select user_id,order_id,total_amount,payment_amount from cloud_orders where order_id != 164554966428567553;

 

例外情况1:索引列相同值很多

但如果索引列的值基数很小,有很多列的有相同值,比如男女、状态等字段不适合建立索引,所以这样做范围查询,其成本和走全表扫描差不多或者还不如全表扫描,所以就会走全表扫描,而不是走索引,比如:

select user_id,order_id,total_amount,payment_amount from cloud_orders where order_channel>1 and order_channel <=2;

 

例外情况2:如果是普通索引,!= 不等于会不走索引

select * from dev_test.role  where card != 10;

 

9.7.1.4 联合索引等值匹配

对于联合索引,我们可以在where列对联合索引中对所有字段进行等值查询,即便在where中顺序和联合索引中顺序不一样,也不要紧,MySQL自己会做优化,也会使用索引的。前提是联合索引中索引字段都要作为where等值查询条件,而不是其中的某一个或者几个字段,比如对成绩表建立联合索引class_name_subject(class,name,subject)

select * from dev_test.scores where subject='数学' and name='秦尘' and class='2班'

 

9.7.1.5 联合索引最左侧列匹配

对于联合索引,我们可以对所有字段进行等值查询,且顺序不一样也可以走索引。

比如:class='1班' and name ='楚灵溪' and subject='语文'。

但是如果只是其中一个或者几个进行等值查询,并非全部索引字段,则就不满足条件。遇到这种情况,需要在where条件中满足联合索引最左侧列匹配规则。

联合索引非使用全部索引字段进行查询走索引的情况

查询class='1班' 或者class='1班' and name ='楚灵溪' 或者name ='楚灵溪'  and class='1班'都可以使用索引。

select * from dev_test.scores where class='1班';

select * from dev_test.scores where class='1班' and name='楚灵溪';

select * from dev_test.scores where  name='楚灵溪' and  class='1班';

联合索引非使用全部索引字段进行查询不走索引的情况

如果没有使用class字段,比如,查询 name ='楚灵溪'  或者 name ='楚灵溪' and subject='语文',则不会使用索引字段。

select * from dev_test.scores where name='秦尘';

select * from dev_test.scores where name='秦尘' and subject='数学'; 

 

9.7.1.6 联合索引最左侧前缀匹配

#1 必须满足联合索引最左侧列匹配

#2 必须联合索引第一个字段不能进行like 前缀匹配;其他任何字段都可以

正确使用情况:

select * from dev_test.scores where class='1班' and name like '楚%';

select * from dev_test.scores where class='1班' and name='秦尘' and subject='数%';

错误使用情况:

select * from dev_test.scores where class='1%';

select * from dev_test.scores where class like '2%' and  name='楚灵溪';

 

9.7.1.7 联合索引范围查找

对于联合索引范围查找规则,只有对联合索引最左侧的列进行范围查找才有用,对于students建立索引grade_class_card_idx(`grade`,`class`,`card`)。

正确使用情况:

select * from dev_test.students where grade > 1 and grade < 3;

select * from dev_test.students where grade = 1 and class > 1 and class < 3; 

错误使用情况:

不是最左侧列进行范围查询,不走索引

select * from dev_test.students where class > 5;

9.7.2 order by 使用索引

对于普通排序字段,当where条件过滤出要返回的字段的时候,然后讲这些记录加载到内存或者临时磁盘文件按照排序字段进行排序,最后将排序后的数据返回。如果要返回数据量少可以使用内存排序,如果要返回的数据量很大的话,内存放不下,则会基于磁盘文件排序,即filesort. 这样的话速度就很慢了。

如果排序字段使用索引有什么好处呢?我们知道InnoDB引擎存储的索引是有序的,所以我们一旦对排序字段建立索引,那么就不需要再排序了,速度提升将会很快。

比如,cloud_orders 建立索引order_id(PK),order_no,user_id,另外order_channel没有索引,SQL语句

9.7.2.1 排序字段不使用索引

select * from cloud_order where user_id = 164394235868506113 order by order_channel;

#1 首先会查询所有user_id的订单

#2 然后根据订单渠道order_channel排序

#3 因为order_channel不是索引字段,所以可能会走文件排序(filesort)

9.7.2.2 排序字段索引

#1 没有where,即便使用索引字段排序也不会生效

select * from cloud_order order by user_id;

#2 有where,但是where 中的字段没有索引,即便使用索引字段排序也不会生效

select * from cloud_order where order_channel = 3 order by user_id;

#3有where,且where 中的字段有索引,而且排序字段也有索引,也不生效

select * from cloud_order where user_id = 164394235868506113 order by order_no;

9.7.2.3 排序字段使用联合字段索引

我们从以上看到,无论是否有where或者where是否使用索引字段,是否排序是否使用索引字段,都无法使用索引进行排序,都是走的filesort。

怎么样才能让排序的索引字段走索引排序(index sort)而非文件排序(file sort)呢?

我们需要对where中查询使用的索引字段和可能排序的字段构建联合索引,比如可能经常根据用户查询用户订单,并且按照订单状态排序,订单状态相同则按照订单总额排序。我们可以创建一个联合索引:

CREATE INDEX user_status_total_idx ON cloud_orders (user_id,order_status,total_amount);

select * from cloud_orders where user_id = 164344266070432769 order by order_status;

select * from cloud_orders where user_id = 164344266070432769 order by order_status,total_amount; 

9.7.2.4 联合索引必须满足一定规则,where条件必须是联合索引最左侧列

select * from cloud_orders where user_id = 164344266070432769 order by order_status,total_amount;

select * from cloud_orders where user_id = 164344266070432769 and order_status = 1 order by total_amount;

 

9.7.2.5 排序字段有多个索引字段排序,且已经建立联合索引,则排序字段必须同时升序或者降序,否则就不会走索引排序

select * from cloud_orders where user_id = 164344266070432769 order by order_status ,total_amount desc;

9.7.3 group by 使用索引

对于分组而言,如果使用的未建立索引字段分组,那么将where中过滤的数据创建临时表,然后对分组字段进行filesort 排序,排序后的相同分组数据紧挨着一起,最后进行分组聚合计算,返回分组聚合后的结果。所以,说白了,其实分组没有索引也是会走order by 一样的流程,进行文件排序,这个过程速度是比较慢的。解决办法当然和order by 一样,对排序字段创建联合索引。

9.7.3.1 分组字段不是索引字段

SELECT grade, COUNT(class) FROM dev_test.colleges  GROUP BY grade;

我们可以看到它会对查询结果使用临时表存储,并且通过filesort排序

9.7.3.2 分组字段是单个索引字段

SELECT grade, COUNT(class) FROM dev_test.university GROUP BY grade

9.7.3.3 分组字段是多个索引字段,建立联合索引

如果分组是多个索引字段,是不会走索引排序,会使用临时表和文件排序

SELECT grade, COUNT(class) FROM dev_test.university WHERE grade > 0 GROUP BY grade,class;

所以可以通过组合索引进行多字段分组,grade_class_card_idx(grade,class,card):

SELECT grade, COUNT(class) FROM dev_test.students WHERE grade > 0 GROUP BY grade,class;

9.7.4 索引失效的场景

9.7.4.1 索引字段参加计算,比如函数

索引字段没有参加运算:

select * from cloud_order where user_id = 164397617080467457;

索引字段参加运算,使用函数ABS

select * from cloud_order where ABS(user_id) = 164397617080467457;

9.7.4.2 联合索引第一个字段使用了前缀匹配或者其他普通索引字段模糊匹配使用了后缀匹配

联合索引字段第一个字段使用了前缀

select * from dev_test.scores where class like '1%';

联合索引字段第一个字段不使用前缀,其他字段使用前缀

select * from dev_test.scores where class  = '1班' and name like '楚灵%';

普通索引单字段前缀匹配,使用索引

select * from dev_test.student where name like '秦雨%';

普通索引单字段后缀匹配,不使用索引

select * from dev_test.student where name like '%雨诗';

9.7.4.3 in 在有的版本不使用索引,有的使用,具体看版本情况

select * from dev_test.scores where class in (1,2,3);

9.7.4.4 范围查询中中索引失效

9.7.4.4.1如果要查询的数据量太多,优化器可能会走全表扫描

数据量少走索引:

select * from cloud_order where order_no > 3585221911097344 and order_no < 4065111911097519;

数据量大走全表扫描

select * from cloud_order where order_no > 3585221911097344 and order_no < 8337321911097545;

9.7.4.4.2 如果索引字段存在大量相似的值索引失效

如果索引字段存在大量相似的值,则不会走索引,因为可能会有大量的值被查询,优化器会走全表扫描。

select * from dev_test.university where grade > 1 and grade < 5;

但是,注意的是这样字段如果建立索引,如果是等值查询,是会走索引,只不过范围查询因为数据量可能太大,所以不走索引。

9.7.4.4.3 如果范围查询存在不存在的值也可能导致索引失效

假设grade = (3,4,5,6,7)

select * from dev_test.product where grade > 2 and grade < 7;

9.7.4.5 非操作符导致索引失效

非操作符可能产生很多记录,优化器判定走全表扫描,所以非操作符会导致索引失效

select * from dev_test.university where grade != 5;

 

9.7.4.6 IS NULL 或者 IS NOT NULL查询索引失效

select * from dev_test.scores where class is null;

select * from dev_test.scores where class is not null;

9.8 索引存储和查询详细流程

 

9.8.1 插入记录,构建索引

在插入数据的时候,会根据定义的主键列构建索引;如果没有定义主键列,那么则看第一列是不是非空且唯一,如果是作为主键列;如果不是则使用隐藏的rowid构建索引。
InnoDB引擎数据文件本身就是索引文件,叶子节点保存了完整的数据记录,我们可以将数据页分为2种,一种是存放数据的数据页,一种是只存放索引信息的数据页,第二种我们简称为索引页,其实本质也是数据页,只不过放的是索引信息。根节点和分支节点的都是索引页,叶子节点都是数据页。

MyISAM引擎也是基于B+树实现的索引,但是区别在于MyISAM索引和数据是分开存储的,叶子节点数据域存储的是数据地址,而不是数据。

如上图所示,当添加一条数据主键为100的记录,流程如下:

#1 将根节点加载到内存,按顺序比较100和索引页上所有主键的大小,如果小于则走右边,如果大于则走左边,在这里因为100> 50, 且索引页1上只有1个主键,所以只需要比较1次,判断需要到索引页3上去查找,将索引页3加载到内存,然后和索引页3上主键比较,因为100>80,所以需要将数据页8加载到内存

#2 因为是数据页,此时根据页目录找到最大记录伪行所在的组中,目前只有2条记录,最小的记录是80,最大的是90,所以页目录只有2个组(不满足8个数据),最小记录伪行一个组,80、90和最大记录伪行是一个组。此时新加入的100是会插入到最大记录伪行的一组,因为100插入进去,分组数量为4,不满足分裂条件,不会分裂;因为100大于当前最大的记录,所以最大伪行记录需要更新,100这个记录的next_record指向这个最大记录伪行,并且更新也目录中最大分组最后一个记录,即最大伪行记录的m_own参数,表示当前槽中又增加一个数据。

#3 数据插入后更新前后记录行的关系,90的next_record指向100,100的next_record指向最大记录伪行,更新文件头和页头的一些信息,比如也目录中槽数量、最后插入记录的位置,该页中记录数量、修改当前页最大事务ID等

#4 如果插入的记录数满了,需要进行页分裂,也就是B+树中页分裂,如果主键没有顺序,不是自增的,还会进行一些排序的操作;如果是自增的只是分裂,不存在多余的操作,分裂之后,会将一个主键提升提升为索引,放到索引页中。

9.8.2 查询

假设查询id=15

#1 将根节点索引页1加载到内存,然后将15和50比较,因为15 < 50, 所以需要将索引页2加载到内存,因为15 < 30,所以将数据页4加载到内存

#2 找到页目录,根据15进行二分查找,最后发现15 > 7 并且小于22,所以获取7对应的槽位。7这个槽位记录的是该分组中最后一个记录的位置,所以定位到这个位置,发现它的ID是7,所以则通过其next_record继续往下找,数据行7的next_record是8,8不等于15,继续通过8的next_record往下找,发现是10,10不等于15,则继续通过10的next_record往下找,找到15此时发现相同了,则返回这个记录。

9.9 MySQL一些索引中的名词解释

9.9.1 回表

回表主要针对的是普通索引或者说二级索引,我们知道普通索引也会构建一棵B+树,叶子节点存储的数据是该数据的主键信息,拿到主键信息之后去主键索引或者聚簇索引中找到对应的全部数据。这个过程就是回表。

9.9.2 覆盖索引

如果一个SQL查询的列全部使用到了索引列,则属于覆盖索引。

SELECT * FROM employee WHERE name = ‘lily’

SELECT id FROM employee WHERE name = ‘lily’

假设name是普通索引列,如果我要查询name=’lily’,在普通索引B+树找到主键信息,然后需要拿到主键,回表到主键索引查找对应的数据。

但是如果我只是查询id主键,本身在普通索引B+树叶子结点已经存储主键信息,那么此时就不再回表查找数据,因为不需要其他字段信息,那么此时就不再回表,这就叫覆盖索引,是对普通索引查找的一个优化

比如联合索引<name,age,position>,SELECT name,age FROM employee WHERE name = ‘lily’ AND age > 20; 这也属于覆盖索引

9.9.3.索引下推

联合索引,不必再等待前面的WHERE条件进行索引查询之后再进行后面字段的查询,而是根据WHERE所有条件确定联合索引中数据,再回表查询。这样可以减少回表次数。举个例子:

在开始之前先先准备一张用户表(user),其中主要几个字段有:idnameageaddress。建立联合索引(nameage

假设有一个需求,要求匹配姓名第一个为陈的所有用户,sql语句如下:

SELECT * from user where  name like '%'

根据 "最佳左前缀" 的原则,这里使用了联合索引(nameage)进行了查询,性能要比全表扫描肯定要高。

问题来了,如果有其他的条件呢?假设又有一个需求,要求匹配姓名第一个字为陈,年龄为20岁的用户,此时的sql语句如下:

SELECT * from user where  name like '%' and age=20

这条sql语句应该如何执行呢?下面对Mysql5.6之前版本和之后版本进行分析。

 Mysql5.6之前的版本

5.6之前的版本是没有索引下推这个优化的,因此执行的过程如下图:

会忽略age这个字段,直接通过name进行查询,在(name,age)这课树上查找到了两个结果,id分别为2,1,然后拿着取到的id值一次次的回表查询,因此这个过程需要回表两次

 Mysql5.6及之后版本

5.6版本添加了索引下推这个优化,执行的过程如下图:

 

 InnoDB并没有忽略age这个字段,而是在索引内部就判断了age是否等于20,对于不等于20的记录直接跳过,因此在(name,age)这棵索引树中只匹配到了一个记录,此时拿着这个id去主键索引树中回表查询全部数据,这个过程只需要回表一次

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/5/92995.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息