上一篇我们讲了MySQL索引的原理,主要讲MySQL是如何维护索引字段的。简单回顾一下:MySQL维护主键索引最简单,就是根据主键维护一个B+树,因为主键的特性一般是递增的,也就是有序的,所以MySQL只需要维护时依次向数据页添加记录,当数据页满时继续添加到下一个数据页。并且每条记录都是完整的,即维护了所有列的值。但是对于非主键索引,在维护B+树时,会依次根据联合索引的字段进行判断。假设联合索引为:姓名+地址+年龄,那么MySQL在维护索引的B+树时,会先按照姓名排序。如果名称相同,则按照第二个地址排序。如果地址相同,则按照年龄排序,如果年龄相同,则按照主键字段值排序(主键不能相同),而对于非主key索引,MySQL在维护B+树时,只维护索引字段和主键字段。另外,B+树的结构大致是这样的:这里的数据维护过程就不详细说了,不清楚的朋友可以看一篇文章今天我们就来看看MySQL查询的基本原理。因为只有在理解原理的基础上,你才能写出符合预期的SQL,然后你才能知道你的SQL是否使用了索引。这是最基本的原则。因为这篇文章讲的是一些原理,很多东西不是很好画,但是如果我会画,我一定会画给大家的。等价匹配的原理我们已经知道,如果是【主键索引】,插入数据时,是按照主键的顺序依次排列的。如果一个数据页不够用,就会拆分成另一个数据页,然后通过索引页来维护数据页。数据页通过双向链表维护。如果索引页太多,它们会向上分裂(如上图),以此类推,从而形成一个由组件组成的B+树结构,即[AggregateClusterindex]但问题是我们不仅有建立了主键索引,也建立了非主键索引。此时如何维护非主键索引呢?因为主键索引不可能重复,所以在保存到数据页的时候直接追加插入(我们默认的主键是自增的)。至于非主键,一般是可重复的,假设此时一个联合索引字段的值真的都一样,那么怎么办呢?上文开头提到,此时只能按照主键字段排序,这也是为什么非主键索引记录在保存的时候还要保存一个主键字段的原因。另外,如前所述,如果创建过多的索引,会占用过多的空间,因为MySQL会为每个索引维护一个B+树。毕竟非主键字段一方面不一定是增量的,有可??能是Repeated。所以基于此,那些频繁增删的字段肯定不适合做索引。好吧,还是要回到刚才说的name+age的联合索引。假设我们现在有一个SQLSELECT*FROMstudentWHEREname='wx'ANDage=1。像这样WHERE后面的条件是一个联合索引,在联合索引中。字段顺序排列,全部使用等号条件,我们称之为:等价匹配;这是一个非常重要的原则。最左前缀匹配原则假设有这样几条记录:classId=1,name=wx,age=1,id=1;classId=1,name=xq,age=2,id=2;classId=1,name=wx,age=1,id=3;classId=2,name=zs,age=3,id=4;根据上面提到的(classId,name,age)联合索引,他们是这样存储在数据页中的。首先按classId字段值排序。如果classId字段值相同,则按照secondname字段值排序。如果name字段的值相同,则按照age字段的值排序,如果age字段的值也相同,则按照primary的值排序关键领域。然后在查找的时候,因为你当前的条件是classId和name,MySQL可以通过classId快速定位到一批数据。因为这个条件是MySQL维护B+树的第一个条件(即先按classId排序),然后同理,name是MySQL维护B+树的第二个条件(即按name排序),所以即使你的age条件不是Add,使用classId和name的索引肯定没问题,但是如果你这样查询SELECT*FROMstudentWHEREage=1,就不行了,因为MySQL会基于你创建的联合索引.首先按classId查询,然后按名称查询,然后按年龄查询。如果直接跳过前两个字段,这和全表扫描没什么区别,因为此时MySQL无法确认age在哪,只能一个一个扫描。同样,如果你的WHERE条件后面是classId=xx,那么age=xx,此时classId可以使用索引,因为B+树维护的第一个字段就是classId。但是age不能用于索引查询,因为无法定位到name,所以此时只能根据满足classId的记录再做一次全量扫描。这个规则叫做:最左前缀匹配原则;如果对最左匹配原理不理解,我打个比方再介绍。假设由classId、name和age组成的联合索引就像三层。Floor,classId为第一层,name为第二层,age为第三层。假设你要上三楼,是不是要从一楼爬,再爬二楼,再爬三楼;爬到一层就可以了,剩下的两层不爬也没关系,也就是说只用classId做等价查询就可以了,不用也没关系其余字段;同样,你可以从第一层爬上去,然后爬到第二层而不是第三层。就好比你用classId,name去查询,也可以从第一层爬到第二层再到第三层,也就是你依次用classId,name,age这三个字段做一个等价查找。到目前为止一切正常。但是如果不想爬一层,想跳过一层直接从二层开始爬,可以吗?显然是不可能的,也就是说在查询的时候跳过classId,直接查询name,这样索引就根本用不上了。调整名字后查询年龄也是如此。可以跳过第一层和第二层,直接从第三层开始。也就是说调整classId和name后直接查询age是不能使用索引的。现在你应该完全理解最左匹配的原理了吧?需要满足以下原则的最基本条件:最左前缀匹配原则。RangeSearchRules范围搜索规则,相信这也是最常用的原则,比如下面的SQLSELECT*FROMstudentWHEREclassId>1ANDclassId<4因为此时B+树中的数据是由联合索引(classId,name,age)是按照classId,name,age排序的。所以这时候可以根据classId查询一个范围内的数据。虽然它们可能不在同一个数据页,但是正如我们所说,数据页是通过双向链表连接起来的。所以这时候对于classId的范围搜索还是可以通过索引走的。继续看条件是不是SELECT*FROMstudentWHEREclassId>1ANDclassId<4andname>aANDname
