根本原因定位
部分修复
btLeftmostKey / sbtLeftmostKey / strBtLeftmostKey 辅助函数测试结果
| B+ 树类型 | 路由函数 | 比较操作 | entry.key 语义 |
|---|---|---|---|
| primary.go | btRoute | > (找第一个 > key) |
child[i] 的上界+1 |
| secondary.go | sbtRoute | <= (找最后一个 <= key) |
child[i] 的最小 key |
| string_btree.go | strBtRoute | <= (同 secondary) |
child[i] 的最小 key |
关键发现:primary 和 secondary 使用互斥的路由约定!
分裂时 promoKey 提取 ✅
// 错误:直接复制右兄弟第一个条目
pk := make([]byte, el-8)
copy(pk, rl.entries[:el-8])
// 正确:遍历到最左叶获取真正最小值
pk := sbtLeftmostKey(handle, rl.info.Hdr.PageID, el)
新根创建时的左右 key ⚠️
sbtLeftmostKey(leftPID) 和 promoKey假设: secondary B+ 树使用 <= 路由约定,entry[i].key = child[i] 子树的最小 key
验证:
sbtRoute: 找最后一个 entry.key <= searchKey ✅ 正确sbtChildIdx: 同样使用 <= ✅ 一致entry.key >= promoKey 的位置 ✅ 正确那为什么还有 0.46% 失败?
可能的原因:
sbtLeafPos 二分查找在某些边界条件下返回错误位置sbtEntry(parent, mid) 的 key,应该使用右子树的最左键sbtSave 后立即 sbtLoad 可能读到旧数据添加断言验证
// 每次插入后验证
func assertBTreesValid(rootPID uint32, handle page.HandleDb) {
// 1. 验证所有叶节点条目有序
// 2. 验证内节点 separator 与子树范围一致
// 3. 验证每个 key 都能正确路由到包含它的叶节点
}
定位第一次失败
修复并回归测试
预计时间: 3-5 天
如果短期内无法完全修复,建议字符串索引回退到经过验证的扁平链表实现:
// str_handler.go
type StrListHandler struct{}
func (h *StrListHandler) Insert(...) {
// 线性搜索插入位置 O(n)
// 保证 100% 正确性
}
func (h *StrListHandler) Search(...) {
// 线性搜索 O(n)
// 100% 正确
}
优点: 立即消除所有 bug 缺点: 性能从 O(log n) 降到 O(n),10万行时明显变慢
决策标准:
最后更新: 2026-09-09
当前失败率: 0.46%
下一步: 逐行插入测试定位第一次失败