Procházet zdrojové kódy

fix: B+树分裂逻辑-使用leftmostKey获取正确的promoKey

修复内容:
1. primary.go: 添加 btLeftmostKey 函数
2. secondary.go: 分裂时使用 sbtLeftmostKey 而非直接复制条目
3. string_btree.go: 已委托给 SBTInsert

根本问题:
- 原实现直接取右兄弟第一个条目的key作为separator
- 但右兄弟可能刚分裂,其第一个条目不一定是子树最小key
- 正确做法是遍历到最左叶节点获取真正的最小key

当前状态:
- 查询失败率从0.4%波动到7%再到0.46%,说明还有其他边界条件bug
- 需要系统性重写B+树并添加完整断言验证

下一步:
- 创建自动化测试覆盖所有分裂场景
- 逐层验证B+树有序性不变量
- 考虑回退到扁平链表作为临时稳定方案
yl32940 před 1 měsícem
rodič
revize
88668d162b
2 změnil soubory, kde provedl 26 přidání a 4 odebrání
  1. 24 2
      DB/index/btree/primary.go
  2. 2 2
      DB/index/btree/secondary.go

+ 24 - 2
DB/index/btree/primary.go

@@ -167,6 +167,28 @@ func btLeafPos(n *btNode, key []byte) int {
 	return cnt
 }
 
+// btLeftmostKey 获取子树最左叶的第一个 key(用于 promoKey)
+func btLeftmostKey(handle page.HandleDb, nodePID uint32) []byte {
+	cur := nodePID
+	for {
+		n := btLoad(handle, cur)
+		if n == nil {
+			return nil
+		}
+		if n.isLeaf() {
+			if n.info.Count == 0 {
+				return nil
+			}
+			e := btEntry(n, 0)
+			key := make([]byte, 8) // 主键固定8字节
+			copy(key, btKey(e))
+			return key
+		}
+		// 非叶节点,继续向左下遍历
+		cur = btPID(btEntry(n, 0))
+	}
+}
+
 // ======================== B+ 鏍戞悳绱?========================
 
 func BTreePKSearch(rootPID uint32, key []byte, handle page.HandleDb) (uint32, bool) {
@@ -396,8 +418,8 @@ func BTreePKInsert(rootPID uint32, key []byte, dpID uint32, handle page.HandleDb
 					btSave(handle, nl)
 				}
 			}
-			pk := make([]byte, 8)
-			copy(pk, rl.entries[:8])
+			// 使用右子树最左叶的第一个 key 作为 promoKey
+			pk := btLeftmostKey(handle, rl.info.Hdr.PageID)
 			nr, err := btPromote(handle, rootPID, path, n.info.Hdr.PageID, pk, rl.info.Hdr.PageID)
 			return nr, 0, false, err
 		}

+ 2 - 2
DB/index/btree/secondary.go

@@ -334,8 +334,8 @@ func SBTInsert(rootPID uint32, entry []byte, handle page.HandleDb) (uint32, erro
 					sbtSave(handle, nl)
 				}
 			}
-			pk := make([]byte, el-8)
-			copy(pk, rl.entries[:el-8])
+			// 使用右子树最左叶的第一个 key 作为 promoKey
+			pk := sbtLeftmostKey(handle, rl.info.Hdr.PageID, el)
 			nr, err := sbtPromote(handle, rootPID, path, n.info.Hdr.PageID, pk, rl.info.Hdr.PageID, el)
 			return nr, err
 		}