B树的原理与实现

一、前置知识

1. 三级存储结构

计算机存储结构简单的描述有cpu上的寄存器、内存、磁盘。读取速度从磁盘到寄存器依次增快,存储容量从磁盘到寄存器依次减少。

 

 2. 磁盘的物理结构与读取方式

 

磁盘按扇区存储,每次存取则去寻找对应扇区,而每一次寻址的速度都是较慢的。

3. Btree

Btree的出现是为了优化磁盘的读取次数。B树的存储结构可以有效的减少磁盘io次数。

B树,是指多叉搜索树,即一个节点上存储多个关键字。一颗M阶B树的定义是指,这颗树是M叉树,一个节点上最多存储M-1个关键字。

一颗M阶B树有如下规范:

  1. 每个节点至多拥有M棵子树
  2. 根节点至少拥有两颗子树
  3. 除根节点之外的所有节点至少拥有ceil(M/2)棵子树
  4. 所有叶子节点都在同一层
  5. 有k棵子树的节点分支存在k-1个关键字,并且关键字按照递增顺序排序
  6. 关键字数量满足ceil(M/2)-1 <= n <= M-1

很明显,B树的一颗平衡树,而B树的平衡依靠的是插入数据时的分裂和删除数据时的借位与合并。

二、Btree的实现

宏观上,B树节点的插入和删除都是在叶子节点上发生的。

(现定义Btree的阶数为M,节点的关键字为K。且由于奇数阶Btree的实现逻辑相对复杂,因此我们仅讨论并实现偶数阶b树)

1. Btree节点的分裂

B树节点的分裂是发生在关键字数量达到最大值的节点上的。分裂操作是,将节点中间的关键字提升插入父节点,再将两边的关键字集合分别作为两个子树存储。因此,分裂代码的实现的传入参数应带有分裂节点的父节点。如果分裂节点是根节点的话,则应生成一个新的空节点作为根节点的父节点。

 

 

2. Btree的插入

第一步,找到合适的节点

  如果当前节点不是叶子节点,

    则插入是发生在此节点的子树上,找到合适的子树。

      当子树的节点k == M-1时,子树节点发生分裂,重新从父节点找到合适的插入位置

      如果子树的节点k < M-1,则在子树上查找合适的节点

  如果当前节点是叶子节点,

    则key插入在此节点

第二步,插入key

  节点中的keys是按照递增排序的,因此将key插入叶子节点,要找到合适的插入位置。

注:因为在进行分裂时,我们需要传入父节点,所以我们一般是通过父节点去判断子节点是否需要分裂。在这种代码实现下,根节点是不会经过这个逻辑判断的,因此,在每一次宏观插入时,都要先单独分析一下根节点是否需要分裂。

3. Btree的删除

第一步,找到要删除的key所在的节点

  如果删除key不在当前节点,且当前节点不是叶子节点,则删除发生在该节点的某一子树上

    判断子树K==ceil(M/2)-1,

      则向相邻兄弟子树借位,相邻左树(右树)K >ceil(M/2)-1,将父节点连接子树和相邻子树的key移到子树,用相邻左树(右树)的最后(最前)一个key覆盖父节点的key,次数相邻树的key变少,则相邻树的子节点也要相应移动

      相邻树无法借位,即相邻兄弟子树K==ceil(M/2)-1,则进行合并。将相邻子树+当前子树+父节点的key下沉合并为一个节点作为父节点的子树。

    判断子树K>ceil(M/2)-1,递归找到要删除的key所在的节点

  删除key在当前节点,在此节点执行删除操作

第二步,删除操作

  当前节点是叶子节点,直接删除

  当前节点不是叶子节点,

    判断当前key的左(右)子树K>ceil(M/2)-1,将key与左(右)子树的任意key替换,再在子树上递归执行删除操作

    当前key的左右子树K == ceil(M/2)-1,执行合并,再在合并后的节点上递归执行删除操作

注:在执行合并操作时,有时候会合并到只有一个key的根节点,此时要注意根节点的变化

 

 

——————————————————————————————————————————————————————————————————————————

github:https://github.com/illusorycat/Btree.git

 

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

以下为随手笔记,

 

 多线程下B树的线程安全?

锁根节点?   可以,但是这样子锁的强度太大了

锁子树   可以,因为b树的没有指向父节点的指针,

 

磁盘的慢是慢在寻址

为了在节点数相同的情况下,减少层高,降低寻址次数(多叉树)

 

btree

 

 

每个节点4k

 

 

 

一般M选择偶数,则最大key数为基数,在需要分裂的时候,可以直接选择最中间的,方便分裂

b树在添加key的时候,分裂有两种情况:

1. 只有个根节点的时候,1分裂成三个节点

2. 其余情况,1分裂两个节点,把一个key提到父节点

 

插入的时候,两个步骤

1. 找到对应的节点

2. 对节点的kry对比,找到合适的位置

插入的数据是插在叶子节点的

 

删除的时候

第一步:找到要删除的key

1. 递归到的子树,key数量等于最小key数

       A借位(向同级节点)

              a 从前面的子树借,(前面子树的key大于ceil(m/2)-1)

              b 从后面的子树借,(不满足上一条,且后面子树的key大于ceil(m/2)-1)

       B 合并(不满足借位)

2. 递归到的子树,key数量大于最小key数,直接进行

第二步:删除是发生在叶子节点的

1. 是叶子节点,直接删除

2. 不是叶子节点,

A 借位

              a 向左子树借位,(左子树的key大于ceil(m/2)-1)

              b 向右子树借位,(右子树的key大于ceil(m/2)-1)

       B 合并

              将该节点和左右子树合并,

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

扔个b+树的图

 

posted @ 2022-02-25 15:28  幻cat  阅读(604)  评论(0编辑  收藏  举报