【数据结构】B树和B+树

问题:我们为何选B+树做数据库的索引?

我们都知道,平衡二叉树或红黑树的查找效率最高,时间复杂度是O(nlogn)。那为什么不选择它们来做数据库和文件系统的索引呢?

因为磁盘和内存读写速度有明显的差距,磁盘中存储的数据需要先读取到内存中才能进行高速的检索。而数据库当中存储着海量的数据,光是数据库索引就有可能占据几个GB甚至更大的空间。当我们要查找数据的时候,显然不可能把整个索引树读到内存中。因此,我们只能以索引树的节点为基本单元,每次把单一节点从磁盘读取到内存当中,进行后续操作。

如果磁盘当中的索引树是一棵平衡二叉树,查找的时候,在最坏情况下,磁盘I/O的次数等于索引树的高度。

为了减少磁盘I/O,我们需要把原本“瘦高”的树结构变得“矮胖”,让每一个节点承载更多的元素,拥有更多的孩子。因此B树和B+树就更适合做数据库和文件系统的索引了。

那为什么我们选择B+树而不是B树呢?主要有以下几个原因:

1、B+树可以更好地利用磁盘预读特性
在数据库中,数据通常都存储在磁盘上。而磁盘的读写速度比内存慢很多,因此需要尽量减少磁盘I/O操作。B+树相对于B树来说,其内部节点只存储键值信息,而不存储数据信息,这样可以让每个节点能够存储更多的键值信息,从而使得查询同一层次的所有数据时,能够一次性读入更多的数据块,减少磁盘I/O操作。

2、B+树能够更快地进行范围查询
由于B+树的非叶子节点只存储键值信息,而不存储指向数据的指针,因此当进行范围查询时,只需要遍历B+树的叶子节点即可,而不需要遍历非叶子节点,大大加快了查询速度。

3、B+树更适合于数据库索引
B+树的叶子节点形成了一个有序链表,使得范围查询更加容易实现。而B树则不支持高效的范围查询,并且为了保持平衡需要维护指向所有数据的指针,导致其空间利用率较低。

B树

B树和二叉树、红黑树相比较,子树更多也就是路数越多,子树越多表示数的高度越低,搜索效率越高。B树就是一种平衡的多叉树。

B树的特征

B树(即B-树)单一节点拥有的最多子节点数量,称为B树的“阶”。一个m阶的B树,具有如下几个特征:
1、根节点至少有两个子节点。
2、每个中间节点都包含k-1个元素(也被称为关键字)和k个孩子,其中m/2 <= k <= m。
3、每一个叶子节点都包含k-1个元素,其中m/2 <= k <= m。
4、所有的叶子节点都位于同一层。
5、每个节点中的元素从小到大排列,节点当中k-1个元素正好是k个孩子包含的元素的值域分划。

如图是一个3阶的B树
【数据结构】B树和B+树_第1张图片

B树查询中的比较次数其实不比平衡二叉树少,但是节点内部元素的比较是在内存中进行的,时间成本几乎可以忽略。真正影响性能的,是磁盘I/O的次数,只要树的高度降低,磁盘I/O次数就会相应减少,从而提高整体性能。

B树的缺点

1、B树不方便进行范围查询。
数据库的查询,不止涉及单一结果查询(比如查找学号是110235的学生),也会涉及一个区间内结果的查询(比如查找数学成绩60~80分的所有学生)。
对于前者,B树很容易实现,对于后者,由于区间内结果分布在各个层次的节
点当中,B树实现起来会非常烦琐,需要进行中序遍历,在父节点和子节点之间不断切换,给磁盘I/O带来很多负担。因此B+树就运势而生了。

2、每个节点中既要存索引信息,又要存其对应的数据,如果数据很大,那么当树的体量很大时,每次读到内存中的树的信息就会不太够。

3、B树遍历整个树的过程和二叉树本质上是一样的,B树相对二叉树虽然提高了磁盘IO性能,但并没有解决遍历元素效率低下的问题。

针对以上问题,B+树诞生了

B+树

B+树的特征

B+ 树,只有叶节点才携带数据

一个m阶的B+树具有如下几个特征:

1、有k个子树的中间节点包含k个元素(B树中是k-1个元素),每个元素不保存数据,所有数据都保存在叶子节点。
2、所有的叶子节点包含了全部元素,依照元素的大小升序排列,叶子节点之间用双向指针相连接。(可以支持范围查找)
3、所有中间节点的元素都同时存在于子节点,在子节点元素中是最大(或最小)元素。

【数据结构】B树和B+树_第2张图片

一个三层的B+树可以放多少行数据呢?

在 InnoDB 存储引擎里面,最小的存储单元是页(page),一个页的大小是 16KB,也就是一个节点的大小。根据上文,非叶子节点保存的是索引值和指针,假设索引 id 是 long类型,占 8个byte,指针占 6 byte,所以,根节点可以存放 16KB / (8 + 6) = 1170 个索引值,因此就有1170个指针,假设一条数据的大小是1K,因此叶子节点可以存放 16Kb/1K = 16条数据,所以3层的B+树可以存放 1170 * 1170 * 16 = 21902400行记录,所以,一个三层的 B+树可以存放 2000万左右的数据。

扩展: 索引的数据结构有哪些?

哈希表

哈希表是一种以 key-value 键值对存储数据的结构,比如:java 的 hashmap, redis 的 key-value 都是这样一种形式。hash 表的实现思路也很简单:用一个哈希函数把 key 换算成数组确定的位置,然后把 value 放在数组的这个位置。

【数据结构】B树和B+树_第3张图片
当 key 的 hash 值相同的时候,会采用链表的方式把 value 串起来。

hash 表的问题随着数据量的增多,不同 key 经过哈希计算后结果一样,这种情况叫做 hash 碰撞。处理 hash 碰撞的一种方法是链表,但是当数据量比较大时,链表的长度还是会比较大,性能开销就在链表查询上。哈希表是散列存储,因此这种结构适用于只有等值查询的场景,比如 Memcached 及其他一些 NoSQL 引擎。

有序数组

如果数据按照 id 的升序存放到数组中,就形成了一个有序数组,这样既能根据等值查询,也方便范围查询。
【数据结构】B树和B+树_第4张图片
有序数组问题如果仅仅看查询效率,有序数组是比较好的数据结构,但如果有数据的插入和删除,插入和删除点后面的数据需要移动,所以整体性能会下降,因此,有序数组只适合静态存储引擎。

搜索树

包括
二叉树、二叉查找树、二叉平衡树、红黑树、B树、B+树
(本文只讲B+和B+树,其他下次再说)

你可能感兴趣的:(数据结构,b树,b+树)