英文链接:https://cstack.github.io/db_tutorial/parts/part7.html
B-Tree是SQLite用来表示表和索引的数据结构,因此它是一个非常重要的想法。
为什么树对数据库来讲是非常合适的数据结构呢?
- 搜索特定值很快(时间复杂度:对数)
- 插入/删除您已经找到的值很快(重新平衡的时间是常量级的)
- 遍历一系列值很快(与哈希映射不同)
批注:B-tree中的B是指balanced,而不是binary。后者是2叉树,每个节点最多只有2个子节点,而前者可以有2个以上的字节点,记为m,为了保持树大部分平衡,我们还说节点必须至少有m / 2个孩子(四舍五入)。
例外:
- 叶节点有0个孩子
- 根节点可以有少于m个子节点但必须至少有2个
上面的图片是一个B-Tree,SQLite用它来存储索引。为了存储表,SQLites使用称为B +树的变体。
B-tree | B+ tree | |
---|---|---|
Pronounced | “Bee Tree” | “Bee Plus Tree” |
Used to store | Indexes | Tables |
Internal nodes store keys | Yes | Yes |
Internal nodes store values | Yes | No |
Number of children per node | Less | More |
Internal nodes vs. leaf nodes | Same structure | Different structur |
统一2个概念:
Internal nodes:内部节点(分枝节点),即拥有孩子节点,leaf nodes:叶子节点,即没有孩子节点,区别如下:
For an order-m tree… | Internal Node | Leaf Node |
---|---|---|
Stores | keys and pointers to children | keys and values |
Number of keys | up to m-1 | as many as will fit |
Number of pointers | number of keys + 1 | none |
Number of values | none | number of keys |
Key purpose | used for routing | paired with value |
Stores values? | No | Yes |
让我们通过一个例子来看看在插入元素时B树是如何增长的。为了简单起见,树将是订单3.这意味着:
- 每个内部节点最多3个子节点
- 每个内部节点最多2个key
- 每个内部节点至少有2个子节点
- 每个内部节点至少有一个key
空B树具有单个节点:根节点。根节点作为具有零键/值对的叶节点开始:
如果我们插入几个键/值对,它们将按排序顺序存储在叶节点中。
假设叶节点的容量是两个键/值对。当我们插入另一个时,我们必须拆分叶节点并在每个节点中放置一半对。两个节点都成为新内部节点的子节点,现在它将成为根节点。
内部节点有1个键和2个指向子节点的指针。如果我们想要查找小于或等于5的键,我们会查看左边的孩子。如果我们想要查找大于5的key,我们会找到右边的孩子。
现在让我们插入键“2”。首先,如果它存在,我们将查找它将在哪个叶节点,并且我们到达左叶节点。节点已满,因此我们拆分叶节点并在父节点中创建新条目。
让我们继续添加key。我们到了必须再次拆分的地步,但父节点中没有空间用于另一个键/指针对。
解决方案是将根节点拆分为两个内部节点,然后创建新的根节点作为其父节点。
当我们拆分根节点时,树的深度才会增加。每个叶节点具有相同的深度并且接近相同数量的键/值对,因此树保持平衡并且快速搜索。
在我们实现插入之后,我将推迟讨论从树中删除key的问题。
当我们实现这个数据结构时,每个节点将对应一个页面。根节点将存在于第0页中。子指针将只是包含子节点的页码。
下一次,我们开始实施btree!