secondary.go 45 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454
  1. package btree
  2. import (
  3. "bytes"
  4. "encoding/binary"
  5. "errors"
  6. "fmt"
  7. "unsafe"
  8. "git.ycl.pub/yl32940/TsDB2/DB/core/page"
  9. )
  10. // ======================== 二级 B+ 树索引操作 ========================
  11. // 叶节点 [key(keyLen) + PKID(8B)] entryLen = keyLen + 8
  12. // 内节点 [key(keyLen) + PKID(8B) + childPID(4B)] entryLen = keyLen + 12
  13. //
  14. // 全树条目按复合键 (key, pkID) 全局有序; 内部条目的前 keyLen+8 字节 = 其子树的
  15. // 最小复合键 [key|pkID] (分隔键), 末 4 字节 = 子页号。复合分隔键让 (key, pkID)
  16. // 的点路由精确成立: 删除/插入一次下降即命中目标叶, 不必在重复 key 跨越的兄弟
  17. // 子树间逐棵试探 (旧格式分隔键只有 key, Age 每值 2 万条跨几十个叶时删除退化为
  18. // 平均半个重复区段的页加载, 实测 ~2.5ms/行)。
  19. // 分裂: 统一 50/50 (不做顺序优化, 二级索引键可能非自增)
  20. // 合并: InnoDB 风格 — 删除后检查当前页+兄弟页能否放进一页
  21. // ----- 节点 I/O -----
  22. func sbtNew(handle page.HandleDb, isLeaf bool, entryLen int) *btNode {
  23. pid := handle.NewPage()
  24. buf := nodeBufGet()
  25. clear(buf) // 池化缓冲可能带旧节点数据, 条目区必须回到"新页=全零"语义
  26. info := (*page.IndexHead)(unsafe.Pointer(&buf[0]))
  27. info.Hdr.Type = page.IndexDataIndex
  28. info.Hdr.PageID = pid
  29. info.Hdr.UseSize = uint32(page.IndexHeadSize)
  30. info.IndexType = page.IndexTypeBTree
  31. info.EntryLen = uint16(entryLen)
  32. info.Count = 0
  33. if isLeaf {
  34. info.IsLeaf = 1
  35. }
  36. return &btNode{buf: buf, info: info, entries: buf[page.IndexHeadSize:]}
  37. }
  38. func sbtLoad(handle page.HandleDb, pid uint32) *btNode {
  39. if pid == 0 {
  40. return nil
  41. }
  42. buf := nodeBufGet()
  43. if err := handle.ReadPage(pid, buf); err != nil {
  44. nodeBufPut(buf)
  45. return nil
  46. }
  47. info := (*page.IndexHead)(unsafe.Pointer(&buf[0]))
  48. return &btNode{buf: buf, info: info, entries: buf[page.IndexHeadSize:]}
  49. }
  50. func sbtSave(handle page.HandleDb, n *btNode) {
  51. _ = handle.SavePage(n.info.Hdr.PageID, n.buf)
  52. }
  53. // ----- 条目操作 -----
  54. func sbtEntry(n *btNode, i int) []byte {
  55. el := int(n.info.EntryLen)
  56. return n.entries[i*el : (i+1)*el]
  57. }
  58. func sbtLeafKey(e []byte) []byte { return e[:len(e)-8] }
  59. func sbtPKID(e []byte) uint64 { return binary.BigEndian.Uint64(e[len(e)-8:]) }
  60. func sbtIntKey(e []byte) []byte { return e[:len(e)-12] } // 内部条目的 key 部分
  61. func sbtIntPKID(e []byte) uint64 { return binary.BigEndian.Uint64(e[len(e)-12:]) } // 内部条目的 pkID 部分
  62. func sbtChildPID(e []byte) uint32 { return binary.BigEndian.Uint32(e[len(e)-4:]) }
  63. func sbtSetChildPID(e []byte, pid uint32) { binary.BigEndian.PutUint32(e[len(e)-4:], pid) }
  64. func sbtCmp(a, b []byte) int {
  65. return bytes.Compare(a, b)
  66. }
  67. // sbtCmpKeyPK 复合键 (key, pkID) 的全序比较: 先比 key, 再比 pkID。
  68. func sbtCmpKeyPK(keyA []byte, pkA uint64, keyB []byte, pkB uint64) int {
  69. if c := sbtCmp(keyA, keyB); c != 0 {
  70. return c
  71. }
  72. switch {
  73. case pkA < pkB:
  74. return -1
  75. case pkA > pkB:
  76. return 1
  77. }
  78. return 0
  79. }
  80. // sbtNodeMinKey 返回节点最左条目的复合键 [key|pkID] (长度 keyLen+8)。
  81. // 叶节点取第一个条目本身 (叶条目就是 [key|pkID]); 内部节点取第一条目的前
  82. // EntryLen-4 字节 (即其子树的最小复合键)。该值用于维护
  83. // "父条目复合键 == 子树最小复合键" 不变式。
  84. func sbtNodeMinKey(n *btNode) []byte {
  85. if n == nil || n.info.Count == 0 {
  86. return nil
  87. }
  88. e := sbtEntry(n, 0)
  89. if n.isLeaf() {
  90. return e
  91. }
  92. return e[:len(e)-4]
  93. }
  94. // sbtFirstLeaf 沿最左分支下降, 返回以 pid 为根的子树中最左叶子页 ID (找不到返回 0)。
  95. func sbtFirstLeaf(handle page.HandleDb, pid uint32) uint32 {
  96. for pid != 0 {
  97. n := sbtLoad(handle, pid)
  98. if n == nil {
  99. return 0
  100. }
  101. if n.isLeaf() {
  102. return pid
  103. }
  104. if n.info.Count == 0 {
  105. return 0
  106. }
  107. pid = sbtChildPID(sbtEntry(n, 0))
  108. }
  109. return 0
  110. }
  111. // sbtLastLeaf 沿最右分支下降, 返回以 pid 为根的子树中最右叶子页 ID (找不到返回 0)。
  112. func sbtLastLeaf(handle page.HandleDb, pid uint32) uint32 {
  113. for pid != 0 {
  114. n := sbtLoad(handle, pid)
  115. if n == nil {
  116. return 0
  117. }
  118. if n.isLeaf() {
  119. return pid
  120. }
  121. if n.info.Count == 0 {
  122. return 0
  123. }
  124. pid = sbtChildPID(sbtEntry(n, int(n.info.Count)-1))
  125. }
  126. return 0
  127. }
  128. // sbtUnlinkSubtreeLeaves 把以 pid 为根的子树所覆盖的那一段叶子, 从叶子双向链表中整体摘除。
  129. // 仅在该子树已被确认 "完全为空" 时调用, 用于避免删除收尾阶段残留空叶子导致
  130. // "叶子链长度 != DFS 可达叶子数"。
  131. func sbtUnlinkSubtreeLeaves(handle page.HandleDb, pid uint32) {
  132. first := sbtFirstLeaf(handle, pid)
  133. if first == 0 {
  134. return
  135. }
  136. last := sbtLastLeaf(handle, pid)
  137. fl := sbtLoad(handle, first)
  138. ll := sbtLoad(handle, last)
  139. if fl == nil || ll == nil {
  140. return
  141. }
  142. pre := fl.info.Hdr.PrePage
  143. next := ll.info.Hdr.NextPage
  144. if pre != 0 {
  145. pr := sbtLoad(handle, pre)
  146. if pr != nil {
  147. pr.info.Hdr.NextPage = next
  148. sbtSave(handle, pr)
  149. }
  150. }
  151. if next != 0 {
  152. nl := sbtLoad(handle, next)
  153. if nl != nil {
  154. nl.info.Hdr.PrePage = pre
  155. sbtSave(handle, nl)
  156. }
  157. }
  158. }
  159. // ----- 内节点路由 -----
  160. func sbtRoute(n *btNode, key []byte) uint32 {
  161. cnt := int(n.info.Count)
  162. if cnt <= 0 {
  163. return 0 // 防御: 空节点不应作为路由节点
  164. }
  165. // 二分查找: 找最后一个 entry.key <= key 的位置
  166. left, right := 0, cnt-1
  167. result := 0
  168. for left <= right {
  169. mid := (left + right) / 2
  170. if sbtCmp(sbtIntKey(sbtEntry(n, mid)), key) <= 0 {
  171. result = mid
  172. left = mid + 1 // 继续向右找
  173. } else {
  174. right = mid - 1
  175. }
  176. }
  177. return sbtChildPID(sbtEntry(n, result))
  178. }
  179. func sbtChildIdx(n *btNode, key []byte) int {
  180. cnt := int(n.info.Count)
  181. if cnt <= 0 {
  182. return 0 // 防御: 空节点不应作为路由节点
  183. }
  184. // 二分查找: 找最后一个 entry.key <= key 的位置 (只按 key 路由)
  185. left, right := 0, cnt-1
  186. result := 0
  187. for left <= right {
  188. mid := (left + right) / 2
  189. if sbtCmp(sbtIntKey(sbtEntry(n, mid)), key) <= 0 {
  190. result = mid
  191. left = mid + 1 // 继续向右找
  192. } else {
  193. right = mid - 1
  194. }
  195. }
  196. return result
  197. }
  198. // sbtChildIdxPK 复合键路由: 找最后一个 "子树最小复合键 <= (key, pkID)" 的子树下标。
  199. //
  200. // 全树按 (key, pkID) 全局有序时, 各子树在复合键空间上是连续区间, 该路由唯一
  201. // 确定目标条目所在 (或应当插入) 的子树 —— 插入与 (key,pkID) 精确删除都用它,
  202. // 一次下降即可命中, 无需向左试探兄弟子树。
  203. func sbtChildIdxPK(n *btNode, key []byte, pkID uint64) int {
  204. cnt := int(n.info.Count)
  205. if cnt <= 0 {
  206. return 0 // 防御: 空节点不应作为路由节点
  207. }
  208. left, right := 0, cnt-1
  209. result := 0
  210. for left <= right {
  211. mid := (left + right) / 2
  212. e := sbtEntry(n, mid)
  213. if sbtCmpKeyPK(sbtIntKey(e), sbtIntPKID(e), key, pkID) <= 0 {
  214. result = mid
  215. left = mid + 1
  216. } else {
  217. right = mid - 1
  218. }
  219. }
  220. return result
  221. }
  222. // ----- 叶节点操作 -----
  223. func sbtInsEntry(n *btNode, entry []byte, pos int) {
  224. el := int(n.info.EntryLen)
  225. cnt := int(n.info.Count)
  226. // 整段后移一格 (块拷贝): 逐条目循环移位在千条目级叶上每插一条要搬千次,
  227. // 批量灌叶路径实测占 ~4% CPU; Go 的 copy 是 memmove 语义, 区间重叠安全。
  228. copy(n.entries[(pos+1)*el:(cnt+1)*el], n.entries[pos*el:cnt*el])
  229. copy(n.entries[pos*el:(pos+1)*el], entry)
  230. n.info.Count++
  231. n.info.Hdr.UseSize += uint32(el)
  232. }
  233. func sbtRmEntry(n *btNode, pos int) {
  234. el := int(n.info.EntryLen)
  235. cnt := int(n.info.Count)
  236. for i := pos; i < cnt-1; i++ {
  237. copy(sbtEntry(n, i), sbtEntry(n, i+1))
  238. }
  239. n.info.Count--
  240. n.info.Hdr.UseSize -= uint32(el)
  241. }
  242. func sbtLeafFind(n *btNode, key []byte) (int, bool) {
  243. el := int(n.info.EntryLen)
  244. cnt := int(n.info.Count)
  245. if cnt == 0 {
  246. return 0, false
  247. }
  248. left, right := 0, cnt-1
  249. for left <= right {
  250. mid := (left + right) / 2
  251. cmp := sbtCmp(sbtEntry(n, mid)[:el-8], key)
  252. if cmp == 0 {
  253. return mid, true
  254. } else if cmp < 0 {
  255. left = mid + 1
  256. } else {
  257. right = mid - 1
  258. }
  259. }
  260. return left, false
  261. }
  262. func sbtLeafPos(n *btNode, key []byte) int {
  263. pos, _ := sbtLeafFind(n, key)
  264. return pos
  265. }
  266. // sbtLeafLowerBound 返回第一个 key >= 目标 key 的条目下标 (leftmost lower_bound)。
  267. //
  268. // 二级索引允许重复 key, 同一 key 的条目在叶内是连续区段。普通二分 (sbtLeafFind)
  269. // 命中相等 key 时会立即返回, 落点可能位于区段中部; 若调用方只从落点向右扫描,
  270. // 就会漏掉落点左侧的重复条目。定位"相同 key 区段"的起点必须用本函数。
  271. func sbtLeafLowerBound(n *btNode, key []byte) int {
  272. el := int(n.info.EntryLen)
  273. cnt := int(n.info.Count)
  274. left, right := 0, cnt
  275. for left < right {
  276. mid := (left + right) / 2
  277. if sbtCmp(sbtEntry(n, mid)[:el-8], key) < 0 {
  278. left = mid + 1
  279. } else {
  280. right = mid
  281. }
  282. }
  283. return left
  284. }
  285. // sbtLeafLowerBoundPK 返回第一个复合键 (key, pkID) >= 目标的条目下标。
  286. //
  287. // 叶内条目按复合键有序 (重复 key 的条目按 pkID 排列), 因此插入定位与
  288. // (key, pkID) 精确查找都应使用本函数, 而不是"key 落点 + 同 key 区段线性扫描"。
  289. func sbtLeafLowerBoundPK(n *btNode, key []byte, pkID uint64) int {
  290. cnt := int(n.info.Count)
  291. left, right := 0, cnt
  292. for left < right {
  293. mid := (left + right) / 2
  294. e := sbtEntry(n, mid)
  295. if sbtCmpKeyPK(sbtLeafKey(e), sbtPKID(e), key, pkID) < 0 {
  296. left = mid + 1
  297. } else {
  298. right = mid
  299. }
  300. }
  301. return left
  302. }
  303. // sbtLeafHasKey 判断叶节点中是否存在等于 key 的条目 (基于 lower_bound 判定)
  304. func sbtLeafHasKey(n *btNode, key []byte) bool {
  305. el := int(n.info.EntryLen)
  306. pos := sbtLeafLowerBound(n, key)
  307. return pos < int(n.info.Count) && sbtCmp(sbtEntry(n, pos)[:el-8], key) == 0
  308. }
  309. // sbtLeftmostLeafForKey 返回"可能包含 key"的最左叶子页 ID。
  310. //
  311. // 内部节点的分隔键是子树的"最小键", sbtRoute 取"最后一个 分隔键<=key 的子树"。
  312. // 当同一 key 的条目因叶分裂而跨越相邻两叶 (左叶尾键 == 右叶首键) 时,
  313. // sbtRoute 只会落到右侧那一叶, 从而漏掉位于左叶尾部的重复条目。
  314. // 因此在路由落点的基础上, 沿叶子前驱链向左回溯: 只要前驱叶的最大键 >= key,
  315. // 该前驱叶就仍可能含有 key, 必须一并纳入扫描范围。
  316. func sbtLeftmostLeafForKey(rootPID uint32, key []byte, handle page.HandleDb) uint32 {
  317. pid := rootPID
  318. for pid != 0 {
  319. n := sbtLoad(handle, pid)
  320. if n == nil {
  321. return 0
  322. }
  323. if n.isLeaf() {
  324. break
  325. }
  326. pid = sbtRoute(n, key)
  327. }
  328. for pid != 0 {
  329. n := sbtLoad(handle, pid)
  330. if n == nil || n.info.Count == 0 {
  331. return 0
  332. }
  333. pre := n.info.Hdr.PrePage
  334. if pre == 0 {
  335. return pid
  336. }
  337. pn := sbtLoad(handle, pre)
  338. if pn == nil || pn.info.Count == 0 || pn.info.IsLeaf == 0 {
  339. return pid
  340. }
  341. el := int(n.info.EntryLen)
  342. lastKey := sbtEntry(pn, int(pn.info.Count)-1)[:el-8]
  343. if sbtCmp(lastKey, key) < 0 {
  344. return pid
  345. }
  346. pid = pre
  347. }
  348. return 0
  349. }
  350. // ======================== 按 key+pkID 精确删除 ========================
  351. // SBTDeleteWithPKID 删除指定 key + pkID 的条目(处理重复 key 场景)。
  352. //
  353. // 采用与 sbtDeleteRec 完全一致的递归下溢处理: 叶被删空时由父层 sbtHandleUnderflow
  354. // 借位/合并, 从而不会残留 "空叶可达" (否则会破坏 叶子链 == DFS 可达叶 的不变式,
  355. // 并让 sbtRoute 落到空叶导致查找失败)。
  356. func SBTDeleteWithPKID(rootPID uint32, key []byte, pkID uint64, handle page.HandleDb) (uint32, error) {
  357. if rootPID == 0 {
  358. return 0, page.ErrNotFound
  359. }
  360. found, err := sbtDeleteRecPK(rootPID, key, pkID, nil, handle)
  361. if err != nil {
  362. return rootPID, err
  363. }
  364. if !found {
  365. return rootPID, page.ErrNotFound
  366. }
  367. // 收尾归一化: 与 SBTDelete 一致
  368. newRoot := rootPID
  369. // 归一化过程中被跳过的旧根 (降高时旧根整体退役; 删空时旧根被换成新建空叶)。
  370. // 收集后在本函数末尾才归还, 避免 sbtNew 在同一轮里把这些页又分配回来。
  371. var retiredRoots []uint32
  372. for {
  373. n := sbtLoad(handle, newRoot)
  374. if n == nil || n.isLeaf() {
  375. break
  376. }
  377. if n.info.Count == 1 {
  378. childPID := sbtChildPID(sbtEntry(n, 0))
  379. if childPID == 0 {
  380. break
  381. }
  382. retiredRoots = append(retiredRoots, newRoot)
  383. newRoot = childPID
  384. continue
  385. }
  386. if n.info.Count == 0 {
  387. // n 必为内部节点 (循环开头已 break 叶子): 新建空叶要用叶条目长度
  388. // (内部 EntryLen keyLen+12 - 4 = keyLen+8)
  389. leaf := sbtNew(handle, true, int(n.info.EntryLen)-4)
  390. sbtSave(handle, leaf)
  391. retiredRoots = append(retiredRoots, newRoot)
  392. newRoot = leaf.pageID()
  393. }
  394. break
  395. }
  396. // 只归还退役的旧根自身: Count==1 时其子树仍是新根 (可达), 绝不能按子树回收;
  397. // Count==0 时该节点已是空内部节点, 其子页在上面的递归里已随空子树摘链归还。
  398. for _, pid := range retiredRoots {
  399. btReleasePage(handle, pid)
  400. }
  401. return newRoot, nil
  402. }
  403. // sbtDeleteRecPK 按 (key, pkID) 精确删除, 并复用 sbtDeleteRec 的下溢/合并/归一化流程。
  404. //
  405. // 内部节点的分隔键是子树的最小复合键 (key, pkID), 子树在复合键空间上是连续区间,
  406. // 因此 sbtChildIdxPK 一次路由即唯一确定目标条目所在的子树 —— 不再需要旧实现
  407. // "从 key 路由落点向左逐棵子树试探" 的兜底 (重复 key 跨几十个叶时那是平均半个
  408. // 重复区段的页加载)。
  409. func sbtDeleteRecPK(nodePID uint32, key []byte, pkID uint64, path []btPathEntry, handle page.HandleDb) (bool, error) {
  410. node := sbtLoad(handle, nodePID)
  411. if node == nil {
  412. return false, nil
  413. }
  414. el := int(node.info.EntryLen)
  415. if node.isLeaf() {
  416. return sbtDeleteLeafPK(node, key, pkID, path, handle), nil
  417. }
  418. if node.info.Count == 0 {
  419. return false, nil
  420. }
  421. ci := sbtChildIdxPK(node, key, pkID)
  422. childPID := sbtChildPID(sbtEntry(node, ci))
  423. newPath := make([]btPathEntry, len(path)+1)
  424. copy(newPath, path)
  425. newPath[len(path)] = btPathEntry{nodePID: nodePID, childIdx: ci}
  426. found, err := sbtDeleteRecPK(childPID, key, pkID, newPath, handle)
  427. if err != nil {
  428. return false, err
  429. }
  430. if !found {
  431. return false, nil
  432. }
  433. // 命中并已在该子树内删除: 处理本层的下溢/最小键/归一化 (与 sbtDeleteRec 相同)
  434. node = sbtLoad(handle, nodePID)
  435. if node == nil {
  436. return true, errors.New("sbt reload node failed")
  437. }
  438. if node.info.Count > 0 && ci < int(node.info.Count) && sbtChildPID(sbtEntry(node, ci)) == childPID {
  439. child := sbtLoad(handle, childPID)
  440. if child != nil {
  441. if child.info.Count == 0 {
  442. sbtHandleUnderflow(node, ci, handle)
  443. node = sbtLoad(handle, nodePID)
  444. if node == nil {
  445. return true, errors.New("sbt reload node failed after underflow")
  446. }
  447. } else {
  448. // 维护不变式: 父条目复合键 == 子树最小复合键 (删除叶子最小键后父键会变旧)
  449. copy(sbtEntry(node, ci)[:el-4], sbtNodeMinKey(child))
  450. }
  451. }
  452. }
  453. if !node.isLeaf() && node.info.Count == 1 {
  454. c0 := sbtChildPID(sbtEntry(node, 0))
  455. cn := sbtLoad(handle, c0)
  456. if cn != nil && cn.info.Count == 0 {
  457. sbtUnlinkSubtreeLeaves(handle, c0)
  458. node.info.Count = 0
  459. node.info.Hdr.UseSize = uint32(page.IndexHeadSize)
  460. sbtSave(handle, node)
  461. // c0 子树整段摘链后不再被任何父条目或叶子链引用, 连同其内部节点
  462. // 一并归还 —— 否则每删空一棵子树就漏掉一批索引页。
  463. btReleaseSubtree(handle, c0)
  464. return true, nil
  465. }
  466. }
  467. sbtSave(handle, node)
  468. return true, nil
  469. }
  470. // sbtDeleteLeafPK 在叶内删除指定 (key, pkID); 命中返回 true。
  471. // 下溢处理与 sbtDeleteRec 的叶分支保持一致。
  472. func sbtDeleteLeafPK(node *btNode, key []byte, pkID uint64, path []btPathEntry, handle page.HandleDb) bool {
  473. el := int(node.info.EntryLen)
  474. cnt := int(node.info.Count)
  475. // 叶内条目按复合键有序: 复合下界一次定位, 无需同 key 区段线性扫描
  476. pos := sbtLeafLowerBoundPK(node, key, pkID)
  477. if pos >= cnt {
  478. return false
  479. }
  480. entry := sbtEntry(node, pos)
  481. if sbtCmp(sbtLeafKey(entry), key) != 0 || sbtPKID(entry) != pkID {
  482. return false
  483. }
  484. sbtRmEntry(node, pos)
  485. if node.info.Count == 0 && len(path) == 0 {
  486. sbtSave(handle, node)
  487. return true
  488. }
  489. sbtSave(handle, node)
  490. if len(path) > 0 && node.info.Count > 0 {
  491. sbtTryMerge(handle, path, node, el)
  492. }
  493. return true
  494. }
  495. // ======================== 查找所有相同 key 的 pkID ========================
  496. // SBTSearchAll 查找相同 key 下的所有 pkID
  497. func SBTSearchAll(rootPID uint32, key []byte, handle page.HandleDb) ([]uint64, bool) {
  498. if rootPID == 0 {
  499. return nil, false
  500. }
  501. var result []uint64
  502. for pid := sbtLeftmostLeafForKey(rootPID, key, handle); pid != 0; {
  503. n := sbtLoad(handle, pid)
  504. if n == nil {
  505. return nil, false
  506. }
  507. el := int(n.info.EntryLen)
  508. cnt := int(n.info.Count)
  509. // 用 leftmost lower_bound 定位相同 key 区段起点, 避免漏掉左侧重复条目
  510. pos := sbtLeafLowerBound(n, key)
  511. if pos < cnt {
  512. if sbtCmp(sbtEntry(n, pos)[:el-8], key) != 0 {
  513. break // 已越过相同 key 区段
  514. }
  515. // 收集本叶中所有相同 key 的 pkID
  516. for i := pos; i < cnt; i++ {
  517. entry := sbtEntry(n, i)
  518. if sbtCmp(entry[:el-8], key) != 0 {
  519. break
  520. }
  521. result = append(result, sbtPKID(entry))
  522. }
  523. }
  524. pid = n.info.Hdr.NextPage
  525. }
  526. if len(result) == 0 {
  527. return nil, false
  528. }
  529. return result, true
  530. }
  531. // ======================== 搜索 ========================
  532. func SBTSearch(rootPID uint32, key []byte, handle page.HandleDb) (uint64, bool) {
  533. if rootPID == 0 {
  534. return 0, false
  535. }
  536. // 从左起第一个可能含 key 的叶子开始, 沿 NextPage 右扫, 返回最左(最小 pkID)的条目
  537. for pid := sbtLeftmostLeafForKey(rootPID, key, handle); pid != 0; {
  538. n := sbtLoad(handle, pid)
  539. if n == nil {
  540. return 0, false
  541. }
  542. el := int(n.info.EntryLen)
  543. cnt := int(n.info.Count)
  544. pos := sbtLeafLowerBound(n, key)
  545. if pos < cnt {
  546. if sbtCmp(sbtEntry(n, pos)[:el-8], key) == 0 {
  547. return sbtPKID(sbtEntry(n, pos)), true
  548. }
  549. return 0, false // 已越过相同 key 区段
  550. }
  551. pid = n.info.Hdr.NextPage
  552. }
  553. return 0, false
  554. }
  555. // ======================== 范围扫描 ========================
  556. func SBTRangeScan(rootPID uint32, handle page.HandleDb,
  557. startKey, endKey []byte, startInc, endInc bool, reverse bool,
  558. callback func(row []byte) bool) error {
  559. if rootPID == 0 {
  560. return nil
  561. }
  562. if !reverse {
  563. cur := rootPID
  564. if startKey != nil {
  565. // 必须用 sbtLeftmostLeafForKey 定位"可能含 startKey 的最左叶"。
  566. // sbtRoute 找的是"最后一个分隔键 <= startKey 的子树", 落点是该 key 区段的
  567. // 最右叶; 当同一 key 因叶分裂跨越相邻两叶 (左叶尾键 == 右叶首键) 时,
  568. // 左侧叶子里的重复条目会被整段跳过 —— 等值查询 (startKey == endKey)
  569. // 表现为只返回最右叶那部分: 实测每键 2500 条只回 256 条, 每键 1000 条只回 184 条。
  570. // 全范围扫描 (startKey == nil) 不受影响, 故只有带边界的查询出错。
  571. cur = sbtLeftmostLeafForKey(rootPID, startKey, handle)
  572. } else {
  573. for {
  574. n := sbtLoad(handle, cur)
  575. if n == nil {
  576. return nil
  577. }
  578. if n.isLeaf() {
  579. break
  580. }
  581. // startKey 为空时, 显式路由到最左叶 (跟随第一个子节点)
  582. cur = sbtChildPID(sbtEntry(n, 0))
  583. }
  584. }
  585. for cur != 0 {
  586. n := sbtLoad(handle, cur)
  587. if n == nil {
  588. break
  589. }
  590. if n.info.Hdr.Type != page.IndexDataIndex {
  591. return fmt.Errorf("btree: 正向范围扫描遇到非索引页 pid=%d type=%d, 叶子链已损坏",
  592. n.info.Hdr.PageID, n.info.Hdr.Type)
  593. }
  594. el := int(n.info.EntryLen)
  595. for i := 0; i < int(n.info.Count); i++ {
  596. entry := sbtEntry(n, i)
  597. key := entry[:el-8]
  598. if endKey != nil {
  599. cmp := sbtCmp(key, endKey)
  600. if cmp > 0 || (cmp == 0 && !endInc) {
  601. return nil
  602. }
  603. }
  604. if startKey != nil {
  605. cmp := sbtCmp(key, startKey)
  606. if cmp < 0 || (cmp == 0 && !startInc) {
  607. continue
  608. }
  609. }
  610. pkID := entry[el-8:]
  611. if !callback(pkID) {
  612. return nil
  613. }
  614. }
  615. cur = n.info.Hdr.NextPage
  616. }
  617. } else {
  618. // 反向: 路由到 endKey 或最右叶, 沿 PrePage 遍历
  619. cur := rootPID
  620. if endKey != nil {
  621. for {
  622. n := sbtLoad(handle, cur)
  623. if n == nil {
  624. return nil
  625. }
  626. if n.isLeaf() {
  627. break
  628. }
  629. cur = sbtRoute(n, endKey)
  630. }
  631. } else {
  632. for {
  633. n := sbtLoad(handle, cur)
  634. if n == nil {
  635. return nil
  636. }
  637. if n.isLeaf() {
  638. break
  639. }
  640. cur = sbtChildPID(sbtEntry(n, int(n.info.Count)-1))
  641. }
  642. }
  643. for cur != 0 {
  644. n := sbtLoad(handle, cur)
  645. if n == nil {
  646. break
  647. }
  648. if n.info.Hdr.Type != page.IndexDataIndex {
  649. return fmt.Errorf("btree: 反向范围扫描遇到非索引页 pid=%d type=%d, 叶子链已损坏",
  650. n.info.Hdr.PageID, n.info.Hdr.Type)
  651. }
  652. el := int(n.info.EntryLen)
  653. for i := int(n.info.Count) - 1; i >= 0; i-- {
  654. entry := sbtEntry(n, i)
  655. key := entry[:el-8]
  656. if startKey != nil {
  657. cmp := sbtCmp(key, startKey)
  658. if cmp < 0 || (cmp == 0 && !startInc) {
  659. return nil
  660. }
  661. }
  662. if endKey != nil {
  663. cmp := sbtCmp(key, endKey)
  664. if cmp > 0 || (cmp == 0 && !endInc) {
  665. continue
  666. }
  667. }
  668. pkID := entry[el-8:]
  669. if !callback(pkID) {
  670. return nil
  671. }
  672. }
  673. cur = n.info.Hdr.PrePage
  674. }
  675. }
  676. return nil
  677. }
  678. // ======================== 插入 ========================
  679. func SBTInsert(rootPID uint32, entry []byte, handle page.HandleDb) (uint32, error) {
  680. el := len(entry)
  681. if rootPID == 0 {
  682. leaf := sbtNew(handle, true, el)
  683. sbtInsEntry(leaf, entry, 0)
  684. sbtSave(handle, leaf)
  685. newRoot := leaf.pageID()
  686. leaf.release()
  687. return newRoot, nil
  688. }
  689. var path []btPathEntry
  690. cur := rootPID
  691. for {
  692. n := sbtLoad(handle, cur)
  693. if n == nil {
  694. return rootPID, errors.New("sbt load failed")
  695. }
  696. if n.isLeaf() {
  697. key := entry[:el-8]
  698. pkID := sbtPKID(entry)
  699. // 叶内条目按复合键 (key, pkID) 有序: 复合下界一次定位插入位置
  700. ip := sbtLeafLowerBoundPK(n, key, pkID)
  701. if int(n.info.Hdr.UseSize)+el <= btSplitThreshold() {
  702. sbtInsEntry(n, entry, ip)
  703. sbtSave(handle, n)
  704. if ip == 0 {
  705. // 新条目成为本叶最小复合键: 沿路径向上同步
  706. // "父条目复合键 == 子树最小复合键"
  707. for i := len(path) - 1; i >= 0; i-- {
  708. p := path[i]
  709. pb := sbtLoad(handle, p.nodePID)
  710. if pb == nil || p.childIdx < 0 || p.childIdx >= int(pb.info.Count) {
  711. break
  712. }
  713. pbe := sbtEntry(pb, p.childIdx)
  714. copy(pbe[:len(pbe)-4], entry) // entry 本身就是 [key|pkID]
  715. sbtSave(handle, pb)
  716. pb.release()
  717. if p.childIdx != 0 {
  718. break
  719. }
  720. }
  721. }
  722. n.release()
  723. return rootPID, nil
  724. }
  725. cnt := int(n.info.Count)
  726. total := cnt + 1
  727. all := make([]byte, total*el)
  728. if ip > 0 {
  729. copy(all, n.entries[:ip*el])
  730. }
  731. copy(all[ip*el:], entry)
  732. if cnt > ip {
  733. copy(all[(ip+1)*el:], n.entries[ip*el:cnt*el])
  734. }
  735. // 分裂点: 左叶保留 [0,mid), 右叶保留 [mid,total)
  736. mid := splitPoint(total, SBTSplitCfg, false)
  737. rl := sbtNew(handle, true, el)
  738. rc := total - mid
  739. copy(rl.entries, all[mid*el:total*el])
  740. rl.info.Count = uint32(rc)
  741. rl.info.Hdr.UseSize = uint32(page.IndexHeadSize) + uint32(rc*el)
  742. rl.info.Hdr.NextPage = n.info.Hdr.NextPage
  743. rl.info.Hdr.PrePage = n.info.Hdr.PageID
  744. n.info.Hdr.NextPage = rl.info.Hdr.PageID
  745. copy(n.entries, all[:mid*el])
  746. n.info.Count = uint32(mid)
  747. n.info.Hdr.UseSize = uint32(page.IndexHeadSize) + uint32(mid*el)
  748. sbtSave(handle, n)
  749. sbtSave(handle, rl)
  750. if rl.info.Hdr.NextPage != 0 {
  751. nl := sbtLoad(handle, rl.info.Hdr.NextPage)
  752. if nl != nil {
  753. nl.info.Hdr.PrePage = rl.info.Hdr.PageID
  754. sbtSave(handle, nl)
  755. nl.release()
  756. }
  757. }
  758. // 提升的分隔复合键 = 右叶首条目 [key|pkID] (rl 已在内存, 无需再下降)
  759. pkc := append([]byte(nil), sbtEntry(rl, 0)...)
  760. nr, err := sbtPromote(handle, rootPID, path, n.info.Hdr.PageID, pkc, rl.info.Hdr.PageID, el)
  761. n.release()
  762. rl.release()
  763. return nr, err
  764. }
  765. // 插入同样按复合键路由: 子树在复合键空间上是连续区间, key 路由可能
  766. // 落到重复 key 区段右半的子树, 会破坏全局 (key, pkID) 有序性
  767. ci := sbtChildIdxPK(n, entry[:el-8], sbtPKID(entry))
  768. cpid := sbtChildPID(sbtEntry(n, ci))
  769. path = append(path, btPathEntry{nodePID: cur, childIdx: ci})
  770. n.release()
  771. cur = cpid
  772. }
  773. }
  774. // sbtPromote 把分裂出的右节点 (及其最小复合键 promoComposite) 插入父层, 必要时
  775. // 递归分裂。leafEl 是叶条目长度 keyLen+8; 内部条目长度恒为 leafEl+4
  776. // ([key | pkID(8) | childPID(4)])。promoComposite 长度 = leafEl ([key|pkID])。
  777. func sbtPromote(handle page.HandleDb, rootPID uint32, path []btPathEntry, leftPID uint32, promoComposite []byte, rightPID uint32, leafEl int) (uint32, error) {
  778. iel := leafEl + 4
  779. promoKey := promoComposite[:len(promoComposite)-8]
  780. promoPK := binary.BigEndian.Uint64(promoComposite[len(promoComposite)-8:])
  781. if len(path) == 0 {
  782. nr := sbtNew(handle, false, iel)
  783. ln := sbtLoad(handle, leftPID)
  784. if ln == nil {
  785. nr.release()
  786. return rootPID, errors.New("sbt promote: left load failed")
  787. }
  788. le := make([]byte, iel)
  789. copy(le[:iel-4], sbtNodeMinKey(ln)) // leftPID 可能是叶或内部节点, 取其子树最小复合键
  790. ln.release()
  791. sbtSetChildPID(le, leftPID)
  792. re := make([]byte, iel)
  793. copy(re[:iel-4], promoComposite)
  794. sbtSetChildPID(re, rightPID)
  795. sbtInsEntry(nr, le, 0)
  796. sbtInsEntry(nr, re, 1)
  797. sbtSave(handle, nr)
  798. newRoot := nr.pageID()
  799. nr.release()
  800. return newRoot, nil
  801. }
  802. pi := len(path) - 1
  803. parent := sbtLoad(handle, path[pi].nodePID)
  804. if parent == nil {
  805. return rootPID, errors.New("sbt parent load failed")
  806. }
  807. ci := path[pi].childIdx
  808. cnt := int(parent.info.Count)
  809. pos := cnt
  810. for i := ci + 1; i < cnt; i++ {
  811. pe := sbtEntry(parent, i)
  812. if sbtCmpKeyPK(sbtIntKey(pe), sbtIntPKID(pe), promoKey, promoPK) >= 0 {
  813. pos = i
  814. break
  815. }
  816. }
  817. for i := cnt; i > pos; i-- {
  818. copy(sbtEntry(parent, i), sbtEntry(parent, i-1))
  819. }
  820. ne := make([]byte, iel)
  821. copy(ne[:iel-4], promoComposite)
  822. sbtSetChildPID(ne, rightPID)
  823. copy(sbtEntry(parent, pos), ne)
  824. parent.info.Count++
  825. parent.info.Hdr.UseSize += uint32(iel)
  826. if int(parent.info.Hdr.UseSize)+iel <= btSplitThreshold() {
  827. sbtSave(handle, parent)
  828. parent.release()
  829. return rootPID, nil
  830. }
  831. cnt = int(parent.info.Count)
  832. mid := splitPoint(cnt, SBTSplitCfg, false)
  833. // 分裂点取 mid: 左半保留 [0,mid), 右半保留 [mid,cnt)。
  834. // 不能丢掉 entry[mid] (否则它指向的整棵子树会不可达)。
  835. rn := sbtNew(handle, false, iel)
  836. rc := cnt - mid
  837. if rc > 0 {
  838. copy(rn.entries, parent.entries[mid*iel:cnt*iel])
  839. }
  840. rn.info.Count = uint32(rc)
  841. rn.info.Hdr.UseSize = uint32(page.IndexHeadSize) + uint32(rc*iel)
  842. parent.info.Count = uint32(mid)
  843. parent.info.Hdr.UseSize = uint32(page.IndexHeadSize) + uint32(mid*iel)
  844. sbtSave(handle, parent)
  845. sbtSave(handle, rn)
  846. // 提升的分隔复合键 = 右节点首条目的复合键 (其子树的最小复合键)
  847. e0 := sbtEntry(rn, 0)
  848. pk2 := make([]byte, iel-4)
  849. copy(pk2, e0[:iel-4])
  850. ret, err := sbtPromote(handle, rootPID, path[:pi], parent.info.Hdr.PageID, pk2, rn.info.Hdr.PageID, leafEl)
  851. parent.release()
  852. rn.release()
  853. return ret, err
  854. }
  855. // ======================== 删除 ========================
  856. func SBTDelete(rootPID uint32, key []byte, handle page.HandleDb) (uint32, error) {
  857. if rootPID == 0 {
  858. return 0, page.ErrNotFound
  859. }
  860. newRoot, err := sbtDeleteRec(rootPID, key, nil, handle)
  861. if err != nil {
  862. return rootPID, err
  863. }
  864. // 收尾归一化:
  865. // - 根为内部节点且只剩 1 个子节点 -> 用该子节点作为新根 (继续向上收缩)
  866. // - 根为内部节点且已无条目 (整棵树被删空) -> 换成一个全新的空叶子,
  867. // 避免留下 "空内部节点" 这种会让查找读到脏数据的状态
  868. // 归一化过程中被跳过的旧根 (降高时旧根整体退役; 删空时旧根被换成新建空叶)。
  869. // 收集后在本函数末尾才归还, 避免 sbtNew 在同一轮里把这些页又分配回来。
  870. var retiredRoots []uint32
  871. for {
  872. n := sbtLoad(handle, newRoot)
  873. if n == nil || n.isLeaf() {
  874. break
  875. }
  876. if n.info.Count == 1 {
  877. childPID := sbtChildPID(sbtEntry(n, 0))
  878. if childPID == 0 {
  879. break
  880. }
  881. retiredRoots = append(retiredRoots, newRoot)
  882. newRoot = childPID
  883. continue
  884. }
  885. if n.info.Count == 0 {
  886. // 此时 n 必为内部节点 (循环开头已 break 叶子), 其 EntryLen 是内部条目
  887. // 长度 keyLen+12; 新建空叶要用叶条目长度 keyLen+8
  888. leaf := sbtNew(handle, true, int(n.info.EntryLen)-4)
  889. sbtSave(handle, leaf)
  890. retiredRoots = append(retiredRoots, newRoot)
  891. newRoot = leaf.pageID()
  892. }
  893. break
  894. }
  895. // 只归还退役的旧根自身: Count==1 时其子树仍是新根 (可达), 绝不能按子树回收;
  896. // Count==0 时该节点已是空内部节点, 其子页在上面的递归里已随空子树摘链归还。
  897. for _, pid := range retiredRoots {
  898. btReleasePage(handle, pid)
  899. }
  900. return newRoot, nil
  901. }
  902. // sbtDeleteRec 按 key 删除 (删除该 key 的一条条目), 并做下溢/合并/归一化。
  903. func sbtDeleteRec(nodePID uint32, key []byte, path []btPathEntry, handle page.HandleDb) (uint32, error) {
  904. node := sbtLoad(handle, nodePID)
  905. if node == nil {
  906. return nodePID, page.ErrNotFound
  907. }
  908. el := int(node.info.EntryLen)
  909. if node.isLeaf() {
  910. pos, ok := sbtLeafFind(node, key)
  911. if !ok {
  912. return nodePID, page.ErrNotFound
  913. }
  914. sbtRmEntry(node, pos)
  915. if node.info.Count == 0 && len(path) == 0 {
  916. sbtSave(handle, node)
  917. return nodePID, nil
  918. }
  919. sbtSave(handle, node)
  920. if len(path) > 0 && node.info.Count > 0 {
  921. sbtTryMerge(handle, path, node, el)
  922. }
  923. return nodePID, nil
  924. }
  925. ci := sbtChildIdx(node, key)
  926. childPID := sbtChildPID(sbtEntry(node, ci))
  927. newPath := make([]btPathEntry, len(path)+1)
  928. copy(newPath, path)
  929. newPath[len(path)] = btPathEntry{nodePID: nodePID, childIdx: ci}
  930. if _, err := sbtDeleteRec(childPID, key, newPath, handle); err != nil {
  931. return nodePID, err
  932. }
  933. // 递归过程中子节点可能触发合并, 而合并会单独加载并保存本节点。
  934. // 这里必须重新加载, 否则后面用过期副本保存会把合并结果覆盖掉 (数据重复/条目丢失)。
  935. node = sbtLoad(handle, nodePID)
  936. if node == nil {
  937. return nodePID, errors.New("sbt reload node failed")
  938. }
  939. if node.info.Count > 0 {
  940. // 仅当原 child 条目仍在本节点的 ci 位置时才处理
  941. // (若该条目已被合并移除, 则说明子树已被兄弟吸收, 无需再处理)
  942. if ci >= 0 && ci < int(node.info.Count) && sbtChildPID(sbtEntry(node, ci)) == childPID {
  943. child := sbtLoad(handle, childPID)
  944. if child != nil {
  945. if child.info.Count == 0 {
  946. sbtHandleUnderflow(node, ci, handle)
  947. // 借位/合并会直接改写本节点并保存, 必须重新加载
  948. node = sbtLoad(handle, nodePID)
  949. if node == nil {
  950. return nodePID, errors.New("sbt reload node failed after underflow")
  951. }
  952. } else {
  953. // 维护不变式: 父条目复合键 == 子树最小复合键
  954. // (删除叶子的最小键后, 父条目键会变旧)
  955. copy(sbtEntry(node, ci)[:el-4], sbtNodeMinKey(child))
  956. }
  957. }
  958. }
  959. }
  960. // 归一化: 内部节点若只剩一个 "已空" 的子节点 (没有兄弟可合并), 则该节点本身
  961. // 也变空。此时必须把该空子树整段从叶子链摘除并让本节点变为空 (Count=0),
  962. // 由上层继续向上收缩。否则会残留 "空内部节点", 破坏路由/查找。
  963. if !node.isLeaf() && node.info.Count == 1 {
  964. c0 := sbtChildPID(sbtEntry(node, 0))
  965. cn := sbtLoad(handle, c0)
  966. if cn != nil && cn.info.Count == 0 {
  967. sbtUnlinkSubtreeLeaves(handle, c0)
  968. node.info.Count = 0
  969. node.info.Hdr.UseSize = uint32(page.IndexHeadSize)
  970. sbtSave(handle, node)
  971. // c0 子树整段摘链后不再被任何父条目或叶子链引用, 连同其内部节点
  972. // 一并归还 —— 否则每删空一棵子树就漏掉一批索引页。
  973. btReleaseSubtree(handle, c0)
  974. return nodePID, nil
  975. }
  976. }
  977. if node.info.Count == 0 {
  978. sbtSave(handle, node)
  979. return nodePID, nil
  980. }
  981. sbtSave(handle, node)
  982. return nodePID, nil
  983. }
  984. func sbtTryMerge(handle page.HandleDb, path []btPathEntry, node *btNode, el int) {
  985. if len(path) == 0 {
  986. return
  987. }
  988. nodePID := node.info.Hdr.PageID
  989. pp := path[len(path)-1].nodePID
  990. parent := sbtLoad(handle, pp)
  991. if parent == nil {
  992. return
  993. }
  994. ci := -1
  995. for i := 0; i < int(parent.info.Count); i++ {
  996. if sbtChildPID(sbtEntry(parent, i)) == node.info.Hdr.PageID {
  997. ci = i
  998. break
  999. }
  1000. }
  1001. if ci < 0 {
  1002. return
  1003. }
  1004. if ci > 0 {
  1005. leftPID := sbtChildPID(sbtEntry(parent, ci-1))
  1006. left := sbtLoad(handle, leftPID)
  1007. if left != nil && left.isLeaf() {
  1008. combined := uint32(page.IndexHeadSize) + (left.info.Count+node.info.Count)*uint32(el)
  1009. threshold := uint32(btSplitThreshold())
  1010. if combined <= threshold {
  1011. rc := int(node.info.Count)
  1012. copy(left.entries[int(left.info.Count)*el:], node.entries[:rc*el])
  1013. left.info.Count += node.info.Count
  1014. left.info.Hdr.UseSize += uint32(rc * el)
  1015. left.info.Hdr.NextPage = node.info.Hdr.NextPage
  1016. sbtSave(handle, left)
  1017. if left.info.Hdr.NextPage != 0 {
  1018. nl := sbtLoad(handle, left.info.Hdr.NextPage)
  1019. if nl != nil {
  1020. nl.info.Hdr.PrePage = leftPID
  1021. sbtSave(handle, nl)
  1022. }
  1023. }
  1024. sbtRmEntry(parent, ci)
  1025. sbtSave(handle, parent)
  1026. // node 的条目已全部并入 left, 且指向 node 的父条目已被摘除,
  1027. // 叶子链也已改接到 left 的 NextPage —— node 页至此彻底不可达。
  1028. btReleasePage(handle, nodePID)
  1029. return
  1030. }
  1031. }
  1032. }
  1033. if ci < int(parent.info.Count)-1 {
  1034. rightPID := sbtChildPID(sbtEntry(parent, ci+1))
  1035. right := sbtLoad(handle, rightPID)
  1036. if right != nil && right.isLeaf() {
  1037. combined := uint32(page.IndexHeadSize) + (node.info.Count+right.info.Count)*uint32(el)
  1038. threshold := uint32(btSplitThreshold())
  1039. if combined <= threshold {
  1040. rc := int(right.info.Count)
  1041. copy(node.entries[int(node.info.Count)*el:], right.entries[:rc*el])
  1042. node.info.Count += right.info.Count
  1043. node.info.Hdr.UseSize += uint32(rc * el)
  1044. node.info.Hdr.NextPage = right.info.Hdr.NextPage
  1045. sbtSave(handle, node)
  1046. if node.info.Hdr.NextPage != 0 {
  1047. nl := sbtLoad(handle, node.info.Hdr.NextPage)
  1048. if nl != nil {
  1049. nl.info.Hdr.PrePage = node.info.Hdr.PageID
  1050. sbtSave(handle, nl)
  1051. }
  1052. }
  1053. sbtRmEntry(parent, ci+1)
  1054. sbtSave(handle, parent)
  1055. // right 的条目已全部并入 node, 且指向 right 的父条目已摘除,
  1056. // 叶子链也已改接到 node 的 NextPage —— right 页至此彻底不可达。
  1057. btReleasePage(handle, rightPID)
  1058. return
  1059. }
  1060. }
  1061. }
  1062. }
  1063. // sbtHandleUnderflow 处理空/下溢子节点: 先尝试向兄弟借位, 否则与兄弟合并。
  1064. // 不接收 el 参数 —— child/兄弟与父节点的条目长度可能不同 (叶 keyLen+8,
  1065. // 内部 keyLen+12), 一律从各节点自身的 EntryLen 取。
  1066. func sbtHandleUnderflow(parent *btNode, childIdx int, handle page.HandleDb) {
  1067. childPID := sbtChildPID(sbtEntry(parent, childIdx))
  1068. child := sbtLoad(handle, childPID)
  1069. if child == nil {
  1070. return
  1071. }
  1072. if childIdx > 0 {
  1073. leftPID := sbtChildPID(sbtEntry(parent, childIdx-1))
  1074. leftSib := sbtLoad(handle, leftPID)
  1075. if leftSib != nil && int(leftSib.info.Count) > 1 {
  1076. borrowed := append([]byte(nil), sbtEntry(leftSib, int(leftSib.info.Count)-1)...)
  1077. sbtRmEntry(leftSib, int(leftSib.info.Count)-1)
  1078. // 左兄弟的最后一条被移到 child 首部
  1079. sbtInsEntry(child, borrowed, 0)
  1080. // child 的最小复合键变成 borrowed; 左兄弟最小复合键不变
  1081. pe := sbtEntry(parent, childIdx)
  1082. copy(pe[:len(pe)-4], sbtNodeMinKey(child))
  1083. pe = sbtEntry(parent, childIdx-1)
  1084. copy(pe[:len(pe)-4], sbtNodeMinKey(leftSib))
  1085. sbtSave(handle, leftSib)
  1086. sbtSave(handle, child)
  1087. sbtSave(handle, parent)
  1088. return
  1089. }
  1090. }
  1091. if childIdx < int(parent.info.Count)-1 {
  1092. rightPID := sbtChildPID(sbtEntry(parent, childIdx+1))
  1093. rightSib := sbtLoad(handle, rightPID)
  1094. if rightSib != nil && int(rightSib.info.Count) > 1 {
  1095. borrowed := append([]byte(nil), sbtEntry(rightSib, 0)...)
  1096. sbtRmEntry(rightSib, 0)
  1097. // 右兄弟的首条被追加到 child 尾部: child 的最小复合键不变
  1098. sbtInsEntry(child, borrowed, int(child.info.Count))
  1099. pe := sbtEntry(parent, childIdx)
  1100. copy(pe[:len(pe)-4], sbtNodeMinKey(child))
  1101. // 右兄弟失去首条后最小复合键变化, 更新其父条目
  1102. pe = sbtEntry(parent, childIdx+1)
  1103. copy(pe[:len(pe)-4], sbtNodeMinKey(rightSib))
  1104. sbtSave(handle, rightSib)
  1105. sbtSave(handle, child)
  1106. sbtSave(handle, parent)
  1107. return
  1108. }
  1109. }
  1110. if childIdx > 0 {
  1111. sbtMergeChildren(handle, parent, childIdx-1)
  1112. } else if childIdx < int(parent.info.Count)-1 {
  1113. sbtMergeChildren(handle, parent, childIdx)
  1114. }
  1115. }
  1116. // sbtMergeChildren 把 sepIdx+1 指向的右子树并入 sepIdx 指向的左子树。
  1117. // 条目长度从 left 节点自身的 EntryLen 取 (左右同为叶或同为内部节点)。
  1118. func sbtMergeChildren(handle page.HandleDb, parent *btNode, sepIdx int) {
  1119. leftPID := sbtChildPID(sbtEntry(parent, sepIdx))
  1120. rightPID := sbtChildPID(sbtEntry(parent, sepIdx+1))
  1121. left := sbtLoad(handle, leftPID)
  1122. right := sbtLoad(handle, rightPID)
  1123. if left == nil || right == nil {
  1124. return
  1125. }
  1126. // 注意: 本实现的父条目键是 "子树最小键的副本", 不是经典 B+ 树里独立的分隔键。
  1127. // 因此合并内部节点时, 直接把右兄弟的条目追加到左兄弟即可,
  1128. // 绝不能把 parent[sepIdx] 下放 (那会写出一条指向左兄弟自身的自引用条目)。
  1129. el := int(left.info.EntryLen)
  1130. rc := int(right.info.Count)
  1131. copy(left.entries[int(left.info.Count)*el:], right.entries[:rc*el])
  1132. left.info.Count += right.info.Count
  1133. left.info.Hdr.UseSize += uint32(rc * el)
  1134. if left.isLeaf() {
  1135. left.info.Hdr.NextPage = right.info.Hdr.NextPage
  1136. }
  1137. sbtSave(handle, left)
  1138. if left.isLeaf() && left.info.Hdr.NextPage != 0 {
  1139. nl := sbtLoad(handle, left.info.Hdr.NextPage)
  1140. if nl != nil {
  1141. nl.info.Hdr.PrePage = leftPID
  1142. sbtSave(handle, nl)
  1143. }
  1144. }
  1145. sbtRmEntry(parent, sepIdx+1)
  1146. sbtSetChildPID(sbtEntry(parent, sepIdx), leftPID)
  1147. // 左节点若原本是空节点, 合并后其最小复合键变为右兄弟的最小复合键, 必须同步
  1148. // 父条目, 否则会出现 "父键 != 子树最小复合键", 破坏路由。
  1149. if left.info.Count > 0 {
  1150. pe := sbtEntry(parent, sepIdx)
  1151. copy(pe[:len(pe)-4], sbtNodeMinKey(left))
  1152. }
  1153. sbtSave(handle, parent)
  1154. // right 的条目已全部并入 left, 父条目 sepIdx+1 已摘除 (叶子链在上面的
  1155. // NextPage/PrePage 重接中已绕过 right) —— right 子树至此彻底不可达。
  1156. btReleasePage(handle, rightPID)
  1157. }
  1158. // SBTBuildFromSorted 从已排序的条目批量构建二级 B+ 树 (自底向上)
  1159. // entries: 每个条目 = [key(keyLen) + pkID(8B)], 必须按复合键 (key, pkID) 升序排列
  1160. // (只按 key 排序不够: 复合分隔键的路由/二分要求叶内同 key 条目也按 pkID 有序)
  1161. // 返回新树根 pageID
  1162. //
  1163. // 内存优化: 避免逐行 B+ 树搜索 (每次 sbtLoad 16KB), 改为顺序创建所有节点
  1164. func SBTBuildFromSorted(entries [][]byte, handle page.HandleDb) uint32 {
  1165. if len(entries) == 0 {
  1166. return 0
  1167. }
  1168. el := len(entries[0]) // entry len: keyLen + 8
  1169. keyLen := el - 8
  1170. maxPerLeaf := maxLeafEntries(el)
  1171. type childInfo struct {
  1172. pid uint32
  1173. firstKey []byte // 该子树最小 key (keyLen 字节)
  1174. firstPK uint64 // 该子树最小 pkID
  1175. }
  1176. var children []childInfo
  1177. var prevLeafPID uint32
  1178. // 构建叶节点 (带链表链接)
  1179. for i := 0; i < len(entries); {
  1180. end := i + maxPerLeaf
  1181. if end > len(entries) {
  1182. end = len(entries)
  1183. }
  1184. leaf := sbtNew(handle, true, el)
  1185. for j := i; j < end; j++ {
  1186. dst := sbtEntry(leaf, int(leaf.info.Count))
  1187. copy(dst, entries[j])
  1188. leaf.info.Count++
  1189. leaf.info.Hdr.UseSize += uint32(el)
  1190. }
  1191. leaf.info.Hdr.PrePage = prevLeafPID
  1192. sbtSave(handle, leaf)
  1193. if prevLeafPID != 0 {
  1194. prevLeaf := sbtLoad(handle, prevLeafPID)
  1195. if prevLeaf != nil {
  1196. prevLeaf.info.Hdr.NextPage = leaf.pageID()
  1197. sbtSave(handle, prevLeaf)
  1198. }
  1199. }
  1200. prevLeafPID = leaf.pageID()
  1201. fk := make([]byte, keyLen)
  1202. copy(fk, entries[i][:keyLen])
  1203. children = append(children, childInfo{pid: leaf.pageID(), firstKey: fk, firstPK: sbtPKID(entries[i])})
  1204. i = end
  1205. }
  1206. if len(children) == 1 {
  1207. return children[0].pid
  1208. }
  1209. // 自底向上构建内部节点
  1210. // 路由约定 (sbtRoute/sbtChildIdx): 找最后一个 分隔复合键 <= 搜索复合键 的子树
  1211. // 内部节点条目: [child.firstKey | child.firstPK | child.pid]
  1212. // 即 entry[i] 的前 keyLen+8 字节 = child[i] 子树的最小复合键
  1213. intEl := el + 4 // 内部条目 = [key | pkID(8) | childPID(4)]
  1214. maxPerInt := maxLeafEntries(intEl)
  1215. for len(children) > 1 {
  1216. var parents []childInfo
  1217. for i := 0; i < len(children); {
  1218. end := i + maxPerInt
  1219. if end > len(children) {
  1220. end = len(children)
  1221. }
  1222. node := sbtNew(handle, false, intEl)
  1223. for j := i; j < end; j++ {
  1224. e := make([]byte, intEl)
  1225. // entry[j] 前 keyLen+8 字节 = 该 child 子树的最小复合键
  1226. copy(e[:keyLen], children[j].firstKey)
  1227. binary.BigEndian.PutUint64(e[keyLen:keyLen+8], children[j].firstPK)
  1228. sbtSetChildPID(e, children[j].pid)
  1229. dst := sbtEntry(node, int(node.info.Count))
  1230. copy(dst, e)
  1231. node.info.Count++
  1232. node.info.Hdr.UseSize += uint32(intEl)
  1233. }
  1234. sbtSave(handle, node)
  1235. parents = append(parents, childInfo{
  1236. pid: node.pageID(),
  1237. firstKey: children[i].firstKey,
  1238. firstPK: children[i].firstPK,
  1239. })
  1240. i = end
  1241. }
  1242. children = parents
  1243. }
  1244. return children[0].pid
  1245. }
  1246. // ======================== 二级叶批量插入 ========================
  1247. // SBTBulkInsert 批量插入二级索引条目。entries 是 [key|pkID] 定长条目的有序串
  1248. // (按复合键 (key, pkID) 升序, 调用方排序), el 为条目长度 (keyLen+8)。
  1249. //
  1250. // 思路: 排序批量对叶做"顺序扫描式"填充 —— 驻留叶加载一次, 条目在页内定位插入;
  1251. // 目标叶单调右移 (整批有序), 沿叶链走而不是逐条从根下降。每张叶加载/落盘各一次,
  1252. // 单条 SBTInsert 兜底只发生在三种边角: 叶满要分裂、条目成为叶最小键 (父键同步)、
  1253. // 链尾之后还有更大键 —— 分裂/提升/同步全套复用既有逻辑。
  1254. //
  1255. // 正确性关键: 驻留叶的可用区间是 [叶首条目, 上界), 上界 = 右邻叶首条目 (叶链
  1256. // NextPage)。判定"该不该留在当前叶"必须用上界, 不能用"大于叶末条"—— 后者会在
  1257. // 条目越过右邻叶首键时仍然往左叶追加, 破坏父层路由 (该条目从此按键查不到)。
  1258. // 批量键与既有数据交错 (少基数键的翻页灌数) 恰好整批落进叶中段, 走页内定位插入,
  1259. // 不再退化成逐条单插。
  1260. func SBTBulkInsert(rootPID uint32, entries []byte, el int, handle page.HandleDb) (uint32, error) {
  1261. if el <= 12 || len(entries)%el != 0 {
  1262. return rootPID, errors.New("sbt bulk insert: bad entry layout")
  1263. }
  1264. n := len(entries) / el
  1265. if n == 0 {
  1266. return rootPID, nil
  1267. }
  1268. th := btSplitThreshold()
  1269. var leaf *btNode
  1270. var upper []byte // 驻留叶复合键上界 (右邻叶首条目的副本); nil = 最右叶 (上界无穷)
  1271. // upperOf 计算叶的上界。副本必须复制出来 —— 右邻叶还回缓冲池后内存会被复用。
  1272. // 空叶 (Count==0, 健康树上不应出现) 一律当"无上界", 交给后续单条兜底修正。
  1273. upperOf := func(nd *btNode) []byte {
  1274. if nd.info.Hdr.NextPage == 0 {
  1275. return nil
  1276. }
  1277. nl := sbtLoad(handle, nd.info.Hdr.NextPage)
  1278. if nl == nil || nl.info.Count == 0 {
  1279. if nl != nil {
  1280. nl.release()
  1281. }
  1282. return nil
  1283. }
  1284. up := make([]byte, el)
  1285. copy(up, sbtEntry(nl, 0))
  1286. nl.release()
  1287. return up
  1288. }
  1289. flushLeaf := func() {
  1290. if leaf != nil {
  1291. sbtSave(handle, leaf)
  1292. if rootPID == 0 {
  1293. rootPID = leaf.pageID()
  1294. }
  1295. leaf.release()
  1296. leaf = nil
  1297. }
  1298. }
  1299. // descend 按复合键从根下降到 ent 所属叶 (与 SBTInsert 的路由约定一致), 并取上界。
  1300. descend := func(ent []byte) error {
  1301. cur := rootPID
  1302. for {
  1303. nd := sbtLoad(handle, cur)
  1304. if nd == nil {
  1305. return errors.New("sbt bulk insert: load failed")
  1306. }
  1307. if nd.isLeaf() {
  1308. leaf = nd
  1309. upper = upperOf(nd)
  1310. return nil
  1311. }
  1312. ci := sbtChildIdxPK(nd, ent[:el-8], sbtPKID(ent))
  1313. cur = sbtChildPID(sbtEntry(nd, ci))
  1314. nd.release()
  1315. }
  1316. }
  1317. // stepTo 沿叶链从 from 走到第一张非空叶 (或链尾) 并驻留。
  1318. // 调用方随后用上界复查, ent 仍越界就再走一步。
  1319. stepTo := func(from uint32) error {
  1320. cur := from
  1321. for {
  1322. nd := sbtLoad(handle, cur)
  1323. if nd == nil {
  1324. return errors.New("sbt bulk insert: chain load failed")
  1325. }
  1326. if nd.info.Count > 0 || nd.info.Hdr.NextPage == 0 {
  1327. leaf = nd
  1328. upper = upperOf(nd)
  1329. return nil
  1330. }
  1331. cur = nd.info.Hdr.NextPage // 空叶没有承载键, 直接跳过
  1332. nd.release()
  1333. }
  1334. }
  1335. for i := 0; i < n; i++ {
  1336. ent := entries[i*el : (i+1)*el]
  1337. if leaf == nil {
  1338. if rootPID == 0 {
  1339. leaf = sbtNew(handle, true, el)
  1340. upper = nil
  1341. } else if err := descend(ent); err != nil {
  1342. return rootPID, err
  1343. }
  1344. }
  1345. // 越过驻留叶上界: 目标叶在右侧。落盘驻留叶后沿叶链右移 (比重新下降省
  1346. // 2/3 页加载); 链走尽了重新下降, 让路由把后续大键送进(新的)最右叶。
  1347. for upper != nil && sbtCmp(ent, upper) >= 0 {
  1348. next := leaf.info.Hdr.NextPage
  1349. flushLeaf()
  1350. if next == 0 {
  1351. break
  1352. }
  1353. if err := stepTo(next); err != nil {
  1354. return rootPID, err
  1355. }
  1356. }
  1357. if leaf == nil {
  1358. if err := descend(ent); err != nil {
  1359. return rootPID, err
  1360. }
  1361. }
  1362. if int(leaf.info.Hdr.UseSize)+el > th {
  1363. // 叶满: 落盘驻留叶, 单条兜底 (分裂/提升/叶链维护全套)
  1364. flushLeaf()
  1365. nr, err := SBTInsert(rootPID, ent, handle)
  1366. if err != nil {
  1367. return rootPID, err
  1368. }
  1369. rootPID = nr
  1370. continue
  1371. }
  1372. cnt := int(leaf.info.Count)
  1373. ip := sbtLeafLowerBoundPK(leaf, ent[:el-8], sbtPKID(ent))
  1374. if ip == 0 && cnt > 0 {
  1375. // 新条目将成为叶最小复合键: "父条目复合键 == 子树最小复合键"不变式
  1376. // 需要沿路径向上同步, 交给单条路径 (整批有序, 只可能发生在批首条目)
  1377. flushLeaf()
  1378. nr, err := SBTInsert(rootPID, ent, handle)
  1379. if err != nil {
  1380. return rootPID, err
  1381. }
  1382. rootPID = nr
  1383. continue
  1384. }
  1385. sbtInsEntry(leaf, ent, ip)
  1386. }
  1387. flushLeaf()
  1388. return rootPID, nil
  1389. }