这是我参与8月更文挑战的第13天,活动详情查看:8月更文挑战
1 B树(Balance Tree)
对于在内存中的查找结构而言,红黑树的效率已经非常好了(实际上很多实际应用还对RBT进行了优化)。但是如果是数据量非常大的查找呢?将这些数据全部放入内存组织成RBT结构显然是不实际的。实际上,像OS中的文件目录存储,数据库中的文件索引结构的存储…. 都不可能在内存中建立查找结构。必须在磁盘中建立好这个结构。 在磁盘中组织查找结构,从任何一个结点指向其他结点都有可能读取一次磁盘数据,再将数据写入内存进行比较。大家都知道,频繁的磁盘IO操作,效率是很低下的(机械运动比电子运动要慢不知道多少)。显而易见,所有的二叉树的查找结构在磁盘中都是低效的。因此,B树很好的解决了这一个问题。
B树也称B-树、B-Tree,它是一颗多路平衡查找树。描述一颗B树时需要指定它的阶数,阶数表示了一个结点最多有多少个孩子结点,一般用字母m表示阶数。当m取2时,就是我们常见的二叉搜索树。B树的定义:
- 每个结点最多有m-1个关键字
- 根结点最少可以只有1个关键字
- 非根结点至少有Math.ceil(m/2)-1个关键字
- 每个结点中的关键字都按照从小到大的顺序排列,每个关键字的左子树中的所有关键字都小于它,而右子树中的所有关键字都大于它
- 所有叶子结点都位于同一层,或者说根结点到每个叶子结点的长度都相同
B树的优势
- B树的高度远远小于AVL树和红黑树(B树是一颗“矮胖子”),磁盘IO次数大大减少
- 对访问局部性原理的利用。指当一个数据被使用时,其附近的数据有较大概率在短时间内被使用。当访问其中某个数据时,数据库会将该整个节点读到缓存中;当它临近的数据紧接着被访问时,可以直接在缓存中读取,无需进行磁盘IO
如下图(B树的内部节点可以存放数据,类似ZK的中间节点一样。B树不是每个节点都有足够多的子节点):
上图是一颗阶数为4的B树。在实际应用中的B树的阶数m都非常大(通常大于100),所以即使存储大量的数据,B树的高度仍然比较小。每个结点中存储了关键字(key)和关键字对应的数据(data),以及孩子结点的指针。我们将一个key和其对应的data称为一个记录。但为了方便描述,除非特别说明,后续文中就用key来代替(key, value)键值对这个整体。在数据库中我们将B树(和B+树)作为索引结构,可以加快查询速速,此时B树中的key就表示键,而data表示了这个键对应的条目在硬盘上的逻辑地址。
2 B+树(B+Tree)
B+树是从B树的变体。跟B树的不同:
- 内部节点不保存数据,只用于索引
- B+树的每个叶子节点之间存在指针相连,而且是单链表,叶子节点本身依关键字的大小自小而大顺序链接
如下图(其实B+树上二叉搜索树的扩展,二叉搜索树是每次一分为二,B树是每次一分为多),现代操作系统中,磁盘的存储结构使用的是B+树机制,mysql的innodb引擎的存储方式也是B+树机制:
B+树与B树相比有以下优势
- 更少的IO次数:B+树的非叶节点只包含键,而不包含真实数据,因此每个节点存储的记录个数比B数多很多(即阶m更大),因此B+树的高度更低,访问时所需要的IO次数更少。此外,由于每个节点存储的记录数更多,所以对访问局部性原理的利用更好,缓存命中率更高
- 更适于范围查询:在B树中进行范围查询时,首先找到要查找的下限,然后对B树进行中序遍历,直到找到查找的上限;而B+树的范围查询,只需要对链表进行遍历即可
- 更稳定的查询效率:B树的查询时间复杂度在1到树高之间(分别对应记录在根节点和叶节点),而B+树的查询复杂度则稳定为树高,因为所有数据都在叶节点。
B+树劣势
由于键会重复出现,因此会占用更多的空间。但是与带来的性能优势相比,空间劣势往往可以接受,因此B+树的在数据库中的使用比B树更加广泛。
3 B*树
是B+树的变体,在B+树的非根和非叶子结点再增加指向兄弟的指针,且定义了非叶子结点关键字个数至少为(2/3)×M,即块的最低使用率为2/3(代替B+树的1/2):