上一篇回顾:
1.一个索引对应一颗B+树,所有的真实记录都是存在叶子节点里面的,所有的项目录都存在内节点或者说根节点上。
2.innodb会为我们的表格主键添加一个聚簇索引,如果没有主键的话数据库是会为我们自动添加row_id这一列的。聚簇索引的叶子节点包含完整的用户记录。
3.我们是可以为自己感兴趣的列添加二级索引的,二级索引的叶子节点没有用户完整的信息,只是拥有对应列和主键的信息,如果想要拥有完整的信息是需要进行回表操作用二级索引找到的主键去聚簇索引寻找完整信息。
4.B+树的每一层节点都是按照索引列的大小信息进行排序而组成的双向链表,每个页里里面的记录也是按照索引列大小信息组成的单向链表。如果是联合索引的话,先按照前面的列进行排序,如果是相同的情况下再根据其他的列进行排序。
5.每个索引的搜索都是从根节点进行的,由于每个页面都按照索引列的值建立了Page Directory,所以在确定了具体页面信息的情况下是可以根据二分法进行快速的定位的。
索引的代价:
1.空间上的代价:每一个索引对应的都是一颗B+树,B+树的每一个节点都对应的是一个16kb大小的数据页,如果是一个很大的数据库的话那么占用的内存还是很大的。
2.时间上的代价:我们在上面讲过,每层节点都是按照数据的大小顺序进行排列的单向链表,每个页也是按照大小排列的双向链表。那么我们在对数据进行操作的时候必然避免不了的就是数据的迁移,数据页的删除,回收,分裂等等,如果我们创建的索引过多的话那么对应的问题就是频繁的需要对这些东西进行操作。那就是浪费时间,给性能拖后腿。
B+树适用的范围:
1:创建一个我们这篇文章需要用到的数据表:
我们创建好表格以后需要注意的问题:
1>我们是没有主键的,那么是由数据库给我们生成主键,然后再根据主键创建聚簇索引;
2>我们自己创建的索引是没有包含country这个列的,所以我们索引的叶子节点只包含name,birthday,phon_num的值以及数据库帮助我们创建的主键row_id;
下面我们给出的就是这个索引的示意图:我们用颜色对内节点以及叶子节点进行了区分,而且我们必须要注意的就是这是根据name先排序,然后再根据birthday、phon
全值匹配:
如果是我们进行查询的数据列和我们索引所有列的顺序都是一样的话,那么我们称之为全值匹配,如下所示的查询:
我们就可以利用索引进行快速的确定name=asiz的位置,然后如果有相同数据的话再根据这个信息进行birthday和phon_num的匹配。因为我们的索引是现根据name进行排序,再根据birthday和phon_num进行的排序。
但是,如果我们要是改变了这个顺序,首先使用birthday进行查询的话,那么就是不能使用这个索引,只能全文检索了。因为我们的索引都是先根据name进行排序的。
所以我们在使用联合索引的时候必须要严格按照顺序,至于里面具体的规则我们下面在讲。
匹配最左边的列:
1>只包含最左边的一个列:如下图所示,这样也是可以使用到我们的联合索引的
2>包含左边的多个列:如下图所示,这样操作也是没问题的
3>如果我们在查询的时候没有使用到最左边的name列,如下图所示,这是不能使用索引的,只能进行全文的检索
注意:
所以我们在使用联合索引的时候,务必需要记住的就是一定要使用到第一个列,因为我们的索引就是按照第一个列最先开始排序的,如果不按照这个规则进行,那么我们是不能使用到索引的。而且,就如我们最后一条查询而言,我们在进行完成name的索引以后,在相同情况下进行phon_num的查询的时候是不能使用索引的,因为name完成以后是根据birthday进行的索引排序,所以一定要严格按照索引定义的顺序进行查找。
匹配值前缀:
1>如果我们在进行字符串的搜素的时候是没有必要输入完整的字符串的,就好像我们的模糊查询,我们只需要输入字符串的前面字母即可得到筛选的结果,因为B+树是现根据name进行排序的,我们只使用前面的部分字符也是可以进行二分查找迅速定位。
2>如果我们给定的字符是位于字符串中间,那么这样是不可行的,是不能使用索引的,只能进行全文的检索,如下图所示:
范围匹配:
1>我们的索引也是可以应用在范围查询里面的,如下图所示,因为我们的数据都是在页内按照单向链表进行排列,页之间是按照双向链表进行排列,所以是可以很快速获取到我们需要的数据:
2>但是我们在使用多个列的范围查找的时候我们只能使用到的是第一列的索引,但是其他列的索引我们是使用不到的,因为我们是根据查询出来的结果在不同的name里面在进行birthday的筛选,索引是根据相同name的条件下才对birthday进行排序的,如下图所示:
精确匹配某一列并范围匹配某一列:
对于同一个索引来说,我们使用多个列的范围查询的时候,只能使用最左边列的B+树,其他列是不能使用的。但是我们左边使用的是精确查询,右边使用的是范围查询,那么,我们的右边也是可以使用到B+树的,如下图所示:
我们分析一下上图:
1>第一部分的name进行的精确匹配当然是可以使用到索引的
2>因为我们name是一样的,和索引的排序规则一致,所以birthday的范围搜索也是可以使用到B+树的
3>因为birthday的范围进行不同查找的结果,所以我们在进行phon_num的查找的时候是不能使用B+树的。
用于排序:
我们在使用排序比如说Order by的时候也是可以使用到索引的,如下图所示,具体的规则和我们进行查询的时候是一样的,因为我们索引就是按照顺序已经进行好排序的,所以如果我们的排序的顺序和索引的顺序是一致的,那么完全没问题可以直接取用数据,但是就是一直强调的问题,如果我们列的顺序改变了们就不能在使用B+树了。
用于分组:
如下所示,我们在使用group by的时候需要进行分组,这个过程包含了三个部分,第一个是先对name一致的进行分组,第二个在着基础上在对birthday一致的进行分组,然后最后在基础上对phon_num一致的进行分组。这就正好和我们的索引是一致的,所以是可以使用到B+树的,和上面一样,我们的顺序问题是坚决的不能乱的。
索引的挑选:
1>必须条件:只为我们使用到的查询条件,分组,排序列创建索引。查询列表里面的列我们没有必要建立索引。
2>基数考虑:如果一个列的差异数据不是很多,我们称之为基数小的列。也就是说所有数据的这个列的数据大部分都相同,那么就是基数小,这种列没必要创建索引。
3>数据类型:我们知道的是索引列可以有很多的数据类型,比如说整形数据我们就有TINYINT、MEDIUMINT、INT、BIGINT,它们所占用的空间内存肯定是不一样的,所以我们挑选数据类型小的类型作为索引列的数据类型,可以有效的节约空间,储存更多的数据,那么我们在进行数据取用的时候一次可以加载更多的数据进入内存,减小IO损耗,同时在CPU层次来说,数据类型越小,查询处理的速度是越快的。
4>索引字符串的前缀:这个问题我们在上面其实提到过,我们在使用索引的时候是可以的,那么在创建索引的时候当然也是可以的,这样可以减少很多的内存空间,e而且我们在做字符串比较的时候如果我们使用的是前缀那么比较的时间也是可以大大进行缩短的。具体的语法如下:
5>尽量使用联合索引:因为我们的每一个索引对应的都是一颗B+树,需要使用时间和空间进行维护的,我们文章开始就说了索引需要付出的代价。我们使用联合索引,是可以满足很多字段的索引条件的。
6>主键插入的顺序:记不记得我们在上边说的,索引的一个目录项对应的是一个页,我们的数据都是有序的进行单向链表的维护,那么如果我们的主键在后期插入中间的话就涉及到了位置的移动,目录项的修改,页面分裂,数据迁移等等问题。所以我们建议的是让数据库给主键进行自增生成。
7>避免冗余重复的索引:不要为一个列重复的添加多个索引,这样是不好的,他对效率的提升没有半点的帮助,但是对空间的消耗确实实打实的。
8>覆盖索引:比如我们开始创建的索引是没有包含country这个列的,如果我们如下图所示进行查询,我们本来是可以在索引直接得到三个列的数据,但是差一个列,这时候就必须用主键去聚簇索引进行回表操作了。所以我们查询的列最好都是我们索引的列,也就是说我们是鼓励把需要查询的列明确进行书写的。