当前位置: 首页 > 科技观察

MySQL遵循最左前缀匹配原则!面试官:回去等通知

时间:2023-03-13 13:00:23 科技观察

我们都知道在MySQL的Innodb引擎中,索引是通过B+树实现的。不管是普通索引还是联合索引,都需要构建B+树索引结构。那么,我们都知道普通索引的存储结构中B+树的每个非节点上记录的索引值,而簇索引(主键索引)的值记录在这个B+的叶子节点上树。比如:那么,如果是联合索引,这个B+树是怎么存储的呢?在联合索引中,联合索引(name,age)也是一颗B+树。非叶子节点记录name和age这两个字段的值,叶子节点记录name和age这两个字段的值以及主键id。.在存储过程中,如上图所示,年龄不同时按年龄排序,年龄相同时按姓名排序。因此,了解了索引的存储结构后,我们就很容易理解最左前缀匹配:因为索引的底层是B+树,如果是联合索引,在构造B+树的时候,会先按左边的key排序,当左边的key相同时,再按右边的key排序。所以在通过索引查询的时候,也需要遵守最左前缀匹配的原则,即从联合索引的最左边开始匹配。这时候要求查询语句的where条件包含最左边索引的值。了解了最左前缀匹配后,在日常工作中,我们在建立索引和查询简历时,往往会根据这个默认约定来设计索引和优化SQL。大家默认MySQL必须按照最左边的前缀匹配。会认为存在age和name的联合索引时,如果查询语句中不包含age作为条件,则一定不能使用该索引。MySQL必须遵循最左前缀匹配。这句话之前是对的,没有错。但在MySQL8.0中,就不一定了。IndexSkipScanMySQL8.0.13版本引入IndexSkipScan优化范围查询(什么是范围后面会讲到),支持不满足组合索引最左前缀原则的SQL。能够使用复合索引来减少不必要的扫描。让我通过一个例子向你解释。首先有如下表(参考MySQL官网的例子,不过我做了一些改动和优化):通过上面的SQL,先创建一个t1表,并将f1,f2两个字段设置为联合指数。然后向其中插入一些记录。分别在MySQL5.7.9和MySQL8.0.30上执行:执行结果如下:可以看到,主要有以下区别:在MySQL5.7中,type=index,rows=160,extra=Usingwhere;UsingindexinMySQL8.0,type=range,rows=16,extra=Usingwhere;Usingindexforskipscan这里的type是指扫描方式,range表示范围扫描,index表示索引树扫描。通常,范围需要比索引快得多。从行数也可以看出,使用索引扫描的方式一共扫描了160行,而使用范围扫描的方式只扫描了16行。那么,重点来了,那就是为什么MySQL8.0中的扫描方式可以更快呢?主要是因为Usingindexforskipscan说明他使用了indexskipscan技术。也就是说,虽然我们的SQL没有遵循最左前缀原则,只使用f2作为查询条件,但是经过MySQL8.0的优化,仍然是通过索引跳过扫描来使用索引。优化原则MySQL8.0.13及之后版本,SELECTf1,f2FROMt1WHEREf2=40;SQL执行过程如下:获取f1字段的第一个唯一值,即f1=1。构造f1=1和f2=40进行范围查询。获取f1字段的第二个唯一值,即f1=2。构造f1=2和f2=40进行范围查询。f1字段的所有唯一值都扫描完了,最后将结果合并返回。也就是说,最终执行的SQL语句如下:也就是MySQL优化器帮我们查询联合索引中的f1字段作为查询条件。限制在了解了索引跳跃扫描的执行过程后,很多聪明的读者会发现,这种查询优化更适用于f1的取值范围比较小,判别度不高的情况。一旦f1的判别度特别高,这样的查询可能会变慢。因此,是否使用索引跳跃扫描取决于MySQL优化器的成本估算。所以这种优化一般用在联合索引中第一个字段不是高度区分的情况下。不过话又说回来,我们一般不会把区分度低的字段放在联合索引的左边,但是也没有绝对,既然MySQL已经给出了优化方案,说明还是有这样的需求。但是,我们不能依赖他的优化。在建立索引时,应优先将差异化程度高、查询频率高的字段放在联合索引的左侧。另外,MySQL官网上也提到索引跳过扫描还有一些其他的限制:表T至少有一个联合索引,但是对于联合索引(A,B,C,D),A和D可以为空,但B和C必须非空。查询必须只依赖一张表,不能多表JOIN。查询中不能使用GROUPBY或DISTINCT语句。要查询的字段必须是索引中的列。原文地址:https://mp.weixin.qq.com/s/IYRTE00_3bXD6y3YBW9P6Q