MySQL实战45讲—— 05|深入浅出索引(下)

文章介绍了MySQL中的索引概念,特别是覆盖索引的概念和优势,指出覆盖索引可以减少树的搜索次数,提高查询性能。同时,阐述了最左前缀原则,说明如何通过这一原则有效地利用索引。此外,还提到了MySQL5.6引入的索引下推优化,能减少不必要的回表操作,提高查询效率。
摘要由CSDN通过智能技术生成

05|深入浅出索引(下)

请支持正版:MySQL实战45讲

在开始讲解之前,先看一个例子:

mysql>select * from T where k between 3 and 5;

这条语句会执行几次树的搜索?又会扫描多少行?

下面是这个表的初始化语句:

mysql>create table T(
	ID int primary key,
    k int NOT NULL DEFAULT 0,
    s varchar(16) NOT NULL DEFAULT '',
    index k(k)
)engine=InnoDB;

随后执行插入语句:

insert into T values(100,1,'aa'),(200,2,'bb'),(300,3,'cc'),(500,5,'ee'),(600,6,'ff'),(700,7,'gg');

那么开头的查询SQL语句执行流程是:

  1. 在k索引树上找到k = 3的记录,取得主键ID=300
  2. 再到ID索引树上查到ID=300对应的记录
  3. 在k索引树上找到k=5的记录,取得主键ID=500
  4. 再回到ID索引树查找ID=500的记录
  5. 在k索引树上取得下一个k=6,不满足条件,循环结束

上一篇文章,04|深入浅出索引(上) 提到过这种回到主键索引树搜索的过程称之为回表。可以看出,这个查询过程中在k索引树上读了3条记录,回表了两次

因为普通索引树上只存主键,所以要查询数据不得不回表,那有没有办法避免呢?

覆盖索引

如果执行的语句是select ID from T where k between 3 and 5,这个时候只需要查询ID的值,而ID的值已经在k索引树上,因此可以直接提供查询结果,不需要回表。

也就是说,在这个查询里,索引k已经覆盖了我们的查询需求,我们称之为覆盖索引

由于覆盖索引可以减少树的搜索次数,显著提升查询性能,所以使用覆盖索引是一个常用的性能优化手段

值得注意的是:在引擎内部使用覆盖索引在索引k上其实读了三个记录 ,但是对于MySQL的Server层,它就是找引擎要了两条记录,因此MySQL会认为扫描行数是2

那么再回想上一篇中的一个问题,在一个市民信息表上,是否有必要将身份证号和名字建立联合索引?

假设市民表是如下定义:

mysql>create table 'tuser'(
	'id' int(11) NOT NULL,
    'id_card' varchar(32) DEFAULT NULL,
    'name' varchar(32) DEFAULT NULL,
    'age' int(11) DEFAULT NULL,
    'ismale' tinyint(1) DEFAULT NULL,
    primary key(id),
    key 'id_card' ('id_card'),
    key 'name_age' ('name','age')
)engine=InnoDB;

我们知道,身份证号是市民的唯一标识,也就是说,如果有根据身份证号查询市民信息的需求,我们只要在身份证号字段上建立索引就够了,而再建立一个(身份证号、姓名)的联合索引,是不是白白浪费空间?

如果现在有一个高频请求,要根据市民的身份证号查询他的姓名,这个联合索引就有意义了。它可以在这个高频请求上用到覆盖索引,不再需要回表查整行记录,减少语句的执行时间

当然,索引字段的维护总是有代价的,因此,在建立冗余索引来支持覆盖索引时就需要权衡考虑了

最左前缀原则

看到这里你一定有一个疑问,如果为每一种查询都设计一个索引,索引是不是太多了?如果我现在要按照市民的身份证号去查他的家庭地址呢?

虽然这个查询请求在业务中出现的概率不高,但是总不能让它走全表扫描吧。反过来说,单独为了一个不频繁的请求创建一个(身份证号,地址)的索引又感觉优点浪费,应该怎么做呢?

结论:B+数这种索引结构,可以利用索引的最左前缀原则,来定位记录

假设有一个(姓名,年龄)的索引树的一个节点

”李四“,20”王五“,10“张六”,30“张三”,10“张三”,10“张三”,20
ID1ID2ID3ID4ID5ID6

可以看到,索引项是按照索引定义里面出现的字段顺序排序的

当你的逻辑需求是查到所有名字是“张三”的人时,可以快速定位到ID4,然后向后遍历得到所有需要的结果

如果你要查的是所有名字第一个是“张”的人,你的SQL语句的条件是where name like'张%‘ 。这时,你也能够用上这个索引,查找到第一个符号条件的记录是ID3,然后向后遍历,直到不满足条件为止

可以看到,不只是索引的全部定义,只要满足最左前缀,就可以利用索引来加速检索,这个最左前缀可以是联合索引的最左N个字段,也可以是字符串索引的最左M个字符

基于以上对最左前缀索引的说明,我们来讨论一个问题,在建立联合索引的时候,如何安排索引内的字段顺序

结论是:利用最左前缀,当有了(a, b)这个联合索引后,一般就不需要在a上建立索引了,因此,第一原则是,如果通过调整顺序,可以少维护一个所以你,那么这个顺序往往就是需要优先考虑采用的

那么回到前面的那个问题,我们要为高频请求创建(身份证号,姓名)这个联合索引,并用这个联合索引顺序来实现按照身份证号查询地址的需求

那么又有一个问题:如果既有联合查询,又有基于a,b各自的查询呢?查询条件里只有b的语句,是无法使用(a, b)这个联合索引的,这个时候不得不维护另一个索引,也就是说你需要同时维护(b)和(a, b)这两个索引

这个时候我们需要考虑的原则就是空间了,比如上面这个市民表的情况,name字段是比age字段大的,那我就建议建立一个(name, age)的联合索引和(age)的单字段索引

索引下推

说完最左前缀以后,你可能会想知道,那些不符合最左前缀的部分,会怎么样

还是以市民表的联合索引(name,age)为例,如果现在有一个需求,检索出表中名字第一个字是张,而且年龄是10岁的所有男孩,那么SQL语句应该这么写

mysql>select * from tuser where name like '张%' and age=10 and ismale=1;

在MySQL5.6之前,只能从ID3开始一个个回表,到主键索引上找出数据行,再对比字段值

而MySQL5.6引入的索引下推优化,可以在索引遍历的过程中,对索引中包含的字段先做判断,直接过滤掉不满足条件的记录,减少回表的次数

”李四“,20”王五“,10“张六”,30“张三”,10“张三”,10“张三”,20
ID1ID2ID3ID4ID5ID6

从数据中可以看出,我们要从ID3开始遍历

MySQL5.6之前是一个一个遍历,回表查看是否符合,具体来说就是,根据索引树上找到,”张六“,30,找到ID3这个主键,然后从ID3开始,回表查找数据。这一步操作要一直到ID6为止

而有了索引下推技术,在索引树上找到一个索引"张三", 30的时候,就会判断是否符合条件,显然age不等于10,所以并不会回表,同理,"张三",20也不会回表

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值