数据库索引创建与优化 (4)

       图中最开始是没有id为10,12和20的数据的,此时磁盘数据是按照id顺序组织的,当我们插入id为10的数据时,发现第二个叶结构上有多余的存储空间,因而10号数据插入到了该位置,然后插入id为12的数据时,发现已经没有多余的空间存储数据了,此时MySQL会顺序往下查找最近的可用地址空间,并创建一个新的页结构,将id为12的数据插入到该页中,并且为了维护B+树叶结构的顺序性,第二个数据页上指向下一个叶结构的指针将指向新创建的数据页,并且新数据页的指针将指向第三个数据页,上面的过程称为页分裂。页分裂造成的问题在于在进行磁盘数据顺序扫描时磁头先定位到第一个数据页,当读取完前两个数据页的数据时,磁头需要再次定位到新的数据页,读取完新数据页数据之后再定位回第三个数据页,这样磁头因为一次页分裂到额外多出了两次磁头的随机移动,这将对数据库的性能造成极大的影响。

       从上面的分析可以看出,当数据按照我们所指定的方式聚集时,如果插入和更新数据比较频繁,那么将会导致大量的数据页分裂,会大大的影响数据库效率。MySQL默认使用主键作为聚簇索引,使用主键有一个非常大的优点在于主键都是自增的,每次新插入的数据只会在磁盘的尾部,也就不会出现页分裂的问题。对于更新数据,其造成的磁盘存储空间影响较小,因而造成页分裂可能性较小。从这一点看来,如非特别必要,尽量不使用自定义聚簇索引,并且使用数值型的id作为主键(使用UUID将导致新数据往磁盘数据中间插入)。另外,如果磁盘数据组织较为混乱,定期使用”optimize table table_name“命令进行数据重组将有利于查询效率的提升。

2.2 联合索引

       联合索引指的是多个字段按照一定顺序组织的索引。如下表所示为一个联合索引的示意图:

联合索引

       对于图中的索引(name, city, gender),其首先是按照name字段顺序组织的,当name字段的值相同时(如Bush),其按照city字段顺序组织,当city字段值相同时,其按照gender字段组织。根据前面的讲解,联合索引也是一种二级索引,因而其叶节点保存有聚簇索引的id值,如表中最后一列所示。

2.2.1 where条件联合

       所谓where条件联合指的是,对于where条件中的等值条件,其字段使用与联合索引的字段一致(顺序可以部一致),比如下面的查询:

SQL语句: select * from actor where first_name='qWhNIZqxcbD' and last_name='rxkPMBqBvWnzbJe';

索引 first_name first_name, last_name
耗时   0.24   0.01  

       可以看到,这里的联合索引(first_name, last_name)就是一个联合索引,其将该查询所使用的两个字段都覆盖了。通过与只使用单字段的索引比较,发现覆盖率越高的联合索引,其查询效率有一个质的提升。

2.2.2 order by条件联合

       order by联合指的是如果order by后面的字段是联合索引覆盖where条件之后的一个字段,由于索引已经处于有序状态,MySQL就会直接从索引上读取有序的数据,然后在磁盘上读取数据之后按照该顺序组织数据,从而减少了对磁盘数据进行排序的过程,如下表是磁盘有序结构的一个示意图:

有序联合索引

       从表中可以看到,对于first_name相同的数据,其last_name的值是按照从小到大的顺序排列的。作为比较,我们可以对比如下SQL查询:

SQL语句: select * from actor where first_name='rMqChueZJThP' order by last_name;

索引 first_name first_name, last_name
耗时   0.24   0.14  

       可以看到,如果只使用单列索引,其耗时比使用覆盖order by的联合索引耗时高一倍左右(实际使用时可能更高)。如下是这两个查询的执行耗时:

覆盖order by索引

       左图为未覆盖order by的查询,右图则为覆盖order by的查询。可以看到对于未覆盖order by的查询,其有一项Creating sort index,即为磁盘数据进行排序的耗时最高;对于覆盖order by的查询,其就不需要进行排序,而其耗时主要体现在从磁盘上拉取数据。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zyxxpj.html