【数据结构】B+树
B+树的定义
B树是B+树针对数据库的变种,拥有比B树更高的查询效率。
下面,我们看着图,先搞清楚它的定义:
一个结点的最大孩子个数称为树的阶,通常用m表示。一棵m阶B+树要么是空树,要么满足以下定义:
(1)一个结点的结构是:(K1,P1,K2,…,Kn,Pn),其中P为指向子树的指针,K为关键字,它们一一对应
(2)对于根结点,如果它本身不是叶子结点,至少拥有 1 个关键字,即至少拥有 1 棵子树
(3)对于除根结点之外的所有结点,至少拥有⌈m/2⌉ 棵子树,即至少拥有 ⌈m/2⌉ 个关键字
(4)为了满足阶的定义,任何结点最多拥有 m 个子树,即最多拥有 m 个关键字
(5)B+树的所有数据保存在叶子结点,且叶子结点由指针链接起来
B+树的查找
查找某一行记录
还是看图举栗子,比如查找30:
不难看出,和B树的查询如出一辙,仍旧是 ① 从磁盘读取结点 ② 在内存中顺序/二分查找关键字;
值得注意的是,B+树与B树相比,所有的数据都是放在叶子结点上的,这导致了:
(1)非叶子结点仅仅用于标识范围,所以查找过程不会像B树那样有可能在中途结束,而是每次必须查找到叶子结点。所以说,B+树比B树稳定。
(2)非叶子结点上不存储数据,所以盘块(存一个结点)上可以存储更多的向下索引。因此,B+树比B树更加矮胖,I/O次数会更少,这也是B+树查找性能优于B树的其中一个重要原因。
范围查询
还还还是看图举栗子,比如查找 20~40 :
首先,还是不断根据索引读磁盘、内存排序找索引,找了20;接下来的操作非常简单,即通过叶子结点间的指针一口气找到40之前的所有数据!!!正是因为数据的有序性和叶子结点间的指针,B+树的范围查询性能极高。
而在B树中,是如何进行范围查找的呢?先找到20,然后从20开始不断中序遍历。中序遍历的过程中,同时也找到了父结点中的目标数据,但是,中序遍历的磁盘I/O开销非常可怕。
聚集索引和非聚集索引
什么是聚集索引?
数据行的物理顺序与列值(一般是主键的那一列)的逻辑顺序相同。由于表的物理存储位置固定,索引一个表中只能拥有一个聚集索引。
什么是非聚集索引?
以主键以外的列值作为键值构建的索引,我们称之为非聚集索引。显然对于加非聚集索引所用的列,逻辑顺序与物理顺序是无关的。
什么是二次查询问题?
如果使用非聚集索引作为查询条件,且查询列中包含了没有任何被任何索引覆盖的列,那么就会进行两次查询——第一次根据非聚集索引查询聚集索引(主键),第二次根据聚集索引查询结果。
举个例子吧:
我们有一张表:users(id,name,age),其中id加了聚集索引,name加了非聚集索引。
select id, name from users where name="Alice" -- 不会发生二次查询,id和name都已被索引覆盖
select id, age from users where name="Cocoa" -- 会发生二次查询,age未被索引覆盖
B+树的插入
和B树的插入基本一致:
如果插入后结点满足定义,则直接插入;如果插入后结点不满足定义,则结点分裂。
值得注意的是,B+树非叶子结点并不存数据,只是索引值。
看图理解非常简单,这是个4阶B+树:
B+树的删除
与B树的删除相比,思路基本一致,但是简单很多很多 >_<
跟着下面的图走一遍(4阶B+树),一定可以学会!
首先明确,B+树没有B树那种删除非叶子结点的情况。
因为B+的非叶子结点不存储数据。
如果删除后仍符合B+树定义,则直接删除。
如果删除后不符合B+树定义,且兄弟有的借,则直接向兄弟借。
与B树相比非常方便!因为非叶子结点不存数据、叶子结点指针的存在,不需要向B树那样带着父结点旋转了,仅仅需要修改一下父结点对应关键字的值即可。
如果删除后不符合B+树定义,且兄弟没的借,则与兄弟合并。
与B树相比还是非常方便!不需要带着父结点一起合并,仅仅需要修改一下父结点对应关键字的值即可。
面试题:谈一谈二叉搜索树、二叉平衡树、B树、B+树的区别?以及它们对于数据库索引的意义。
(0)谈到这四种树,就不得不说一下数据库的索引查询。数据库的数据庞大,是无法一次性读到内存中进行查询的,因此,数据库的查询其实是一个不断读磁盘到内存、在内存中查找下一个磁盘的过程。
(0)查找过程是一个树形结构,这课树的高度即磁盘I/O的次数(不考虑第一个页表常驻内存的情况,且假定一个树结点占一个磁盘盘块)。也就是说,这棵树越矮胖,磁盘I/O次数就越少,查询的性能就越高;而在内存中的查找开销,比起磁盘的I/O开销基本可以忽略不计。
(1)二叉搜索树不是平衡的,其余三种是平衡的。平衡的意义是什么呢?想象一种极限的情况,二叉平衡树退化为线性链表。磁盘I/O次数极多,查询性能极低。
(2)二叉平衡树不是自平衡的,B树和B+树是自平衡的。平衡二叉树的平衡是通过查找最低不平衡结点并递归调整实现的;自平衡二叉树的平衡是通过局部不平衡的动态优化最终达到全局优化的。
(3)二叉平衡树是二叉的,B树和B+树是多叉的。多叉的意义何在?还是让树变得矮胖,以获得更高的查询效率。
(4)B树的非叶子结点也存储数据,B+树的非叶子结点不存数据。具体的说,B树的关键字有两层含义,一来标识范围,二来指向卫星数据;B+树的关键字只有一层含义,仅仅表示范围。B+树的这个思路有什么好处呢?让一个结点可以存储更多的索引信息,从而让树形更加矮胖;另外,B+树的查询一定会进行到叶子结点,与B树相比更加稳定。
(5)B+树的叶子结点由指针链接起来。这样做的目的很明确,即满足范围查询的需求。
(6)更细节的说,B树结点的结构是(m-1)个关键字和m个索引指针,B+树结点的结构是m个关键字和m个索引指针(索引指针个数即子树个数);B树和B+树结点都最少拥有⌈m/2⌉个索引指针,最多拥有 m 个索引指针,但是关键字个数不同(B树减1,B+树相等)。
☘️