main.go 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174
  1. package main
  2. // =============================================================================
  3. // B+ 树独立验证 Demo
  4. //
  5. // 目的: 绕开真实 DB 文件 / WAL / BufferPool 等上层, 直接用「内存页存储」驱动
  6. // B+ 树公开 API, 主动确认是否存在结构或查找错误。
  7. //
  8. // 覆盖路径:
  9. // - 插入: 叶子分裂 -> 内部节点分裂(升级根)
  10. // - 查找: 逐键精确查找
  11. // - 删除: 借位(左/右兄弟) -> 叶子合并 -> 内部节点合并
  12. //
  13. // 校验手段(两套独立证据):
  14. // 1) 功能查找: 所有"存活键"必须查得到且值正确; 已删除键必须查不到
  15. // 2) 结构校验: 父键 == 子树最小键 / 相邻子树严格分离 / 叶子链顺序 /
  16. // 叶子条目集合 == 逻辑存活集合
  17. // =============================================================================
  18. import (
  19. "TsDB2/DB/core/index/btree"
  20. "TsDB2/DB/core/page"
  21. "bytes"
  22. "encoding/binary"
  23. "fmt"
  24. "math/rand"
  25. "sort"
  26. "unsafe"
  27. )
  28. var totalFail int
  29. func report(ok bool, msg string) {
  30. if ok {
  31. fmt.Printf(" [PASS] %s\n", msg)
  32. } else {
  33. totalFail++
  34. fmt.Printf(" [FAIL] %s\n", msg)
  35. }
  36. }
  37. // =============================================================================
  38. // 内存页存储 (实现 page.HandleDb)
  39. // =============================================================================
  40. type memStore struct {
  41. pages map[uint32][]byte
  42. next uint32
  43. }
  44. func newMemStore() *memStore {
  45. return &memStore{pages: make(map[uint32][]byte), next: 1}
  46. }
  47. func (m *memStore) handle() page.HandleDb {
  48. return page.HandleDb{
  49. NewPage: func() uint32 {
  50. pid := m.next
  51. m.next++
  52. return pid
  53. },
  54. FreePage: func(pid uint32) { delete(m.pages, pid) },
  55. SavePage: func(pid uint32, buf []byte) error {
  56. cp := make([]byte, len(buf))
  57. copy(cp, buf)
  58. m.pages[pid] = cp
  59. return nil
  60. },
  61. ReadPage: func(pid uint32, buf []byte) error {
  62. p, ok := m.pages[pid]
  63. if !ok {
  64. return fmt.Errorf("page %d not found", pid)
  65. }
  66. copy(buf, p)
  67. return nil
  68. },
  69. }
  70. }
  71. // =============================================================================
  72. // 原始节点解析 (不依赖 btree 包内部函数)
  73. // =============================================================================
  74. type rawNode struct {
  75. buf []byte
  76. head *page.IndexHead
  77. entries []byte
  78. }
  79. func loadNode(h page.HandleDb, pid uint32) *rawNode {
  80. buf := make([]byte, page.IndexPageSize)
  81. if err := h.ReadPage(pid, buf); err != nil {
  82. return nil
  83. }
  84. head := (*page.IndexHead)(unsafe.Pointer(&buf[0]))
  85. return &rawNode{buf: buf, head: head, entries: buf[page.IndexHeadSize:]}
  86. }
  87. func (n *rawNode) el() int { return int(n.head.EntryLen) }
  88. func (n *rawNode) cnt() int { return int(n.head.Count) }
  89. func (n *rawNode) leaf() bool { return n.head.IsLeaf == 1 }
  90. func (n *rawNode) entry(i int) []byte {
  91. el := n.el()
  92. return n.entries[i*el : (i+1)*el]
  93. }
  94. // kb 把 key 的前 8 字节按大端整数打印, 便于阅读
  95. func kb(k []byte) string {
  96. if len(k) >= 8 {
  97. return fmt.Sprintf("%d", binary.BigEndian.Uint64(k[:8]))
  98. }
  99. return fmt.Sprintf("%x", k)
  100. }
  101. func intKey(e []byte) []byte { return e[:len(e)-4] }
  102. func childPID(e []byte) uint32 { return binary.BigEndian.Uint32(e[len(e)-4:]) }
  103. func leafKey(e []byte, secondary bool) []byte {
  104. if secondary {
  105. return e[:len(e)-8] // [key + PKID(8)]
  106. }
  107. return e[:len(e)-4] // [key(8) + dpID(4)]
  108. }
  109. func leftmostKeyOf(h page.HandleDb, pid uint32, secondary bool) ([]byte, bool) {
  110. for {
  111. n := loadNode(h, pid)
  112. if n == nil {
  113. return nil, false
  114. }
  115. if n.leaf() {
  116. if n.cnt() == 0 {
  117. return nil, false
  118. }
  119. k := leafKey(n.entry(0), secondary)
  120. out := make([]byte, len(k))
  121. copy(out, k)
  122. return out, true
  123. }
  124. if n.cnt() == 0 {
  125. return nil, false
  126. }
  127. pid = childPID(n.entry(0))
  128. }
  129. }
  130. func rightmostKeyOf(h page.HandleDb, pid uint32, secondary bool) ([]byte, bool) {
  131. for {
  132. n := loadNode(h, pid)
  133. if n == nil {
  134. return nil, false
  135. }
  136. if n.leaf() {
  137. if n.cnt() == 0 {
  138. return nil, false
  139. }
  140. k := leafKey(n.entry(n.cnt()-1), secondary)
  141. out := make([]byte, len(k))
  142. copy(out, k)
  143. return out, true
  144. }
  145. if n.cnt() == 0 {
  146. return nil, false
  147. }
  148. pid = childPID(n.entry(n.cnt() - 1))
  149. }
  150. }
  151. // leafChain 从最左叶沿 NextPage 走完整条叶子链
  152. func leafChain(h page.HandleDb, root uint32) []uint32 {
  153. if root == 0 {
  154. return nil
  155. }
  156. pid := root
  157. for {
  158. n := loadNode(h, pid)
  159. if n == nil {
  160. return nil
  161. }
  162. if n.leaf() {
  163. break
  164. }
  165. if n.cnt() == 0 {
  166. return nil
  167. }
  168. pid = childPID(n.entry(0))
  169. }
  170. var out []uint32
  171. seen := map[uint32]bool{}
  172. for pid != 0 && !seen[pid] {
  173. seen[pid] = true
  174. out = append(out, pid)
  175. n := loadNode(h, pid)
  176. if n == nil {
  177. break
  178. }
  179. pid = n.head.Hdr.NextPage
  180. }
  181. return out
  182. }
  183. // =============================================================================
  184. // 结构校验器
  185. // =============================================================================
  186. func validateTree(h page.HandleDb, root uint32, secondary bool) []string {
  187. var issues []string
  188. if root == 0 {
  189. return issues
  190. }
  191. visited := map[uint32]bool{}
  192. var leafSeq []uint32
  193. var walk func(pid uint32, depth int)
  194. walk = func(pid uint32, depth int) {
  195. if visited[pid] {
  196. issues = append(issues, fmt.Sprintf("节点被重复引用/成环 pid=%d", pid))
  197. return
  198. }
  199. visited[pid] = true
  200. n := loadNode(h, pid)
  201. if n == nil {
  202. issues = append(issues, fmt.Sprintf("加载失败 pid=%d", pid))
  203. return
  204. }
  205. // 根节点为空叶是合法的"空树"; 非根节点为空则说明删除收尾有问题
  206. if n.cnt() == 0 && depth > 0 {
  207. issues = append(issues, fmt.Sprintf("节点为空 pid=%d leaf=%v", pid, n.leaf()))
  208. }
  209. if n.leaf() {
  210. for i := 1; i < n.cnt(); i++ {
  211. if bytes.Compare(leafKey(n.entry(i-1), secondary), leafKey(n.entry(i), secondary)) >= 0 {
  212. issues = append(issues, fmt.Sprintf("叶节点键未严格递增 pid=%d idx=%d", pid, i))
  213. }
  214. }
  215. leafSeq = append(leafSeq, pid)
  216. return
  217. }
  218. // 内部节点: 条目有序
  219. for i := 1; i < n.cnt(); i++ {
  220. if bytes.Compare(intKey(n.entry(i-1)), intKey(n.entry(i))) >= 0 {
  221. issues = append(issues, fmt.Sprintf("内部节点键未严格递增 pid=%d idx=%d", pid, i))
  222. }
  223. }
  224. for i := 0; i < n.cnt(); i++ {
  225. cp := childPID(n.entry(i))
  226. ck, ok := leftmostKeyOf(h, cp, secondary)
  227. if !ok {
  228. issues = append(issues, fmt.Sprintf("内部节点 pid=%d 的第 %d 个子节点无最左键(空?) pid=%d", pid, i, cp))
  229. continue
  230. }
  231. stored := intKey(n.entry(i))
  232. if secondary {
  233. // 二级树内部键 = key + 4B 填充, 取前缀比较
  234. if len(stored) < len(ck) || !bytes.Equal(stored[:len(ck)], ck) {
  235. issues = append(issues, fmt.Sprintf("父键 != 子树最小键 pid=%d entry%d storedKey=%s childMin=%s", pid, i, kb(stored), kb(ck)))
  236. }
  237. } else if !bytes.Equal(stored, ck) {
  238. issues = append(issues, fmt.Sprintf("父键 != 子树最小键 pid=%d entry%d storedKey=%s childMin=%s", pid, i, kb(stored), kb(ck)))
  239. }
  240. if i > 0 {
  241. pm, ok2 := rightmostKeyOf(h, childPID(n.entry(i-1)), secondary)
  242. if !ok2 || bytes.Compare(pm, ck) >= 0 {
  243. issues = append(issues, fmt.Sprintf("相邻子树未严格分离 pid=%d child%d.max >= child%d.min", pid, i-1, i))
  244. }
  245. }
  246. walk(cp, depth+1)
  247. }
  248. }
  249. walk(root, 0)
  250. chain := leafChain(h, root)
  251. if len(chain) != len(leafSeq) {
  252. issues = append(issues, fmt.Sprintf("叶子链长度=%d 与 DFS 可达叶子数=%d 不一致", len(chain), len(leafSeq)))
  253. } else {
  254. for i := range chain {
  255. if chain[i] != leafSeq[i] {
  256. issues = append(issues, fmt.Sprintf("叶子链顺序与树结构不一致 idx=%d chain=%d dfs=%d", i, chain[i], leafSeq[i]))
  257. break
  258. }
  259. }
  260. }
  261. return issues
  262. }
  263. // collectLeafEntries 沿叶子链收集所有条目 (hex), 排序返回
  264. func collectLeafEntries(h page.HandleDb, root uint32) []string {
  265. var out []string
  266. for _, pid := range leafChain(h, root) {
  267. n := loadNode(h, pid)
  268. if n == nil {
  269. continue
  270. }
  271. el := n.el()
  272. for i := 0; i < n.cnt(); i++ {
  273. out = append(out, fmt.Sprintf("%x", n.entry(i)[:el]))
  274. }
  275. }
  276. sort.Strings(out)
  277. return out
  278. }
  279. func sameStrings(a, b []string) bool {
  280. if len(a) != len(b) {
  281. return false
  282. }
  283. for i := range a {
  284. if a[i] != b[i] {
  285. return false
  286. }
  287. }
  288. return true
  289. }
  290. func stats(h page.HandleDb, root uint32) (leafCount, entryCount, depth int) {
  291. if root == 0 {
  292. return 0, 0, 0
  293. }
  294. var walk func(pid uint32, d int)
  295. walk = func(pid uint32, d int) {
  296. n := loadNode(h, pid)
  297. if n == nil {
  298. return
  299. }
  300. if d+1 > depth {
  301. depth = d + 1
  302. }
  303. if n.leaf() {
  304. leafCount++
  305. entryCount += n.cnt()
  306. return
  307. }
  308. for i := 0; i < n.cnt(); i++ {
  309. walk(childPID(n.entry(i)), d+1)
  310. }
  311. }
  312. walk(root, 0)
  313. return
  314. }
  315. // =============================================================================
  316. // 通用验证
  317. // =============================================================================
  318. type treeAPI struct {
  319. name string
  320. secondary bool
  321. keyOf func(v uint64) []byte
  322. search func(root uint32, key []byte, h page.HandleDb) (uint64, bool)
  323. entryHex func(v uint64) string
  324. }
  325. func verify(api treeAPI, title string, root uint32, h page.HandleDb, alive, dead []uint64) {
  326. leafCount, entryCount, depth := stats(h, root)
  327. fmt.Printf("\n--- %s ---\n", title)
  328. fmt.Printf(" [info] root=%d 叶子数=%d 条目数=%d 层数=%d 逻辑存活=%d\n",
  329. root, leafCount, entryCount, depth, len(alive))
  330. // 1) 存活键必须可查
  331. miss := 0
  332. wrongVal := 0
  333. var missSamples, wrongSamples []uint64
  334. for _, v := range alive {
  335. got, ok := api.search(root, api.keyOf(v), h)
  336. if !ok {
  337. miss++
  338. if len(missSamples) < 8 {
  339. missSamples = append(missSamples, v)
  340. }
  341. continue
  342. }
  343. if got != v {
  344. wrongVal++
  345. if len(wrongSamples) < 8 {
  346. wrongSamples = append(wrongSamples, v)
  347. }
  348. }
  349. }
  350. report(miss == 0, fmt.Sprintf("查找存活键: %d/%d 命中, 缺失 %d", len(alive)-miss, len(alive), miss))
  351. if len(missSamples) > 0 {
  352. fmt.Printf(" [样例] 查不到的存活键: %v\n", missSamples)
  353. }
  354. report(wrongVal == 0, fmt.Sprintf("查找值正确性: 值错误 %d", wrongVal))
  355. if len(wrongSamples) > 0 {
  356. fmt.Printf(" [样例] 返回值错误的键: %v\n", wrongSamples)
  357. }
  358. // 2) 已删除键必须查不到
  359. if len(dead) > 0 {
  360. ghost := 0
  361. var ghostSamples []uint64
  362. for _, v := range dead {
  363. if _, ok := api.search(root, api.keyOf(v), h); ok {
  364. ghost++
  365. if len(ghostSamples) < 8 {
  366. ghostSamples = append(ghostSamples, v)
  367. }
  368. }
  369. }
  370. report(ghost == 0, fmt.Sprintf("已删除键不应被查到: 幽灵 %d/%d", ghost, len(dead)))
  371. if len(ghostSamples) > 0 {
  372. fmt.Printf(" [样例] 删除后仍能查到的键: %v\n", ghostSamples)
  373. }
  374. }
  375. // 3) 结构校验
  376. issues := validateTree(h, root, api.secondary)
  377. report(len(issues) == 0, fmt.Sprintf("结构校验: 问题 %d 条", len(issues)))
  378. for i, s := range issues {
  379. if i >= 12 {
  380. fmt.Printf(" ... 其余 %d 条省略\n", len(issues)-i)
  381. break
  382. }
  383. fmt.Printf(" - %s\n", s)
  384. }
  385. // 4) 叶子条目集合 == 逻辑存活集合
  386. got := collectLeafEntries(h, root)
  387. want := make([]string, 0, len(alive))
  388. for _, v := range alive {
  389. want = append(want, api.entryHex(v))
  390. }
  391. sort.Strings(want)
  392. report(sameStrings(got, want), fmt.Sprintf("叶子条目集合 == 逻辑存活集合 (叶子=%d 逻辑=%d)", len(got), len(want)))
  393. if !sameStrings(got, want) {
  394. gm, wm := map[string]bool{}, map[string]bool{}
  395. for _, s := range got {
  396. gm[s] = true
  397. }
  398. for _, s := range want {
  399. wm[s] = true
  400. }
  401. lost, extra := 0, 0
  402. for _, s := range want {
  403. if !gm[s] {
  404. lost++
  405. }
  406. }
  407. for _, s := range got {
  408. if !wm[s] {
  409. extra++
  410. }
  411. }
  412. fmt.Printf(" [样例] 叶子中丢失 %d 条, 多出 %d 条\n", lost, extra)
  413. }
  414. }
  415. // =============================================================================
  416. // 主键 B+ 树测试
  417. // =============================================================================
  418. func testPrimary() {
  419. fmt.Println("\n================ 主键 B+ 树 (BTreePKInsert / Search / Delete) ================")
  420. h := newMemStore().handle()
  421. const N = 2500
  422. api := treeAPI{
  423. name: "primary",
  424. secondary: false,
  425. keyOf: u64key,
  426. search: func(root uint32, key []byte, hh page.HandleDb) (uint64, bool) {
  427. dp, ok := btree.BTreePKSearch(root, key, hh)
  428. return uint64(dp), ok
  429. },
  430. entryHex: func(v uint64) string { return pkEntryHex(v, uint32(v)) },
  431. }
  432. // ---- 阶段 1: 全量插入 ----
  433. root := uint32(0)
  434. insertErr := 0
  435. for i := 1; i <= N; i++ {
  436. r, _, _, err := btree.BTreePKInsert(root, u64key(uint64(i)), uint32(i), h)
  437. if err != nil {
  438. insertErr++
  439. if insertErr <= 5 {
  440. fmt.Printf(" [FAIL] 插入 key=%d 出错: %v\n", i, err)
  441. }
  442. continue
  443. }
  444. root = r
  445. }
  446. report(insertErr == 0, fmt.Sprintf("插入 %d 个键", N))
  447. alive := make([]uint64, 0, N)
  448. for i := 1; i <= N; i++ {
  449. alive = append(alive, uint64(i))
  450. }
  451. verify(api, "阶段1: 全量插入后", root, h, alive, nil)
  452. // ---- 阶段 2: 删除偶数键 (打散, 触发合并/借位) ----
  453. var dead []uint64
  454. var nextAlive []uint64
  455. delErr := 0
  456. for _, v := range alive {
  457. if v%2 != 0 {
  458. nextAlive = append(nextAlive, v)
  459. continue
  460. }
  461. r, _, err := btree.BTreePKDelete(root, u64key(v), h)
  462. if err != nil {
  463. delErr++
  464. if delErr <= 5 {
  465. fmt.Printf(" [FAIL] 删除 key=%d 出错: %v\n", v, err)
  466. }
  467. nextAlive = append(nextAlive, v) // 删不掉则仍视为存活
  468. continue
  469. }
  470. root = r
  471. dead = append(dead, v)
  472. }
  473. report(delErr == 0, fmt.Sprintf("删除偶数键 (%d 个)", len(dead)))
  474. verify(api, "阶段2: 删除偶数键后", root, h, nextAlive, dead)
  475. // ---- 阶段 3: 逆序删除剩余全部键 ----
  476. cur := append([]uint64(nil), nextAlive...)
  477. sort.Slice(cur, func(i, j int) bool { return cur[i] > cur[j] })
  478. delErr = 0
  479. for _, v := range cur {
  480. r, _, err := btree.BTreePKDelete(root, u64key(v), h)
  481. if err != nil {
  482. delErr++
  483. if delErr <= 5 {
  484. fmt.Printf(" [FAIL] 删除 key=%d 出错: %v\n", v, err)
  485. }
  486. continue
  487. }
  488. root = r
  489. dead = append(dead, v)
  490. }
  491. report(delErr == 0, fmt.Sprintf("逆序删除剩余 %d 个键", len(cur)))
  492. verify(api, "阶段3: 全部删除后", root, h, nil, dead)
  493. }
  494. // =============================================================================
  495. // 二级 B+ 树测试
  496. // =============================================================================
  497. func testSecondary() {
  498. fmt.Println("\n================ 二级 B+ 树 (SBTInsert / SBTSearch / SBTDelete) ================")
  499. h := newMemStore().handle()
  500. const N = 2500
  501. api := treeAPI{
  502. name: "secondary",
  503. secondary: true,
  504. keyOf: u64key,
  505. search: func(root uint32, key []byte, hh page.HandleDb) (uint64, bool) {
  506. return btree.SBTSearch(root, key, hh)
  507. },
  508. entryHex: func(v uint64) string { return sbtEntryHex(v, v) },
  509. }
  510. // ---- 阶段 1: 顺序插入 ----
  511. root := uint32(0)
  512. insertErr := 0
  513. for i := 1; i <= N; i++ {
  514. r, err := btree.SBTInsert(root, sbtEntryBytes(uint64(i), uint64(i)), h)
  515. if err != nil {
  516. insertErr++
  517. if insertErr <= 5 {
  518. fmt.Printf(" [FAIL] 插入 key=%d 出错: %v\n", i, err)
  519. }
  520. continue
  521. }
  522. root = r
  523. }
  524. report(insertErr == 0, fmt.Sprintf("插入 %d 个条目", N))
  525. alive := make([]uint64, 0, N)
  526. for i := 1; i <= N; i++ {
  527. alive = append(alive, uint64(i))
  528. }
  529. verify(api, "阶段1: 全量插入后", root, h, alive, nil)
  530. // ---- 阶段 2: 删除偶数键 ----
  531. var dead []uint64
  532. var nextAlive []uint64
  533. delErr := 0
  534. for _, v := range alive {
  535. if v%2 != 0 {
  536. nextAlive = append(nextAlive, v)
  537. continue
  538. }
  539. r, err := btree.SBTDelete(root, u64key(v), h)
  540. if err != nil {
  541. delErr++
  542. if delErr <= 5 {
  543. fmt.Printf(" [FAIL] 删除 key=%d 出错: %v\n", v, err)
  544. }
  545. nextAlive = append(nextAlive, v)
  546. continue
  547. }
  548. root = r
  549. dead = append(dead, v)
  550. }
  551. report(delErr == 0, fmt.Sprintf("删除偶数键 (%d 个)", len(dead)))
  552. verify(api, "阶段2: 删除偶数键后", root, h, nextAlive, dead)
  553. // ---- 阶段 3: 逆序删除剩余全部 ----
  554. cur := append([]uint64(nil), nextAlive...)
  555. sort.Slice(cur, func(i, j int) bool { return cur[i] > cur[j] })
  556. delErr = 0
  557. for _, v := range cur {
  558. r, err := btree.SBTDelete(root, u64key(v), h)
  559. if err != nil {
  560. delErr++
  561. if delErr <= 5 {
  562. fmt.Printf(" [FAIL] 删除 key=%d 出错: %v\n", v, err)
  563. }
  564. continue
  565. }
  566. root = r
  567. dead = append(dead, v)
  568. }
  569. report(delErr == 0, fmt.Sprintf("逆序删除剩余 %d 个键", len(cur)))
  570. verify(api, "阶段3: 全部删除后", root, h, nil, dead)
  571. }
  572. // =============================================================================
  573. // 二级 B+ 树 (大键) 测试: 用小容量节点强制「内部节点分裂」, 得到 3 层树
  574. // =============================================================================
  575. const bigKeyLen = 248 // key 长度 (字节); 使单节点仅容纳 (8192-32)/256 = 31 个子节点
  576. func bigKey(v uint64) []byte {
  577. b := make([]byte, bigKeyLen)
  578. binary.BigEndian.PutUint64(b, v)
  579. return b
  580. }
  581. func bigEntry(k, pkid uint64) []byte {
  582. b := make([]byte, bigKeyLen+8)
  583. binary.BigEndian.PutUint64(b, k)
  584. binary.BigEndian.PutUint64(b[bigKeyLen:], pkid)
  585. return b
  586. }
  587. // mkKey 生成指定长度的索引键 (前 8 字节为大端整数, 其余补零)
  588. func mkKey(keyLen int, v uint64) []byte {
  589. b := make([]byte, keyLen)
  590. binary.BigEndian.PutUint64(b, v)
  591. return b
  592. }
  593. // mkEntry 生成二级索引条目 [key(keyLen) + pkID(8)]
  594. func mkEntry(keyLen int, k, pkid uint64) []byte {
  595. b := make([]byte, keyLen+8)
  596. binary.BigEndian.PutUint64(b, k)
  597. binary.BigEndian.PutUint64(b[keyLen:], pkid)
  598. return b
  599. }
  600. func testSecondaryBigKey() {
  601. fmt.Println("\n============ 二级 B+ 树 (keyLen=248, 强制内部节点分裂 => 3 层) ============")
  602. h := newMemStore().handle()
  603. const N = 1500
  604. api := treeAPI{
  605. name: "secondary-bigkey",
  606. secondary: true,
  607. keyOf: bigKey,
  608. search: func(root uint32, key []byte, hh page.HandleDb) (uint64, bool) {
  609. return btree.SBTSearch(root, key, hh)
  610. },
  611. entryHex: func(v uint64) string { return fmt.Sprintf("%x", bigEntry(v, v)) },
  612. }
  613. root := uint32(0)
  614. insertErr := 0
  615. for i := 1; i <= N; i++ {
  616. r, err := btree.SBTInsert(root, bigEntry(uint64(i), uint64(i)), h)
  617. if err != nil {
  618. insertErr++
  619. if insertErr <= 5 {
  620. fmt.Printf(" [FAIL] 插入 key=%d 出错: %v\n", i, err)
  621. }
  622. continue
  623. }
  624. root = r
  625. }
  626. report(insertErr == 0, fmt.Sprintf("插入 %d 个大键条目", N))
  627. alive := make([]uint64, 0, N)
  628. for i := 1; i <= N; i++ {
  629. alive = append(alive, uint64(i))
  630. }
  631. verify(api, "阶段1: 大键全量插入后(应为 3 层)", root, h, alive, nil)
  632. }
  633. // =============================================================================
  634. // 定向测试: 触发「从右兄弟借位」
  635. // 触发条件: 待删除叶子位于 childIdx==0 (没有左兄弟), 且右兄弟条目数 > 1
  636. // 做法: 先建树, 再清空最左叶子的全部键
  637. // =============================================================================
  638. func apiPrimary() treeAPI {
  639. return treeAPI{
  640. name: "primary",
  641. secondary: false,
  642. keyOf: u64key,
  643. search: func(root uint32, key []byte, hh page.HandleDb) (uint64, bool) {
  644. dp, ok := btree.BTreePKSearch(root, key, hh)
  645. return uint64(dp), ok
  646. },
  647. entryHex: func(v uint64) string { return pkEntryHex(v, uint32(v)) },
  648. }
  649. }
  650. func apiSecondary() treeAPI {
  651. return treeAPI{
  652. name: "secondary",
  653. secondary: true,
  654. keyOf: u64key,
  655. search: func(root uint32, key []byte, hh page.HandleDb) (uint64, bool) {
  656. return btree.SBTSearch(root, key, hh)
  657. },
  658. entryHex: func(v uint64) string { return sbtEntryHex(v, v) },
  659. }
  660. }
  661. func k2v8(k []byte) uint64 { return binary.BigEndian.Uint64(k[:8]) }
  662. // collectLeafKeys 取指定叶子页中所有条目的索引键
  663. func collectLeafKeys(h page.HandleDb, pid uint32, secondary bool) [][]byte {
  664. n := loadNode(h, pid)
  665. if n == nil {
  666. return nil
  667. }
  668. out := make([][]byte, 0, n.cnt())
  669. for i := 0; i < n.cnt(); i++ {
  670. k := leafKey(n.entry(i), secondary)
  671. c := make([]byte, len(k))
  672. copy(c, k)
  673. out = append(out, c)
  674. }
  675. return out
  676. }
  677. func deleteLeftmostLeaf(title string, api treeAPI, root uint32, h page.HandleDb, n int,
  678. remove func(root uint32, v uint64, h page.HandleDb) (uint32, error)) uint32 {
  679. chain := leafChain(h, root)
  680. if len(chain) < 2 {
  681. report(false, title+": 前置条件不足(叶子数<2), 无法触发借位")
  682. return root
  683. }
  684. keys := collectLeafKeys(h, chain[0], api.secondary)
  685. fmt.Printf(" [info] 最左叶子 pid=%d 含 %d 个键\n", chain[0], len(keys))
  686. dead := map[uint64]bool{}
  687. errCnt := 0
  688. for _, k := range keys {
  689. v := k2v8(k)
  690. r, err := remove(root, v, h)
  691. if err != nil {
  692. errCnt++
  693. if errCnt <= 5 {
  694. fmt.Printf(" [FAIL] %s: 删除最左叶子键 %d 出错: %v\n", title, v, err)
  695. }
  696. continue
  697. }
  698. root = r
  699. dead[v] = true
  700. }
  701. report(errCnt == 0, fmt.Sprintf("%s: 清空最左叶子 (%d 个键, 失败 %d)", title, len(keys), errCnt))
  702. var alive, deadList []uint64
  703. for i := 1; i <= n; i++ {
  704. if dead[uint64(i)] {
  705. deadList = append(deadList, uint64(i))
  706. } else {
  707. alive = append(alive, uint64(i))
  708. }
  709. }
  710. verify(api, "清空最左叶子后", root, h, alive, deadList)
  711. return root
  712. }
  713. func testPrimaryBorrowRight() {
  714. fmt.Println("\n========= 主键 B+ 树: 定向触发「从右兄弟借位」=========")
  715. h := newMemStore().handle()
  716. const N = 20000
  717. root := uint32(0)
  718. for i := 1; i <= N; i++ {
  719. r, _, _, err := btree.BTreePKInsert(root, u64key(uint64(i)), uint32(i), h)
  720. if err != nil {
  721. fmt.Printf(" [FAIL] 插入 %d 出错: %v\n", i, err)
  722. return
  723. }
  724. root = r
  725. }
  726. deleteLeftmostLeaf("主键树", apiPrimary(), root, h, N,
  727. func(r uint32, v uint64, hh page.HandleDb) (uint32, error) {
  728. nr, _, err := btree.BTreePKDelete(r, u64key(v), hh)
  729. return nr, err
  730. })
  731. }
  732. func testSecondaryBorrowRight() {
  733. fmt.Println("\n========= 二级 B+ 树(大键,3层): 定向触发「从右兄弟借位」=========")
  734. h := newMemStore().handle()
  735. const N = 3000
  736. root := uint32(0)
  737. for i := 1; i <= N; i++ {
  738. r, err := btree.SBTInsert(root, bigEntry(uint64(i), uint64(i)), h)
  739. if err != nil {
  740. fmt.Printf(" [FAIL] 插入 %d 出错: %v\n", i, err)
  741. return
  742. }
  743. root = r
  744. }
  745. api := apiSecondary()
  746. api.keyOf = bigKey
  747. api.entryHex = func(v uint64) string { return fmt.Sprintf("%x", bigEntry(v, v)) }
  748. deleteLeftmostLeaf("二级树", api, root, h, N,
  749. func(r uint32, v uint64, hh page.HandleDb) (uint32, error) {
  750. return btree.SBTDelete(r, bigKey(v), hh)
  751. })
  752. }
  753. // =============================================================================
  754. // 批量建树 SBTBuildFromSorted (deleter.go 压缩流程会用到)
  755. // =============================================================================
  756. func testSBTBuildFromSorted() {
  757. fmt.Println("\n========= 二级 B+ 树: SBTBuildFromSorted 批量建树 ===========")
  758. cases := []struct {
  759. name string
  760. bigKey bool
  761. n int
  762. }{
  763. {"小键(2层)", false, 2000},
  764. {"大键(3层)", true, 1500},
  765. }
  766. for _, c := range cases {
  767. h := newMemStore().handle()
  768. entries := make([][]byte, 0, c.n)
  769. for i := 1; i <= c.n; i++ {
  770. if c.bigKey {
  771. entries = append(entries, bigEntry(uint64(i), uint64(i)))
  772. } else {
  773. entries = append(entries, sbtEntryBytes(uint64(i), uint64(i)))
  774. }
  775. }
  776. root := btree.SBTBuildFromSorted(entries, h)
  777. api := apiSecondary()
  778. if c.bigKey {
  779. api.keyOf = bigKey
  780. api.entryHex = func(v uint64) string { return fmt.Sprintf("%x", bigEntry(v, v)) }
  781. }
  782. alive := make([]uint64, 0, c.n)
  783. for i := 1; i <= c.n; i++ {
  784. alive = append(alive, uint64(i))
  785. }
  786. verify(api, fmt.Sprintf("SBTBuildFromSorted %s n=%d", c.name, c.n), root, h, alive, nil)
  787. }
  788. }
  789. // =============================================================================
  790. // 深层树验证: 强制 4 层以上, 证明分裂/提升递归没有层数上限
  791. //
  792. // 原理: 节点容量 ≈ (btSplitThreshold - IndexHeadSize) / (keyLen+8)。
  793. // 键长越大, 单节点能放的条目越少, 用较少的数据量即可把树逼到很深。
  794. // 覆盖三条会产生/消除高度的路径:
  795. // 1) 逐条插入 -> 内部节点递归分裂, 树不停长高
  796. // 2) 逆序删除 -> 深层合并/借位 + 根逐层收缩
  797. // 3) 批量建树 -> 自底向上循环, 层数同样不受限
  798. // =============================================================================
  799. func testDeepTree() {
  800. fmt.Println("\n========= 深层树验证 (强制 4 层以上, 验证深度可无限分裂) ==========")
  801. cases := []struct {
  802. keyLen int
  803. n int
  804. minDep int
  805. }{
  806. {1000, 4000, 4}, // 单节点约 8 条 -> 预期 5~6 层
  807. {2000, 1500, 4}, // 单节点约 4 条 -> 预期 7~8 层
  808. }
  809. for _, c := range cases {
  810. kl, n := c.keyLen, c.n
  811. h := newMemStore().handle()
  812. api := apiSecondary()
  813. api.keyOf = func(v uint64) []byte { return mkKey(kl, v) }
  814. api.entryHex = func(v uint64) string { return fmt.Sprintf("%x", mkEntry(kl, v, v)) }
  815. // --- 1) 逐条插入 ---
  816. root := uint32(0)
  817. insErr := 0
  818. for i := 1; i <= n; i++ {
  819. r, err := btree.SBTInsert(root, mkEntry(kl, uint64(i), uint64(i)), h)
  820. if err != nil {
  821. insErr++
  822. if insErr <= 5 {
  823. fmt.Printf(" [FAIL] keyLen=%d 插入 %d 出错: %v\n", kl, i, err)
  824. }
  825. continue
  826. }
  827. root = r
  828. }
  829. report(insErr == 0, fmt.Sprintf("keyLen=%d 逐条插入 %d 条", kl, n))
  830. _, _, depth := stats(h, root)
  831. report(depth >= c.minDep, fmt.Sprintf("keyLen=%d 插入后树高=%d (要求 >=%d: 已突破 3 层)",
  832. kl, depth, c.minDep))
  833. alive := make([]uint64, 0, n)
  834. for i := 1; i <= n; i++ {
  835. alive = append(alive, uint64(i))
  836. }
  837. verify(api, fmt.Sprintf("深层树(keyLen=%d,n=%d) 全量校验", kl, n), root, h, alive, nil)
  838. // --- 2) 逆序删除全部: 深层合并/借位 + 根逐层收缩 ---
  839. delErr := 0
  840. midIssues := 0
  841. step := n / 8
  842. if step < 1 {
  843. step = 1
  844. }
  845. for i := n; i >= 1; i-- {
  846. r, err := btree.SBTDelete(root, mkKey(kl, uint64(i)), h)
  847. if err != nil {
  848. delErr++
  849. if delErr <= 5 {
  850. fmt.Printf(" [FAIL] keyLen=%d 删除 %d 出错: %v\n", kl, i, err)
  851. }
  852. continue
  853. }
  854. root = r
  855. if i%step == 0 {
  856. if iss := validateTree(h, root, true); len(iss) > 0 {
  857. midIssues++
  858. if midIssues <= 3 {
  859. fmt.Printf(" [info] keyLen=%d 剩 %d 条时结构问题 %d: %s\n", kl, i-1, len(iss), iss[0])
  860. }
  861. }
  862. }
  863. }
  864. report(midIssues == 0, fmt.Sprintf("keyLen=%d 删除过程中(每%d步采样)结构始终保持", kl, step))
  865. report(delErr == 0, fmt.Sprintf("keyLen=%d 逆序删除全部 %d 条", kl, n))
  866. verify(api, fmt.Sprintf("深层树(keyLen=%d) 删除全部后", kl), root, h, nil, alive)
  867. // --- 3) SBTBuildFromSorted 批量建树 ---
  868. h2 := newMemStore().handle()
  869. entries := make([][]byte, 0, n)
  870. for i := 1; i <= n; i++ {
  871. entries = append(entries, mkEntry(kl, uint64(i), uint64(i)))
  872. }
  873. root2 := btree.SBTBuildFromSorted(entries, h2)
  874. _, _, depth2 := stats(h2, root2)
  875. report(depth2 >= c.minDep, fmt.Sprintf("keyLen=%d 批量建树树高=%d (要求 >=%d)",
  876. kl, depth2, c.minDep))
  877. verify(api, fmt.Sprintf("深层批量建树(keyLen=%d,n=%d)", kl, n), root2, h2, alive, nil)
  878. }
  879. }
  880. // =============================================================================
  881. // 随机压测: 乱序全量插入 -> 随机增删, 每轮做结构校验 + 随机抽样查找
  882. // =============================================================================
  883. type stressConfig struct {
  884. title string
  885. api treeAPI
  886. insert func(root uint32, v uint64, h page.HandleDb) (uint32, error)
  887. remove func(root uint32, v uint64, h page.HandleDb) (uint32, error)
  888. keySpace uint64
  889. rounds int
  890. batch int
  891. seed int64
  892. }
  893. func runStress(h page.HandleDb, c stressConfig) {
  894. fmt.Printf("\n--- 随机压测 %s (keySpace=%d, %d 轮 x %d 次) ---\n",
  895. c.title, c.keySpace, c.rounds, c.batch)
  896. rng := rand.New(rand.NewSource(c.seed))
  897. root := uint32(0)
  898. alive := map[uint64]bool{}
  899. deadSet := map[uint64]bool{}
  900. opErr := 0
  901. mark := func(format string, a ...interface{}) {
  902. opErr++
  903. if opErr <= 8 {
  904. fmt.Printf(" [FAIL] "+format+"\n", a...)
  905. }
  906. }
  907. // 阶段 A: 乱序全量插入
  908. for _, idx := range rng.Perm(int(c.keySpace)) {
  909. v := uint64(idx) + 1
  910. nr, err := c.insert(root, v, h)
  911. if err != nil {
  912. mark("%s 乱序插入 %d 出错: %v", c.title, v, err)
  913. continue
  914. }
  915. root = nr
  916. alive[v] = true
  917. }
  918. report(opErr == 0, fmt.Sprintf("%s 阶段A: 乱序全量插入 %d 个键", c.title, c.keySpace))
  919. // 阶段 B: 以删除为主的随机增删, 迫使不断合并/借位/收缩
  920. for r := 0; r < c.rounds; r++ {
  921. for b := 0; b < c.batch; b++ {
  922. v := uint64(rng.Int63n(int64(c.keySpace))) + 1
  923. if alive[v] {
  924. nr, err := c.remove(root, v, h)
  925. if err != nil {
  926. mark("%s 第%d轮 删除 %d 出错: %v", c.title, r, v, err)
  927. continue
  928. }
  929. root = nr
  930. delete(alive, v)
  931. deadSet[v] = true
  932. } else if rng.Intn(10) < 3 {
  933. nr, err := c.insert(root, v, h)
  934. if err != nil {
  935. mark("%s 第%d轮 插入 %d 出错: %v", c.title, r, v, err)
  936. continue
  937. }
  938. root = nr
  939. alive[v] = true
  940. delete(deadSet, v)
  941. }
  942. }
  943. // 每轮: 结构校验
  944. issues := validateTree(h, root, c.api.secondary)
  945. if len(issues) > 0 {
  946. mark("%s 第%d轮 结构问题 %d 条, 例: %s", c.title, r, len(issues), issues[0])
  947. }
  948. // 每轮: 随机抽样查找
  949. for s := 0; s < 300; s++ {
  950. v := uint64(rng.Int63n(int64(c.keySpace))) + 1
  951. _, found := c.api.search(root, c.api.keyOf(v), h)
  952. if alive[v] && !found {
  953. mark("%s 第%d轮: 存活键 %d 查不到", c.title, r, v)
  954. }
  955. if !alive[v] && found {
  956. mark("%s 第%d轮: 已删/未插入的键 %d 被查到", c.title, r, v)
  957. }
  958. }
  959. }
  960. report(opErr == 0, fmt.Sprintf("%s 阶段B: 随机增删 %d 轮, 累计异常 %d", c.title, c.rounds, opErr))
  961. aliveList := make([]uint64, 0, len(alive))
  962. for v := range alive {
  963. aliveList = append(aliveList, v)
  964. }
  965. deadList := make([]uint64, 0, len(deadSet))
  966. for v := range deadSet {
  967. deadList = append(deadList, v)
  968. }
  969. verify(c.api, "压测结束: 全量校验", root, h, aliveList, deadList)
  970. }
  971. func testStress() {
  972. {
  973. h := newMemStore().handle()
  974. runStress(h, stressConfig{
  975. title: "主键树",
  976. api: apiPrimary(),
  977. keySpace: 3000,
  978. rounds: 120,
  979. batch: 200,
  980. seed: 20260910,
  981. insert: func(root uint32, v uint64, hh page.HandleDb) (uint32, error) {
  982. nr, _, _, err := btree.BTreePKInsert(root, u64key(v), uint32(v), hh)
  983. return nr, err
  984. },
  985. remove: func(root uint32, v uint64, hh page.HandleDb) (uint32, error) {
  986. nr, _, err := btree.BTreePKDelete(root, u64key(v), hh)
  987. return nr, err
  988. },
  989. })
  990. }
  991. {
  992. // 大键 -> 每节点仅 31 条, 3 层树, 能充分触发分裂/合并/借位
  993. h := newMemStore().handle()
  994. api := apiSecondary()
  995. api.keyOf = bigKey
  996. api.entryHex = func(v uint64) string { return fmt.Sprintf("%x", bigEntry(v, v)) }
  997. runStress(h, stressConfig{
  998. title: "二级树(大键,3层)",
  999. api: api,
  1000. keySpace: 2000,
  1001. rounds: 120,
  1002. batch: 120,
  1003. seed: 20260911,
  1004. insert: func(root uint32, v uint64, hh page.HandleDb) (uint32, error) {
  1005. return btree.SBTInsert(root, bigEntry(v, v), hh)
  1006. },
  1007. remove: func(root uint32, v uint64, hh page.HandleDb) (uint32, error) {
  1008. return btree.SBTDelete(root, bigKey(v), hh)
  1009. },
  1010. })
  1011. }
  1012. {
  1013. // 超长键(keyLen=1000) -> 每节点仅约 8 条, 树高 5+,
  1014. // 随机增删时每轮都做结构校验, 覆盖深层分裂/合并/借位与根收缩。
  1015. const kl = 1000
  1016. h := newMemStore().handle()
  1017. api := apiSecondary()
  1018. api.keyOf = func(v uint64) []byte { return mkKey(kl, v) }
  1019. api.entryHex = func(v uint64) string { return fmt.Sprintf("%x", mkEntry(kl, v, v)) }
  1020. runStress(h, stressConfig{
  1021. title: "二级树(深,keyLen=1000)",
  1022. api: api,
  1023. keySpace: 2500,
  1024. rounds: 80,
  1025. batch: 150,
  1026. seed: 20260912,
  1027. insert: func(root uint32, v uint64, hh page.HandleDb) (uint32, error) {
  1028. return btree.SBTInsert(root, mkEntry(kl, v, v), hh)
  1029. },
  1030. remove: func(root uint32, v uint64, hh page.HandleDb) (uint32, error) {
  1031. return btree.SBTDelete(root, mkKey(kl, v), hh)
  1032. },
  1033. })
  1034. }
  1035. }
  1036. // =============================================================================
  1037. // 工具
  1038. // =============================================================================
  1039. func u64key(v uint64) []byte {
  1040. b := make([]byte, 8)
  1041. binary.BigEndian.PutUint64(b, v)
  1042. return b
  1043. }
  1044. func pkEntryHex(k uint64, dp uint32) string {
  1045. b := make([]byte, 12)
  1046. binary.BigEndian.PutUint64(b, k)
  1047. binary.BigEndian.PutUint32(b[8:], dp)
  1048. return fmt.Sprintf("%x", b)
  1049. }
  1050. func sbtEntryHex(k uint64, pkid uint64) string {
  1051. return fmt.Sprintf("%x", sbtEntryBytes(k, pkid))
  1052. }
  1053. func sbtEntryBytes(k uint64, pkid uint64) []byte {
  1054. b := make([]byte, 16)
  1055. binary.BigEndian.PutUint64(b, k)
  1056. binary.BigEndian.PutUint64(b[8:], pkid)
  1057. return b
  1058. }
  1059. func main() {
  1060. // 降低填充因子, 在数据量较小的情况下也能触发分裂/合并
  1061. page.BTreeFillFactor = 50
  1062. fmt.Println("============================================================")
  1063. fmt.Println(" TsDB2 B+ 树独立验证 (内存页存储, 不涉及真实 DB 文件)")
  1064. fmt.Printf(" 页大小=%d 页头=%d 填充因子=%d%% 分裂阈值=%d\n",
  1065. page.IndexPageSize, page.IndexHeadSize, page.BTreeFillFactor,
  1066. page.IndexPageSize*page.BTreeFillFactor/100)
  1067. fmt.Println("============================================================")
  1068. testPrimary()
  1069. testSecondary()
  1070. testSecondaryBigKey()
  1071. testSBTBuildFromSorted()
  1072. testDeepTree()
  1073. testPrimaryBorrowRight()
  1074. testSecondaryBorrowRight()
  1075. testStress()
  1076. fmt.Println("\n============================================================")
  1077. if totalFail == 0 {
  1078. fmt.Println(" 结论: 全部检查通过 (未发现 B+ 树问题)")
  1079. } else {
  1080. fmt.Printf(" 结论: 共发现 %d 项检查失败, B+ 树存在问题\n", totalFail)
  1081. }
  1082. fmt.Println("============================================================")
  1083. }