B树和B+树

本文深入解析了B树和B+树的数据结构特点,包括它们在数据库中的应用,如聚簇索引和非聚簇索引的概念,以及如何通过B+树提高数据查找效率。

B树

我们应该寻找一种单个节点可以存储多个键值和数据的平衡树。
B 树(Balance Tree)即为平衡树的意思,下图即是一棵 B 树:

在这里插入图片描述
图中的每个节点称为页,页就是我们上面说的磁盘块,在 MySQL 中数据读取的基本单位都是页

B 树查找数据读取磁盘的次数将会很少,数据的查找效率也会比平衡二叉树高很多。

假如我们要查找 id=28 的用户信息,那么我们在上图 B 树中查找的流程如下:

先找到根节点也就是页 1,判断 28 在键值 17 和 35 之间,那么我们根据页 1 中的指针 p2 找到页 3。
将 28 和页 3 中的键值相比较,28 在 26 和 30 之间,我们根据页 3 中的指针 p2 找到页 8。
将 28 和页 8 中的键值相比较,发现有匹配的键值 28,键值 28 对应的用户信息为(28,bv)。

B+树

直接上图:
在这里插入图片描述
最直观的区别,就是叶子节点之间通过双向链表连接。
①B+ 树非叶子节点上是不存储数据的,仅存储键值,而 B 树节点中不仅存储键值,也会存储数据。
②因为 B+ 树索引的所有数据均存储在叶子节点,而且数据是按照顺序排列的。

聚簇索引

以主键作为B+树的键值。
在 B+ 树的叶子节点中,存储了表中所有的数据。

非聚簇索引

以主键以外的列值作为键值构建的 B+ 树索引。
叶子节点不存储表中的数据,而是存储该列对应的主键。想要查找数据我们还需要根据主键再去聚集索引中进行查找(回表)。

### B与B+的区别 BB+都是多路平衡查找,适用于外部存储大规模数据索引,但它们在结构功能上存在显著差异。 #### 1. **关键字与节点关系** B中,每个节点既可以存储关键字,也可以存储数据。对于一个m阶的B来说,每个节点最多包含m-1个关键字,同时子节点数量也最多为m。而B+的每个节点可以包含m个关键字,并且所有数据都存储在叶子节点中。非叶子节点仅存储索引信息,这使得B+更适用于大数据量的索引结构。 #### 2. **数据存储位置** B数据可以存储在任意节点中,包括根节点、中间节点叶子节点。而B+数据仅存储在叶子节点中,中间节点仅用于索引。这种设计使得B+能够更高效地进行批量数据访问。 #### 3. **分支节点的构造** B的分支节点存储关键字子节点指针,同时可能存储数据。而B+的分支节点仅存储关键字子节点指针,不存储实际数据。这意味着在相同的页大小(例如16KB)下,B+可以存储更多的索引信息,从而减少的高度,提高查询效率。 #### 4. **查询性能** B在查询时,可能在非叶子节点就找到所需数据,因此查询路径较短。但B+的查询路径固定,从根节点到叶子节点的高度一致,因此查询性能更加稳定。这种稳定性对于数据库索引尤为重要。 #### 5. **区间访问** B+叶子节点通过链表连接,支持高效的区间查询。例如,数据库中的范围查询(如`SELECT * FROM table WHERE id BETWEEN 100 AND 200`)可以利用B+的这一特性快速完成。而B叶子节点之间没有链接,区间查询效率较低。 #### 6. **磁盘I/O效率** 由于B+的非叶子节点不存储数据,每个节点可以容纳更多的关键字,从而减少的高度。这使得B+在磁盘I/O操作上更加高效,尤其适合需要频繁读取外部存储的场景。 ### B与B+的应用场景 #### B的应用场景 B适用于需要频繁更新查询的小规模数据集。由于B的非叶子节点存储数据,它在某些特定的场景中具有优势,例如: - **内存数据库**:当数据量较小且需要快速访问时,B可以减少磁盘I/O操作。 - **嵌入式系统**:在资源受限的环境中,B的简单结构可以提供较高的性能[^3]。 #### B+的应用场景 B+由于其结构特性,广泛应用于大规模数据存储索引系统,主要包括: - **数据库索引**:如MySQL的InnoDB引擎使用B+作为主键索引的底层实现,支持高效的查询范围扫描。 - **文件系统**:B+叶子节点链表结构支持高效的文件顺序访问,例如在Linux文件系统中。 - **分布式存储系统**:B+的稳定查询性能高效的区间访问能力使其成为分布式数据库存储系统的首选索引结构[^2]。 ### 示例代码:B+的插入操作 以下是一个简化的B+插入操作的Python实现,用于展示B+的基本结构插入逻辑: ```python class BPlusTreeNode: def __init__(self, order, is_leaf): self.order = order self.is_leaf = is_leaf self.keys = [] self.children = [] def insert(self, key): if self.is_leaf: self.keys.append(key) self.keys.sort() else: child = self.get_child_for_key(key) child.insert(key) if len(child.keys) >= self.order: self.split_child(child) def split_child(self, child): new_node = BPlusTreeNode(child.order, child.is_leaf) mid = len(child.keys) // 2 new_node.keys = child.keys[mid:] child.keys = child.keys[:mid] if not child.is_leaf: new_node.children = child.children[mid:] child.children = child.children[:mid] self.keys.append(new_node.keys[0]) self.keys.sort() index = self.children.index(child) self.children.insert(index + 1, new_node) def get_child_for_key(self, key): for i, k in enumerate(self.keys): if key < k: return self.children[i] return self.children[-1] class BPlusTree: def __init__(self, order): self.order = order self.root = BPlusTreeNode(order, is_leaf=True) def insert(self, key): self.root.insert(key) if len(self.root.keys) >= self.order: new_root = BPlusTreeNode(self.order, is_leaf=False) new_root.keys = [self.root.keys[0]] new_root.children = [self.root] self.root = new_root # 示例:创建一个B+并插入几个键 tree = BPlusTree(order=4) for key in [10, 20, 5, 6, 15, 30]: tree.insert(key) ``` ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值