索引技 术演进
信息
2024年8月14日 · ·
索引主流技术
B 树(B-Tree)
B 树是一种自平衡的多叉树数据结构,所有叶子节点在同一层级,设计目的是为了高效存储和检索数据。
- 特点:
- 每个节点包含多个键和子节点指 针。
- 维护排序的键列表,并通过二分查找进行快速查询。
- 支持动态插入、删除和查找操作。
B+ 树(B+ Tree)
B+ 树是对 B 树的一种改进,区别在于:
- 所有值都存储在叶子节点:非叶子节点仅存储键和指向子节点的指针。
- 叶子节点通过链表连接:方便范围查询。
- 优势:范围查询更高效;叶子节点有序链表链,易于顺序遍历。
B*树(B* Tree)
B* 树是对 B+ 树的进一步改进,目的是提升节点分裂和空间利用率。
- 主要改进:
- 节点分裂改为三分之一:节点分裂只有在兄弟节点也满的情况下才发生。
- 增加了兄弟节点的分裂:分裂时节点向右兄弟节点借键,增加了空间利用率。
- 优势:
- 更高的节点空间利用率。
- 插入和删除操作造成的调整次数减少。
LSM 树(Log-Structured Merge-Tree)
LSM 树是一种用于高写入负载优化的 数据结构,广泛应用于现代数据库(如 Cassandra、LevelDB)。
- 特点:
- 写操作首先写入内存中的 MemTable,并定期刷新到磁盘生成 SSTable。
- 通过异步合并和压缩策略提升写性能。
- 优势:
- 高效地处理大规模写入操作。
- 读取操作通过内存和磁盘多级机制进行,综合性能优越。
B 树
B 树是一种自平衡的树数据结构,主要用于数据库和文件系统中,以保持数据的有序性并允许高效的搜索、顺序访问、插入和删除操作。
关键特点
- 平衡性:所有叶子节点都处于同一层,保证了树的高度平衡。
- 节点度:每个节点可以有多个子节点。内部节点包含指向子节点的指针,叶子节点则包含实际数据。
- 键的数目:每个节点包含的键数目有明确的最小和最大范围。假设 B 树的阶数(degree)为 ,则每个节点至少有 个键,至多有 个键。
- 时间复杂度:搜索、插入和删除操作的时间复杂度均为 ,其中 是树中元素数目。
这些特点使得 B 树非常适合用于磁盘存取和其他基于块存储的应用。
B 树结构
- 节点组成:
- 每个节点包含若干个键值(keys)和指向子节点的指针(pointers)。
- 每个节点最多有 个键和 个子节点。这里 被称为 B 树的最低度数(minimum degree)。
- 根节点:
- 特殊的,根节点可以有少于 个键,但其他节点必须至少有 个键。
- 叶子节点:
- 叶子节点不包含子节点指针,只包含实际存储的数据。
- 分裂与合并:
- 当插入键导致一个节点的键数超过最大值 时,需要进行节点分裂(split)。分裂会将中间键上移至父节点,并将分裂的节点分成两个新的节点。
- 当删除键导致一个节点的键数少于最小值 时,可能需要合并(merge)节点或从兄弟节点借键,以维持树的平衡。
- 顺序保持:
- 在每个节点内部,所有键按升序排列。指向子节点 的指针将节点分成 个区间,其中每个区间都包含比前一个键大但比后一个键小的所有键。
例如,假设 (即每个节点可以包含 2 到 5 个键),那么每个节点可能的结构如下:
- 一个节点可能包含 3 个键:
[k1, k2, k3]
,并有 4 个指向子节点的指针:[P0, P1, P2, P3]
。 - 这些指针分隔键的范围,使得所有属于
P0
的值小于k1
,属于P1
的值在k1
和k2
之间,依此类推。
以上结构特性使得 B 树在插入、删除和查找操作时能够高效地保持平衡和有序性。
B 树操作
B 树的主要操作包括插入、删除和查找。每种操作都设计成在保持树的平衡和结构完整性的同时进行。
查找(Search)
查找操作在 B 树中类似于在二分搜索树中的查找:
- 从根节点开始,逐个比较节点内的键值。
- 如果找到目标键,返回对应的节点。
- 如果目标键小于当前节点内的某个键,则转向左子节点进行递归查找;若大于,则转向右子节点。
- 如果到达叶子节点仍未找到目标键,则说明目标键不存在。
插入(Insert)
插入操作需要确保插入后的树仍然是平衡的:
- 首先,在树中找到插入键应当位于的叶子节点位置。
- 将新键插入该叶子节点中,使节点内的键保持有序。
- 如果插入后该节点的键数超过最大限制( 个键),则进行节点分裂(split)。分裂将节点中间的键上移 至父节点,原节点分裂为包含前 个键和后 个键的两个新节点。
- 如果分裂引起父节点也超过键数限制,则对父节点继续进行分裂,必要时一直递归到根节点。
删除(Delete)
删除操作稍微复杂些,因为需要保持树的平衡:
- 直接删除:
- 如果目标键在叶子节点中,直接删除该键。
- 如果目标键在内部节点中,情况较为复杂:
- 如果待删除键的直接前驱或后继在对应的子树中存在并且包含至少 个键,则用前驱或后继键替换该键,并递归删除前驱或后继节点中的键。
- 否则,将目标键与其子节点合并,并递归删除目标键。
- 借键:
- 如果删除操作导致某个节点的键数小于最小限制( 个键),需要从兄弟节点借键以保持平衡。兄弟节点必须有至少 个键。
- 借键后,需要调整父节点和相关子节点的键值和指针。
- 合并:
- 如果借键不可行,则将受影响的节点与相邻的兄弟节点合并, 同时将父节点中的一个键下移至合并后的节点。
复杂度分析
无论是查找、插入还是删除操作,B 树的高度始终保持在 的范围内( 为树中的键数量)。因此,这些操作的时间复杂度均为 ,保证了高效的性能,尤其适用于需要频繁读写的大规模数据存储环境,如数据库和文件系统。
通过上述操作,B 树能够高效地维护其平衡和有序性,并提供快速的查找、插入和删除操作。
B 树简化实现
class BTreeNode:
def __init__(self, t, leaf=False):
self.t = t
self.leaf = leaf
self.keys = []
self.children = []
def __repr__(self):
return f'Node(keys={self.keys}, leaf={self.leaf})'
class BTree:
def __init__(self, t):
self.root = BTreeNode(t, leaf=True)
self.t = t
def search(self, k, x=None):
if x is None:
x = self.root
i = 0
while i < len(x.keys) and k > x.keys[i]:
i += 1
if i < len(x.keys) and k == x.keys[i]:
return True
elif x.leaf:
return False
else:
return self.search(k, x.children[i])
def insert(self, k):
root = self.root
if len(root.keys) == (2 * self.t) - 1:
new_root = BTreeNode(self.t)
new_root.children.append(self.root)
self.split_child(new_root, 0)
self.root = new_root
self._insert_non_full(self.root, k)
def split_child(self, x, i):
y = x.children[i]
z = BTreeNode(y.t, y.leaf)
x.children.insert(i + 1, z)
x.keys.insert(i, y.keys[self.t - 1])
z.keys = y.keys[self.t : (2 * self.t) - 1]
y.keys = y.keys[0 : self.t - 1]
if not y.leaf:
z.children = y.children[self.t : (2 * self.t)]
y.children = y.children[0 : self.t]
def _insert_non_full(self, x, k):
i = len(x.keys) - 1
if x.leaf:
x.keys.append(None)
while i >= 0 and k < x.keys[i]:
x.keys[i + 1] = x.keys[i]
i -= 1
x.keys[i + 1] = k
else:
while i >= 0 and k < x.keys[i]:
i -= 1
i += 1
if len(x.children[i].keys) == (2 * self.t) - 1:
self.split_child(x, i)
if k > x.keys[i]:
i += 1
self._insert_non_full(x.children[i], k)
# 示例
btree = BTree(3)
keys = [10, 20, 5, 6, 12, 30, 7, 17]
for key in keys:
btree.insert(key)
print("搜索 12:", btree.search(12)) # 输出:搜索 12: True
print("搜索 100:", btree.search(100)) # 输出:搜索 100: False
B+ 树
B+ 树是 B 树的另一种变种,主要用于数据库和文件系统中。它在 B 树的基础上进行了优化,能够更高效地处理范围查询和顺序访问。