上篇文章索引的代价,b+树占的空间比较大,增删改对b+树每个节点的索引排序影响也很大,事件耗费长,所以没有必要不要乱建索引,还介绍了索引的左原则和全值查询。
匹配列前缀
innoDB给其他列添加二级索引,会按列给他排序,不管是页之间的双向链表排序,还是页内数据槽点的单向列表排序,都是按列值排的。比如name列,如果名字有TOM,Anny,Aone等,1)先按首字母给他排序。2)首字母都是A一样,则按第二个字母给他排序。3)依次类推,一样就用后面一直给他排序。所以我们用模糊查询的时候where name like ‘a%’;这种百分比号放在后面是可以触发索引查询的,但若是where name like ‘%a’;这种因为个字母找不到,这种没有排序好,导致只能全盘扫描,放弃索引查询。
举个例子,当我们有字段列url,分别有数据,www.baidu.com,www.google,www.gov.com,www.wto.com。
这时候我们想查询com结尾的,如果用where url like ‘%com’;则效率会非常低,无法触发索引查询。那我们为了提高效率,用索引来查询数据,可以先把表的数据逆排序一下,改为,moc.udiab.www,moc.elgoog.www,nc.vog.www,gro.otw.www,这时候就可以用where url like ‘com%’;来触发索引查询。
匹配值范围
我们看idx_name_birthday_phone索引b+示意图,所有记录都是按索引从小到大进行排序的,比如我们用where name > ‘Anny’ and name < ‘barlow’;所以这个查询过程:1)先在b+树叶子节点找到name值大于Anny的二级索引记录,读取主键,在用聚簇索引进行回表查询操作,获取聚簇索引的全部用户记录数据后发给客户端。2)根据上一步找到的记录,沿着所在的链表位子(页之间双向链表关联,页内数据,单向链表关联)查找下一条二级索引记录,判断该记录是否<barlow,如果符合,则返回给客户端。3)重复2的操作,直到不符合。
所以,这时候会使用索引查询的,但重点需要注意,注意,注意(重要的事要说三遍):如果对多个列进行范围查询,只有索引左边的那个列查询时候会使用到b+树的索引进行查询。
比如where name > ‘Anny’ and name < ‘barlow’and birthday > ‘1990-01-01’;这时候查询过程:1)和前面说的一样,先name在b+树叶子节点找到列>Anny的主键,用主键查询聚簇索引的树回表操作,查询完之后,在查询下一个是否满足<barlow。2)对于上面name不同的值,在进行birhday进行birthday>’1990-01-01’过滤。
这样查询对于联合索引来说,只会name的时候用到了索引排序,而因为biryhday排序的条件是需要先name排序相同才会排序,此刻获取的name都是不同的,所以在birthday范围查询的时候无法触发索引。
到某一列,范围另外一列
当我们用where name = ‘Anny’ and birthday > ‘1990-01-01’ and phone > ‘13200000000’;这时候我们查询过程就是:1)name肯定使用b+树的二级索引先查询到叶子节点的列值加主键,再聚簇索引回表操作返回聚簇索引叶子节点的全部数据。2)因为name相同的情况下,birthday会触发索引查询,先在b+树叶子节点找到>’1990-01-01’的列值和主键,在通过主键回表查询全部数据3)因为phone使用索引查询的前提是birthday相同,而前面的是不同的birthday,索引phone不能使用索引查询。
文章来源:知乎平台 原文地址:https://zhuanlan.zhihu.com/p/403531069