
实现一个简单的Database12(译文)
Part 12 扫描多级B-Tree
我们现在支持构建一个多级B-Tree,但是我们在程序中破坏了 select 语句。下面是一个插入15行数据并打印出来的测例。
但是现在当我们运行这个测例的时候,它实际上发生的是:
这很奇怪。它只打印了一行,而且数据看起来发生了损坏(注意输出的 id 和 username 不匹配)。
这个奇怪之处是因为 execute_select() 函数在表的开始位置执行,我们现在的实现 table_start() 函数返回根节点的第0个单元格。但是现在树的跟节点是一个内部节点,它并不包含任何数据行。打印出的数据是根节点当初作为叶子节点时遗留下的。execute_select() 函数应该返回的是最左侧的叶子节点的第0个单元格。
所以现在抛弃旧的实现:
并且添加一个新的实现来搜索 key 0 (可能存在的最小的 key)。即使在表中 key 0不存在,这个方法也会返回最小id的位置(最左叶子节点的开始位置)。
有了这些修改,测例仍然只打印出一个节点的行数:
有15个条目,B-tree包含了一个内部节点和两个叶子节点,看起来就是下面的样子:
为了遍历整个表,我们需要在遍历到第一个节点的结尾时跳到第二个叶子节点(继续遍历)。为了能够这样,我们需要在叶子节点的头部保存一个叫做“next_leaf”的字段,这个字段将保存右边兄弟叶子节点的 page number。而最右侧的叶子节点中next_leaf字段将保存 0 值,来表示它没有兄弟节点(无论如何,page 0 是为表的根节点保留的)。
更新叶子节点的头部格式来包含新的字段:
添加一个方法来访问这个字段:
在初始化一个新的叶子节点的时候,设置 next_leaf 字段的默认值值为0:
每当分裂叶子节点时,更新同级兄弟节点的指针。老叶子节点的兄弟变成了新叶子节点,而新叶子节点的兄弟变成了旧叶子节点的兄弟。
增加几个新的字段,改变几个变量:
现在,每当我们想将游标推进到叶子节点的末尾时,就可以检查叶子节点是否有兄弟节点。如果有,跳到兄弟节点。否则,我们就结束在表的末尾。
有了这些更改之后,我们实际上就可以打印 15 行了...
...但是有一行数据好像损坏了。
在做几次调试之后,我发现问题是因为在分裂叶子节点时的一个bug导致的:
请记住,叶节点中的每个单元格首先包含一个键,然后包含一个值:
我们将新行(值)写入单元格的开头,键应该放在那里。这意味着用户名(username)的一部分进入了id部分(因为疯狂的大id)。在修改bug之后,我们终于按预期打印出了整个表格。
哇哦!一个又一个BUG,但我们正在取得进展。直到下次。
文章转载自公众号:GreatSQL社区
