MySQL索引原理浅析

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/heroqiang/article/details/88783341

关于B+树的思考

我们都知道,MySQL索引的数据结构是B+树,通常我们衡量一个数据结构的好坏主要是通过时间和空间两个维度,空间我们暂且不谈,时间来看,B+树查询的时间复杂度为O(log(n)),这样来说,比B+树查询速度快的数据结构并不少见,例如一个设计合理的hash查询的时间复杂度通常为O(1),那么为什么MySQL不用hash作为索引的数据结构而用B+树作为索引的数据结构呢?

认识B+树

B+树是B-树(B树)的一个变体,B-树是一种多路自平衡的搜索树,它类似普通的平衡二叉树,不同的一点是B-树允许每个节点有更多的子节点。B-树有如下特点:

  • 所有键值分布在整颗树中
  • 任何一个关键字出现且只出现在一个结点中
  • 搜索有可能在非叶子结点结束
  • 在关键字全集内做一次查找,性能逼近二分查找

图片摘自网络

前面说到B+树是B-树的变体,它也是一种多路搜索树,它与 B-树的不同之处在于:

  • 所有关键字存储在叶子节点出现,内部节点(非叶子节点并不存储真正的 data)
  • 为所有叶子结点增加了一个链指针
    图片摘自网络

这里提到一个叶子结点的概念,叶子结点就是没有子节点的节点,在索引中B+树的叶子结点中的内容存储的是指向行的指针(在主键索引中叶节点保存的值)

对于一个m阶(m的值就是当前树中拥有最多子节点的个数)的B树需要满足以下条件:

  • 每个结点至多拥有m棵子树
  • 根结点至少拥有两颗子树(存在子树的情况下)
  • 除了根结点以外,其余每个分支结点至少拥有m/2棵子树
  • 所有的叶结点都在同一层上
  • 有 k 棵子树的分支结点则存在k-1个关键码,关键码按照递增次序进行排列
  • 关键字数量需要满足ceil(m/2)-1 <= n <= m-1

以上就是B+树的一些基本特点,我们知道,MySQL的数据时存储在磁盘中,磁盘的操作是非常昂贵的,所以减少磁盘操作就可以很大程度上提升MySQL的查询效率,我们来回顾一下有关磁盘的的几个知识点。

磁盘I/O

磁盘读取数据靠的是机械运动,每次读取数据花费的时间可以分为寻道时间、旋转延迟、传输时间三个部分,寻道时间指的是磁臂移动到指定磁道所需要的时间,主流磁盘一般在5ms以下;旋转延迟就是我们经常听说的磁盘转速,比如一个磁盘7200转,表示每分钟能转7200次,也就是说1秒钟能转120次,旋转延迟就是1/120/2 = 4.17ms;传输时间指的是从磁盘读出或将数据写入磁盘的时间,一般在零点几毫秒,相对于前两个时间可以忽略不计。那么访问一次磁盘的时间,即一次磁盘I/O的时间约等于5+4.17 = 9ms左右,听起来还挺不错的,但要知道一台500 -MIPS的机器每秒可以执行5亿条指令,因为指令依靠的是电的性质,换句话说执行一次I/O的时间可以执行40万条指令,数据库动辄十万百万乃至千万级数据,每次9毫秒的时间,显然是个灾难。、

磁盘预读

考虑到磁盘I/O是非常高昂的操作,计算机操作系统做了一些优化,当一次I/O时,不光把当前磁盘地址的数据,而是把相邻的数据也都读取到内存缓冲区内,因为局部预读性原理告诉我们,当计算机访问一个地址的数据的时候,与其相邻的数据也会很快被访问到。每一次I/O读取的数据我们称之为一页(page)。具体一页有多大数据跟操作系统有关,一般为4k或8k,也就是我们读取一页内的数据时候,实际上才发生了一次I/O,这个理论对于索引的数据结构设计非常有帮助。

计算机局部性原理

当一个数据被用到时,其附近的数据也通常会马上被使用,程序运行期间所需要的数据通常比较集中。

为什么是B+树

我们来总结一下上面提到的B+树的一些特点,“二叉树”、“多路”、“平衡”、“数据存储在叶子结点”、“叶子结点之间有链指针”等,通过这些特点,我们来分析下为什么MySQL使用B+树作为索引的存储结构:

“二叉树”我们都知道,“平衡”也不难理解,在极端情况下,比如插入有序时,二叉树会退化成有序链表,平衡二叉树的出现则可以解决这个问题,平衡二叉树可以降低树的高度,在插入的同时调整这棵树,让它的节点尽可能均匀分布,从而提升查询效率;“多路”则可以进一步的降低树的高度,路数越多,树的高度越低,但是路数也不能无限多,不然整棵树就变成一个有序数组了。

MySQL的索引存储在硬盘中,在数据量大时,不一定能够一次性将所有数据都加载到内存中,这个时候多路的优势就显现出来了,可以每次加载B树的一个节点,一步一步的往下找,如果纯粹的在内存中操作,B树并不会比红黑树、234树等平衡树的效率高,但是硬盘操作,无疑是B树更适合。

hash对于单条数据的查询的确很快,毕竟O(1)的时间复杂度不是盖的。但是对于范围查询,和有序并有链指针相连的B+树相比就差很多了。

接下来我们再来看下为什么是B+树而不是B树:

MySQL是一种关系型数据库,区间访问是常见的一种情况,B+树叶节点增加的链指针,加强了区间访问性,可使用在范围区间查询等,而B-树每个节点key和data在一起,则无法区间查找,每次范围查找都需要从根节点开始查找。

磁盘的操作是昂贵的,所以还有一个很重要的考虑就是磁盘I/O次数,B+树每个节点的物理结构是索引页(磁盘块),一次索引页的访问就是一次I/O,所以查找一个索引时,通过二分查找算法从根节点开始,一直到叶子结点,磁盘的访问次数是与树的深度成正比的,索引页的大小是固定的,所以索引字段要尽量的小,这也是为什么B+树要求把真实的数据放到叶子节点而不是内层节点(根节点和叶子结点之间的节点),一旦放到内层节点,磁盘块的数据项会大幅度下降,导致树增高。

参考文献

猜你喜欢

转载自blog.csdn.net/heroqiang/article/details/88783341