mysql学习笔记4

53 阅读7分钟

B+树索引的使用

在笔记3中我提到了B+树来作为InnoDB引擎的查询结构。本文将会来讲一下如何使用B+树索引。

前提

我们先建立一个简单的表结构,该表有A,B,C,D四个字段,其中A是主键,B,C,D三个键一起组成一个联合二级索引。

这样Mysql就会建立两个索引,分别是A和B,C,D两个,A只有一个字段,所以在排序的时候就会按照A的顺序进行排序,B,C,D是三个会按照建立联合索引提供的顺序进行排序:先按照B的值进行排序,当B的值相同的情况下会按照C的值进行排序,在C的值相同的情况下再按照D的值进行排序。

查询

全值匹配

全值匹配是指我们查询条件中的列和索引的列一致的情况。

比如说:select * from table where B='12345' and C = '小美' and D= '789'

从上面提到的索引的构建顺序可以知道,在进行全职匹配的时候也会按照索引的构建顺序进行匹配查询,先查找与B字段相同的记录,再从这些记录中查找与C字段相同的记录,再然后就是D字段。

由于在sql语句执行的过程中有优化器的存在,我们在进行sql语句编写的时候查询条件的顺序可以不必与索引的构建顺序完全一样,比如说:select * from table where C = '小美' and D= '789' and B='12345' 这样的语句也是OK的。

最左匹配

在查询的时候我们提供的查询条件可能并不会完全匹配上我们创建的索引字段。这种时候就可能会触发最左匹配原则。

也就是我们的查询的条件只要是有索引构建顺序的前几个,就能走这个索引。因为构建索引是时候是按照这个字段从左到右提供的字段来构建的,构建的方法就是上面提到的。那么在我们进行查询的时候就会按照顺序走索引。

比如:select * from table where B='12345' and C = '小美' 这条语句查询条件是B,C。就可以使用我们构建的B,C,D索引,先找B,在找C。只查询B也是这样。查询到主键值后再回表查询所有的数据。

但如果我们查询的条件是这样:select * from table where B='12345' and D= '789' 这种情况下只能使用索引查询到B,因为排序是先按B排序的,B排完内容相同才会按C的值来排序,之后再是D。我们的查询条件是B和D,查询找到B的之后无法继续走索引了。这就是索引构建造成的,在B相同的所有记录中看,D是无序的,因为只有在C相同的情况下才会对D排序。所以这种查询条件只能走部分索引。也就是查询时会按照从左到右的顺序走索引查询直到遇到查询条件的中断。

匹配列前缀

在索引构建的时候字符串进行的大小比较是这样的:先比较第一个字符的大小,如果相等再比较第二个字符,以此类推。

也就是说这些字符串的前n个字符,也就是前缀都是排好序的,所以对于字符串类型的索引列来说,我们只匹配 它的前缀也是可以快速定位记录的。比如说对某个字段的查询条件是:B like 'A%'

但如果我们要对中间部分进行查询就无法使用索引了,B like '%A%',这种情况。因为索引的构建是从前向后比较建立的,我们都不知道前面的部分是什么,也就无法使用索引来进行查询了。

范围查询

因为索引在构建的时候已经排好序了,所以我们在进行范围查询的时候就可以很便利的使用索引了。

需要注意的是如果是对联合索引进行范围查询的时候,只有最左侧的构建字段可以使用范围查询。理由和最左匹配原则一样,我们只能找到第一个字段的范围,第一个字段是范围查询,后续字段从整体上看是无序的,也就是我们后续找的记录内容并不是按照第二个字段来进行排序的,那么也就无法使用范围查询了。

查询总结

总结一下上面提到的用法,我的结论是这样的。对联合索引进行查询,可以从左一直向右,直到走完所有的字段,或者是遇到了不能确定排序的情况(比如中断或者范围查询)。

排序

在我们使用Order By 进行排序操作的时候,一般会把数据从磁盘中转移到内存上,再通过各种排序算法进行排序,如果中间的内容过多内存空间不够还可以把中间结果存放到磁盘中暂存,处理完后再把数据返回。但如果我们排序的对象是我们索引的列的话就可能能省略排序操作。比如说:select * from table order by B,C,D Limit 10

原因还是和上面一样,B,C,D是建立过索引的,所以在索引上数据的B,C,D是有序的,那么我们就可以直接把记录拿过来用就好了。

排序的失效情况

1、ASC和DESC混用

如果ASC和DESC混用,那么就不能再使用联合索引来排序了,因为如果单用ASC和DESC那么读数可以直接从最左或最右侧读取数据,而两种混用的情况:select * from table order by B ASC,C DESC Limit 100。这样就会先从左开始查找数据到需要的部分,再从该部分最右开始读数,如果数据量不够100,那么还需要到B的下一个值再重复上面的操作,直到数据量够100为止。这样需要额外的算法来辅助才能实现,所以索性就不让这种查询可以使用索引了。

2、查询条件里有非索引列

这种情况下要先按照查询条件筛选出数据来,所以无法使用这个索引来进行排序,因为不知道非索引列的情况,使用该索引需要一条条回表查询筛选。

回表

当我们使用二级索引进行查询的时候有可能会进行回表操作进行查询,这个回表操作就是根据二级索引的查询结果去主键索引上查找数据的过程。在使用二级索引的时候,我们查询的数据是按照二级索引的排序来进行查询的,这部分是内存连续的,所以我们的查询效率高,但查询到结果后我们进行回表操作,这部分查询到的数据是按照主键来进行排序的,这样我们通过二级索引查询的数据到了主键索引这里又是无序的了,这样就会影响我们的查询速度。查询的数据越多我们回表的操作也就越多,查询时间的影响也就越大。

为了避免回表操作,我们一般使用索引覆盖来进行查询,也就是我们的查询结果已经在二级索引上了,这时候就不需要进行回表操作了,效率也就提升了。

如何挑选索引

  1. 为知会进行查询、分组、排序的字段创建索引。
  2. 考虑列的基数,同一列的值,不同的内容越多,基数越大,查询的效果越好
  3. 构建索引的列的数据类型大小要小,因为大小小的话再cpu上处理的速度就快,而且内存可以存放更多的数据,加快读写效率。