开启掘金成长之旅!这是我参与「掘金日新计划 · 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树:
2. 红黑树
红黑树是一个接近平衡的二叉查找树,也就是说二叉查找树的特性红黑树应该都具备
一文了解红黑树 [红黑树基础 2/2] - 掘金 (juejin.cn)
最熟悉的用红黑树的地方就是jdk8对应的hashmap,红黑树其实也是采用的二分法,相比于二叉查找树,可以避免单一链表,腿脚部分的特殊情况的出现。
最坏的情况,时间复杂度也就是接近logn。同样的道理,这也是为何不用数组+链表,而又加入了红黑树的原因,也是为了避免链表过长,降低了效率。
3. B+Tree
B+Tree是在B-Tree的基础上做了一些修改:
- 叶子节点上添加了一个单向链表
- 并且在“向上分裂”的过程中并不会把需要分裂的节点放上去,而是复制一个上去
- 非叶子节点没有数据
在Mysql中,又对B+Tree进行了修改,提高了检索效率
- 如果是二级索引,Key是指当前索引列的值,Key下面挂着的是主键id。
- 如果当前是聚簇索引,则Key下面挂着的是当前行的地址
- 把单向链表换成了双向链表
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。