为什么Innodb用B+树作为索引的数据结构呢?

469 阅读6分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 5 天 众所周知,MySQL的索引使用了B+树的数据结构。那么为什么不用B树或者其他的数据结构呢?
先看一下B+树与其他数据结构的区别。MySQL 复习笔记(一)------ 索引 [1/3] - 掘金 (juejin.cn)

1. B 树

维基百科对B树的定义为“在计算机科学中,B树(B-tree)是一种树状数据结构,它能够存储数据、对其进行排序并允许以O(log n)的时间复杂度运行进行查找、顺序读取、插入和删除的数据结构。

B树,概括来说是一个节点可以拥有多于2个子节点的二叉查找树。与自平衡二叉查找树不同,B-树为系统最优化大块数据的读和写操作。B-tree算法减少定位记录时所经历的中间过程,从而加快存取速度。普遍运用在数据库和文件系统。”

B 树可以看作是对2-3查找树的一种扩展,即他允许每个节点有M-1个子节点。

  • 根节点至少有两个子节点

  • 每个节点有M-1个key,并且以升序排列

  • 位于M-1和M key的子节点的值位于M-1 和M key对应的Value之间

  • 其它节点至少有M/2个子节点
    下图是一个M=5 阶的B树:

image.png

2. 红黑树

红黑树是一个接近平衡的二叉查找树,也就是说二叉查找树的特性红黑树应该都具备

一文了解红黑树 [红黑树基础 2/2] - 掘金 (juejin.cn)

最熟悉的用红黑树的地方就是jdk8对应的hashmap,红黑树其实也是采用的二分法,相比于二叉查找树,可以避免单一链表,腿脚部分的特殊情况的出现。

最坏的情况,时间复杂度也就是接近logn。同样的道理,这也是为何不用数组+链表,而又加入了红黑树的原因,也是为了避免链表过长,降低了效率。

image.png

3. B+Tree

首先看看B+Tree与B-Tree的区别

B+Tree是在B-Tree的基础上做了一些修改:

  1. 叶子节点上添加了一个单向链表
  2. 并且在“向上分裂”的过程中并不会把需要分裂的节点放上去,而是复制一个上去
  3. 非叶子节点没有数据

在Mysql中,又对B+Tree进行了修改,提高了检索效率

  1. 如果是二级索引,Key是指当前索引列的值,Key下面挂着的是主键id。
  2. 如果当前是聚簇索引,则Key下面挂着的是当前行的地址
  3. 把单向链表换成了双向链表

image.png

4. 总结

4.1 B+树与B树

为什么选用B+Tree呢?

B+树的非叶子结点只包含导航信息,不包含实际的值,所有的叶子结点和相连的节点使用链表相连,便于区间查找和遍历。

B+树只有叶节点存放数据,其余节点用来索引,而B-树是每个索引节点都会有Data域。 所以从Mysql(Inoodb)的角度来看,B+树是用来充当索引的,一般来说索引非常大,尤其是关系性数据库这种数据量大的索引能达到亿级别,所以为了减少内存的占用,索引也会被存储在磁盘上。

B+ 树的优点在于:

  • IO次数更少: B-树/B+树 的特点就是每层节点数目非常多,层数很少,目的就是为了就少磁盘IO次数,但是B-树的每个节点都有data域(指针),这无疑增大了节点大小,说白了增加了磁盘IO次数(磁盘IO一次读出的数据量大小是固定的,单个数据变大,每次读出的就少,IO次数增多,一次IO多耗时),而B+树除了叶子节点其它节点并不存储数据,节点小,磁盘IO次数就少。这是优点之一。
  • 遍历更加方便另一个优点是:  B+树所有的Data域在叶子节点,一般来说都会进行一个优化,就是将所有的叶子节点用指针串起来。这样遍历叶子节点就能获得全部数据,这样就能进行区间访问啦。在数据库中基于范围的查询是非常频繁的,而B树不支持这样的遍历操作。

但是B树也有优点,其优点在于,由于B树的每一个节点都包含key和value,因此经常访问的元素可能离根节点更近,因此访问也更迅速。

4.2 B+树与红黑树

AVL 数和红黑树基本都是存储在内存中才会使用的数据结构

在大规模数据存储的时候,红黑树往往出现由于树的深度过大而造成磁盘IO读写过于频繁,进而导致效率低下的情况。

为什么会出现这样的情况,我们知道要获取磁盘上数据,必须先通过磁盘移动臂移动到数据所在的柱面,然后找到指定盘面,接着旋转盘面找到数据所在的磁道,最后对数据进行读写。

磁盘IO代价主要花费在查找所需的柱面上,树的深度过大会造成磁盘IO频繁读写。根据磁盘查找存取的次数往往由树的高度所决定,所以,只要我们通过某种较好的树结构减少树的结构尽量减少树的高度,B树可以有多个子女,从几十到上千,可以降低树的高度。

数据库系统的设计者巧妙利用了磁盘预读原理,将一个节点的大小设为等于一个页,这样每个节点只需要一次I/O就可以完全载入。为了达到这个目的,在实际实现B-Tree还需要使用如下技巧:每次新建节点时,直接申请一个页的空间,这样就保证一个节点物理上也存储在一个页里,加之计算机存储分配都是按页对齐的,就实现了一个node只需一次I/O。

参考资料

MySQL用B+树(而不是B树)做索引的原因

B+Tree在数据库索引上拥有独特优势的原因(为什么比红黑树更合适)