Mysql索引

    当数据库记录达到一定规模时,常规的查询将会耗费非常多的时间,这对于瞬时性能要求较高的系统来说是无法接受的。为了提高查询的速度,人们设计了索引来辅助查询。在查询的时间消耗上,主要包括内存操作耗时和磁盘IO耗时,而内存操作比磁盘IO操作效率高得多,因而磁盘物理记录的检索占用了绝大部分的查询时间。根据磁盘IO的原理,人们又设计了B树和B+树来尽可能的减少IO操作。所以在讲到索引的时候,都会涉及到磁盘IO原理,以及B树和B+树。

1.什么是索引?

    在查找数据时,很自然就会想到利用一定的查找算法来提高查询效率,例如二分查找等。但是特定的算法对数据结构都有一定的要求(例如数据本身有序等),一种数据结构不可能满足各种查找算法的这种要求。对于数据库来说,为了满足高效查询的要求,数据库就需要维护一定的数据结构,这种结构就是索引。而作为一种数据结构,索引是需要占用磁盘空间的。数据库在存储数据记录的同时,还需要存储索引结构,会消耗额外的磁盘空间。

2.B树和B+树

    首先需要指出,按照研究者July的说法,目前许多地方的所说的B-树和B+树,实际上就是B树和B+树,只是由于国外的文献记称为B-Tree,翻译过来时人们习惯性的叫做B-树,指的就是B树。说实话,我自己就经常弄混,看到July的说法觉得挺有道理的,就借用了。

    这里不打算深入讲解B树和B+树,只是大致上说一下他们的特点。

    B树和B+树都是多路搜索树,叶子节点具有相同的深度,同一个节点内的关键字按照非降序排列。

    B树的每个节点大小都相同,除根节点外,每个节点至少包含[m/2-1]<= m <= m-1个关键字,其中m称为B树的度,[]为向上取整函数。而B+树的节点大小不一,非叶子节点不携带数据信息,只存放索引key值,数据信息都放在叶子节点。同时B+树可以的叶子节点的指针可以指向下一个叶子节点,在进行范围查询时,只需要沿着叶子节点就可以查到,非常高效,这是B树无法做到的。

    一棵含有N个总关键字数的m阶的B树的最大高度是h=log[m/2](N/2)+1,因此增加B树的度可以降低树的高度,一个节点内关键字的个数与节点大小和key的大小有关。由于磁盘预读的特性,一个节点一般设计为一个页的大小,是固定的,因而增加m的值只能减少key的大小。B树的每个节点既包含key,也包含value值,而B+树的非叶子节点由于不携带value,每个关键字会小很多,所以可以使得m更大,因而B+树比B树更适合作为文件索引结构。

3.磁盘IO原理

    磁盘是由主轴,磁头,盘片、磁道、扇区等部分组成,磁道就是一个个的同心圆环,由一个个相互分割的扇区组成。磁盘绕主轴旋转时,与磁头之间形成了一个柱面。在读取数据时分为以下几步:①移动机械臂找到对应的柱面号,称为定位或查找;②根据盘片号找到对应的盘片;③根据磁道地址旋转盘片,找到对应的扇区。这个过程中,步骤①的查找耗时最多,步骤③耗时与磁盘转速有关,其他的耗时还有读取耗时(将磁盘记录读取到内存中)。后续的操作将在内存中进行,而内存操作是相当快的,主要的耗时就集中在磁盘IO操作上。为了减少磁盘IO次数,磁盘每次在读取数据的时候,并不会严格的按需读取,会有一定的预读行为,即多读取一点记录。这依据的是著名的局部性原理:如果一个数据被用到,那么其周围的数据很有可能也会被用到。磁盘每次预读的数据大小是页的整数倍,因此一个页内数据会在一次IO中全部读取到。

    因此,人们在设计索引时,将每个节点的大小就设置为一个页的大小,这样每个节点只需要一次IO操作即可全部读取到内存中。所以对于一颗高度为h的B树,只需要h次IO操作即可完成遍历,非常高效(实际上,由于需要频繁进行查找,因此树的前几层会常驻内存,使得磁盘IO次数进一步降低)。而B+树拥有比B树更小的高度而性能更好,所以目前MySQL的索引都是基于B+树的。

4 MySQL存储引擎与索引

    索引是与存储引擎相关的,不同的存储引擎提供的索引结构有较大的不同,因此在讨论索引的时候,需要区分不同的存储引擎。MySQL最常见的存储引擎是MyISAM和InnoDB。

4.1 MyISAM索引结构

    MyISAM引擎提供的索引称之为非聚集所以,这样称呼是为了与InnoDB的聚集索引进行区分。MyISAM索引底层使用B+树(B+树的特点是非叶子节点不存储数据,只记录用于查找的key值),叶子节点存放的是实际数据的物理地址。因此MyISAM除了保存数据文件之外,还保存着单独的一份索引结构。在MyISAM中,有主索引和辅助索引之分,主索引要求索引key是唯一的,而辅助索引的key可以重复,除此之外二者没有差别。

4.2 InnoDB索引结构

    InnoDB使用的索引称作聚集索引,虽然也采用B+树作为索引结构,但与MyISAM有着很大的不同。最重要的区别是,InnoDB的叶子节点本身存放的就是实际的物理数据,它既是索引,也是数据记录,而MyISAM的索引和数据记录是分开的。第二点区别是,InnoDB的辅助索引会在叶子节点记录主索引的key值,这意味着使用辅助索引检索记录会进行两次查询,先查找辅助索引,找出主索引的key,再查找主索引,找到对应的值。

    需要注意的是,InnoDB的聚集索引结构是以主键聚集,因此要求数据表必须存在主键。如果数据表有主键,就在该主键列上建立索引;如果没有主键,则会在所有列中选择一个具有唯一性的列来建立索引;如果不存在这样的列,MySQL会自动创建一个隐含的主键,是一个6字节的长整数。

    前面谈到,数据库在设计索引时,会将每个节点设计成一个页的大小。节点内的数据是经过排序的,各个节点依次存放,保证了逻辑上相近的节点在物理上也是相邻的,这种做法使得沿着叶子节点遍历数据非常高效。因此在没有特殊要求的情况下,一般建议使用自增字段作为主键,这样磁盘就会按顺序存储数据记录,减少磁盘碎片的产生。但是在使用随机字段的情况下,在插入数据时,经常需要移动磁盘记录从而将新插入的数据存放到合适的位置,这会产生额外的开销,并会增加产生磁盘碎片的概率从而影响到数据库性能。

5.使用索引需要注意的问题

    索引能够显著提高查询效率,而数据库为了维护索引结构需要额外的磁盘空间,并且数据库在进行增删改操作时需要动态更新索引结构,从而影响增删改的效率,因此索引并不是越多越好。

    1)建议使用索引的情况(未完待续。。。)

      ①业务以查询为主,增删改操作占比不高。这种情况下,查询的效率是应该重点关注的,可以适当使用索引。

    2)不建议使用索引的情况(未完待续。。。)

        ①数据量较少,此时使用索引并不能明显提高查询效率,反而会影响增删改等操作的效率。

        ②业务以修改数据(增删改等)为主,使用索引会明显降低业务效率。

    索引是建立在列上的,分为单列索引以及组合索引。在了解了什么情况下需要索引后,还需要了解哪些列适合建立索引,哪些列不适合建立索引。

    1)适合建立索引的列

        ①经常需要进行排序操作的列可以建立索引,例如学校经常更加考试成绩对学生进行排名,那么就可以在考试成绩这一列加上索引。

        ②需要保证唯一性的列,加上唯一索引约束可以避免该列数据重复的情况。

        ③作为外键的列,建立索引可以提高连表查询的效率。

        ④经常需要作范围查询的列,建立索引可以提高查询效率。

    2)不适合建立索引的列

        ①不经常被用到的列,由于不经常用到,建立索引也不会明显提高查询效率,相反还会增加增删改的时间成本。

        ②选择度不高的列不建议使用索引,所谓选择度=count(select distinct(colname))/count(*),即列中不重复的记录所占的比例,取值范围我[0,1],选择度越低则该列数据重复率越高,使用索引将导致查询到大量的记录,从而造成效率不高。

        ③类似text和bit类型的列不适合建立索引,这是因为这些列一般较长,会造成索引结构占用的空间急剧膨胀,不利于提高效率。

6.【参考】

从B树、B+树、B树谈到R 树 - 结构之法 算法之道 - CSDN博客

 MySQL索引背后的数据结构及算法原理

MySQL索引原理及慢查询优化 

原文地址:https://www.cnblogs.com/NaLanZiYi-LinEr/p/7430194.html