本文主要以列表形式将B+树的特点以及注意点等列出来,主要参考《算法导论》、维基百科、各大博客的内容,结合自己的理解写的,如内容有不当之处,请各位雅正。

0x00 前言

B树是为磁盘或其他直接存取的辅助存储设备而设计的一种平衡搜索树。B树类似于红黑树,但它们在降低磁盘I/O操作数方面要更好一些。现在许多数据库系统使用B树或者B树的变种(B+树和B*树)来存储信息。B树用的比较普遍,许多书籍、博客都有详细的介绍,对于B树的严格定义也相对统一,在这里就不予赘述。 B+树是B树的一种变形,它把所有的卫星数据都存储在叶节点中,内部节点只存放关键字和孩子指针,因此最大化了内部节点的分支因子,所以B+树的遍历也更加高效(B树需要以中序的方式遍历节点,而B+树只需把所有叶子节点串成链表就可以从头到尾遍历)。
以下先放一张我所依据的B+树的图示(这张图有所简化,下面讲完定义后会贴一张更加详细的图,两图本质并无差异):

0x01 定义

B+树的定义我没有找到官方的定义(如果有找到的人望告知我),有些定义在论坛还有争议,但是这些并没有多大影响,只是一点小小的差异,下面的定义中有涉及争议的部分我会提及。

B+树的定义如下:

每个节点node有下面的属性: n个关键字key[1],key[2], … ,key[n],以非降序存放,使得key[1]≤key[2]≤…≤key[n];
isRoot,一个布尔值,如果node是根节点,则为TRUE;否则为FALSE;
isLeaf,一个布尔值,如果node是叶子节点,则为TRUE;否则为FALSE;
Node*类型的parent指针,指向该节点的父节点

每个内部节点还包含n个
指向其孩子children[0],children[1], … , children[n],叶子节点没有孩子。(注:此处有争议,B+树到底是与B 树n-1个关键字有n棵子树保持一致,还是B+树n个关键字的结点中含有n棵子树;两种定义都可以,只要自己实现的时候统一用一种就行。如无特殊说明,以下的都是后者:即n个关键字对应n棵子树);
内部节点的关键字对存储在各子树中的关键字范围加以分割:如果key[i]为任意一个存储在内部节点中的关键字,childNum[i]为该节点的对应下标的子树指针指向的节点的任意一个关键字,那么 key[1] ≤ childNum[1] < key[2] ≤ childNum[2] < key[3] ≤ childNum[3] < … < key[n] ≤ childNum[n]
内部节点并不存储真正的信息,而是保存其叶子节点的最小值作为索引。比如下图,标注1和标注2都是内部节点,里面保存的并不是真正的信息,而是标注3所示的节点中的最小值。(注:此处有争议以最大值作为索引,同样也是不影响的争议) 

任何和关键字相联系的“卫星数据(satellite information)” 将与关键字一样存放在叶子节点中,一般地,可能只是为每个关键字对应的”卫星数据”存放一个指针,指针指向存放实际数据的磁盘页,匹配了某个叶子节点的关键字即可通过该指针找到其他对应数据。

每个叶子节点还有指向下一个节点的指针next,方便遍历整棵B+树。
每个叶子节点具有相同的深度,即树的高度h。
每个节点所包含的关键字个数有上界和下界,用一个被B+树的最小度数(minmum degree)的固定整数t≥2来表示这些界: 除了根节点以外的每个节点必须至少有t个关键字。因此,除了根节点以外的每个内部节点至少有t个孩子
每个节点至多有2t个关键字,因此,一个内部节点至多可有2t个孩子。当一个节点恰好有2t个关键字时,称该节点是满的。

结合以上的具体定义,下面这张图更加详细的描述了一棵具体的B+树

0x02 注意点

在B+树的学习与实现过程中,也遇到不少的疑惑之处,现记录如下,持续更新:
内部节点并不存储真正的信息,而是保存其叶子节点的最小值作为索引。每次插入删除都进行更新(此时用到parent指针),保持最新状态。
关于所有叶子节点都处于同一深度是如何实现的?这与B+树具体的插入和删除算法有关。简单解释一下插入时的情况,根据插入值的大小,逐步向下直到对应的叶子节点。如果叶子节点关键字个数小于2t,则直接插入值或者更新卫星数据;如果插入之前叶子节点已经满了,则分裂该叶子节点成两半,并把中间值提上到父节点的关键字中,如果这导致父节点满了的话,则把该父节点分裂,如此递归向上。所以树高是一层层的增加的,叶子节点永远都在同一深度。下面是我实现的B+树中的插入代码的片段:
public void insert(Comparable key, Object obj, BPlusTree tree)
{
// 叶子节点则插入
if (isLeaf) {
// 不需要分裂直接插入
if (containsKeyword(key) || keywords.size() < tree.getDegree()) {
insertInNotFull(key, obj);
// 直接插入
if (parent != null) {
parent.updateAfterInsert(tree); // 更新父节点的信息(将最小的值存到父节点的关键字中作为索引)
}
} else { // 需要分裂成左右两个节点
splitNode(key, obj, tree);
}
} else { // 如果不是叶子节点则继续往下搜索
Node leafNode = downToLeaf(key); // 逐步向下到对应的叶子节点
leafNode.insert(key, obj, tree);
}
}

0x03 结语

B+树还有一个最大的好处,方便扫库,B树必须用中序遍历的方法按序扫库,而B+树直接从叶子结点挨个扫一遍就完了,B+树支持range-query非常方便,而B树不支持。这是数据库选用B+树的最主要原因。

欢迎各位大牛批评指正。PS:我实现了一个小型B+树系统,使用Java写的,支持插入、搜索、遍历B+树,有需要的同学可以去下载。链接奉上:B+树实现(Java源码)

参考文献
[1].《算法导论》原书第3版中文版
[2]. 维基百科B+树条目
[3]. 很详细的一篇B树、B+树、R树的博客 
[4]. 数据库实现的扼要说明
 

参考

SQL学习笔记之B+树的几点总结的更多相关文章

  1. SQL学习笔记之B+树

    0x00 概述 要描述清楚B+树,得先了解二叉查找数,平衡二叉树. 0x01 二叉查找树 任意节点,它的左子树如果不为空,那么左子树上所有节点的值都小于根节点的值:任意节点,他的右子树如果不为空,那么 ...

  2. ORALCE PL/SQL学习笔记

    ORALCE  PL/SQL学习笔记 详情见自己电脑的备份数据资料

  3. 【学习笔记】线段树—扫描线补充 (IC_QQQ)

    [学习笔记]线段树-扫描线补充 (IC_QQQ) (感谢 \(IC\)_\(QQQ\) 大佬授以本内容的著作权.此人超然于世外,仅有 \(Luogu\) 账号 尚可膜拜) [学习笔记]线段树详解(全) ...

  4. Oracle之PL/SQL学习笔记

    自己在学习Oracle是做的笔记及实验代码记录,内容挺全的,也挺详细,发篇博文分享给需要的朋友,共有1w多字的学习笔记吧.是以前做的,一直在压箱底,今天拿出来整理了一下,给大家分享,有不足之处还望大家 ...

  5. SQL学习笔记

    SQL(Structured Query Language)学习笔记 [TOC] Terminal登录数据库 1.登录mysql -u root -p ; 2.显示所有数据库show database ...

  6. SQL学习笔记之MySQL索引知识点

    0x00 概述 之前写过一篇Mysql B+树学习,简单的介绍了B+数以及MySql使用B+树的原因, 有了这些基础知识点,对MySql索引的类型以及索引使用的一些技巧,就比较容易理解了. 0x01 ...

  7. [SQL学习笔记][用exists代替全称量词 ]

    学习sql的必经问题. 学生表student (id学号 Sname姓名 Sdept所在系) 课程表Course (crscode课程号 name课程名) 学生选课表transcript (studi ...

  8. SQL学习笔记——SQL初入门,Ubuntu下MySQL的安装

          刚开始接触sql,于是准备在Ubuntu下学习sql,就跟着itercast的sql教程开始入门了. 下面只是我个人的记录,高手请绕道: 一. 在安装之前,我们可以用下面这个命令通过开放端 ...

  9. PL\SQL学习笔记

    注释 单行--多行 一.declare一般用于做变量的申明.begin 程序体开始执行  end; 程序体结束exception .. dbms_output.put_line('绝对值'||v_ab ...

随机推荐

  1. C++中的自动存储、静态存储和动态存储

    根据用于分配内存的方法,C++中有3中管理数据内存的方式:自动存储.静态存储和动态存储(有时也叫做自由存储空间或堆).在存在是间的长短方面,以这三种方式分配的数据对象各不相同.下面简要介绍这三种类型( ...

  2. Maven新建一个Spring MVC项目

    新建一个Maven项目,选择archetypes为maven-archetype-webapp,相关的名称按个人习惯取,我这里取Group Id:moonlit-groupArtifact Id:mo ...

  3. Java Web项目--使用JSP生成一个页面

    我们使用了servlet生成了一个网页,但是可以看到使用servlet生成网页必须将网页的内容全部嵌入到Java代码当中,不是很方便.所以有没有什么办法是将Java代码嵌入到html代码中,而不是像s ...

  4. PE导入表分析

    A.dll 导入 B.dll 导出函数 A.dll 表内容 这个结构指向的B导出函数的地址 Hook这个位置 等同于 Hook B.dll导出函数

  5. IOS6.0调用通讯录和之前的差别

    6.通讯录列表获取差异 自iOS6.0后获取通讯录列表需要询问用户,经过用户同意后才可以获取通讯录用户列表.而且ABAddressBookRef的初始化工作也由ABAddressBookCreate函 ...

  6. 【BZOJ3648】寝室管理 树分治

    [BZOJ3648]寝室管理 Description T64有一个好朋友,叫T128.T128是寄宿生,并且最近被老师叫过去当宿管了.宿管可不是一件很好做的工作,碰巧T128有一个工作上的问题想请T6 ...

  7. mac java环境

    1.java运行环境jre:http://www.java.com/zh_CN/ 2.jdk:http://www.oracle.com/technetwork/java/javase/downloa ...

  8. spring配置文件注解方式引入的两种方式

    一.#{beanID['propertiesName']}方式 <bean id="propertyConfigurer" class="org.springfra ...

  9. CSS Sprites 原理技术介绍及其优化方法

    先期的准备工作 应对一个项目后期维护成本大的问题,我们最好的解决方案就是在开始前制定一系列的规范来限制问题的产品.好的开始是成功的一半.对于CSS Sprites,在项目开始前,我们要充分认识一个产品 ...

  10. ajax跨域问题解决之cors篇

    现在浏览器出于安全考虑,在域名.协议.端口不同的情况下,浏览器会认为这是跨域,ajax请求是不允许跨域的. 如果我们有跨域的需求,可以使用cors解决.其原理就是,在请求之前先发送一个OPTIONS请 ...