primary.go 58 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967
  1. package btree
  2. import (
  3. "TsDB2/DB/core/index/datapage"
  4. "TsDB2/DB/core/page"
  5. "encoding/binary"
  6. "errors"
  7. "fmt"
  8. "unsafe"
  9. )
  10. // btNode B+ 树节点 (raw bytes + page.IndexHead 指针, 非 IndexPage 对象)
  11. type btNode struct {
  12. buf []byte // 完整页缓冲区
  13. info *page.IndexHead // 指向 buf 头部的 page.IndexHead 指针
  14. entries []byte // buf[page.IndexHeadSize:] 条目区域
  15. }
  16. func (n *btNode) isLeaf() bool { return n.info.IsLeaf == 1 }
  17. func (n *btNode) pageID() uint32 { return n.info.Hdr.PageID }
  18. // btPathEntry 记录 B+ 树 descent 路径
  19. type btPathEntry struct {
  20. nodePID uint32
  21. childIdx int
  22. }
  23. // btSplitThreshold 计算分裂阈值
  24. func btSplitThreshold() int {
  25. ff := page.BTreeFillFactor
  26. if ff < 50 {
  27. ff = 50
  28. }
  29. if ff > 100 {
  30. ff = 100
  31. }
  32. return int(page.IndexPageSize) * ff / 100
  33. }
  34. // ======================== 索引页归还 ========================
  35. // btReleasePage 归还一个已确认不可达的索引页 (内部节点或叶子节点)。
  36. //
  37. // 为什么优先走 DeferFreePage 而不是直接 FreePage: 空闲链表是直写盘的 (绕过
  38. // BufferPool), 而本库是"先 WAL 后回写"。删除/合并途中若立即把页写成空闲链表
  39. // 节点, 一旦崩溃或 Rollback, 树结构虽然退回删除前, 页内容却已在盘上变成空页,
  40. // 索引就指向了一张空页。延迟回收只登记, 等 Commit/Close 再真正还链 ——
  41. // 与 heapReleaseRow / deleter 回收旧 DataPage 是同一套时序约定。
  42. //
  43. // 只有调用方没提供 DeferFreePage 时 (demo 里手搓的 HandleDb) 才退回立即回收。
  44. func btReleasePage(handle page.HandleDb, pid uint32) {
  45. if pid == 0 {
  46. return
  47. }
  48. if handle.DeferFreePage != nil {
  49. handle.DeferFreePage(pid)
  50. return
  51. }
  52. if handle.FreePage != nil {
  53. handle.FreePage(pid)
  54. }
  55. }
  56. // btReleaseSubtree 归还一整棵已确认不可达的子树 (含内部节点与叶子节点)。
  57. // 只用于"整段摘除"的场景 (空子树被摘链 / 旧树被换根), 调用前必须保证该子树
  58. // 已无任何父条目引用、且已从叶子链上摘除。
  59. func btReleaseSubtree(handle page.HandleDb, rootPID uint32) {
  60. if rootPID == 0 {
  61. return
  62. }
  63. for _, pid := range CollectAllPageIDs(handle, rootPID) {
  64. btReleasePage(handle, pid)
  65. }
  66. }
  67. // ======================== 节点 I/O ========================
  68. func btLoad(handle page.HandleDb, pid uint32) *btNode {
  69. if pid == 0 {
  70. return nil
  71. }
  72. buf := nodeBufGet()
  73. if err := handle.ReadPage(pid, buf); err != nil {
  74. nodeBufPut(buf)
  75. return nil
  76. }
  77. info := (*page.IndexHead)(unsafe.Pointer(&buf[0]))
  78. return &btNode{buf: buf, info: info, entries: buf[page.IndexHeadSize:]}
  79. }
  80. // btLoadNode 加载并校验「这确实是一张自洽的 B+ 节点页」, 不自洽返回 nil。
  81. //
  82. // 为什么必须校验: btLoad 只按偏移解析页头, 既不看页类型、也不检查条目区长度。
  83. // 传入一张已被回收复用过的页 (DataPage / 堆页 / WAL 页) 时, 里面根本不是节点布局:
  84. //
  85. // IndexHead.Count 落在 DataPage 的行键 Min/Max 上 → 取到 2 万级的大整数;
  86. // btEntry(n, i) 于是从行数据中间切片, btPID 截出的 4 字节就成了垃圾页号
  87. // (实测 DROP TABLE 收集到 2463488 = 0x259700, 一个从未分配过的页号)。
  88. //
  89. // 校验三条: 页类型是索引节点、页内自述 PageID 与取页号一致、Count*EntryLen 不越界。
  90. // 回收路径 (DropTable / btReleaseSubtree / GetDataPageIDs) 必须走这个版本 —— 垃圾页号
  91. // 一旦 FreePage, 空闲链头就指向文件里不存在的位置, 下一次 NewPage 会写到几十 GB 偏移。
  92. func btLoadNode(handle page.HandleDb, pid uint32) *btNode {
  93. if pid == 0 {
  94. return nil
  95. }
  96. n := btLoad(handle, pid)
  97. if n == nil {
  98. return nil
  99. }
  100. if n.info.Hdr.Type != page.IndexDataIndex || n.info.Hdr.PageID != pid {
  101. return nil
  102. }
  103. el := uint32(n.info.EntryLen)
  104. if el < 4 {
  105. return nil
  106. }
  107. if uint32(n.info.Count)*el > uint32(len(n.entries)) {
  108. return nil
  109. }
  110. return n
  111. }
  112. func btSave(handle page.HandleDb, n *btNode) {
  113. _ = handle.SavePage(n.info.Hdr.PageID, n.buf)
  114. }
  115. func btNew(handle page.HandleDb, isLeaf bool, entryLen int) *btNode {
  116. pid := handle.NewPage()
  117. buf := nodeBufGet()
  118. clear(buf) // 池化缓冲可能带旧节点数据, 条目区必须回到"新页=全零"语义
  119. info := (*page.IndexHead)(unsafe.Pointer(&buf[0]))
  120. info.Hdr.Type = page.IndexDataIndex
  121. info.Hdr.PageID = pid
  122. info.Hdr.UseSize = uint32(page.IndexHeadSize)
  123. info.IndexType = page.IndexTypePrimary
  124. info.EntryLen = uint16(entryLen)
  125. info.Count = 0
  126. if isLeaf {
  127. info.IsLeaf = 1
  128. }
  129. return &btNode{buf: buf, info: info, entries: buf[page.IndexHeadSize:]}
  130. }
  131. // ======================== 条目操作 ========================
  132. func btEntry(n *btNode, i int) []byte {
  133. el := int(n.info.EntryLen)
  134. return n.entries[i*el : (i+1)*el]
  135. }
  136. // ======================== 条目布局 ========================
  137. //
  138. // 两类条目的**键都在前 8 字节、pid 都在偏移 8 处**, 只是长度不同:
  139. //
  140. // 内部节点条目 [key(8B)][childPID(4B BE)] entryLen = btIntEntryLen (12)
  141. // 主键叶条目 [key(8B)][dpID(4B BE)][slot(2B BE)] entryLen = btLeafEntryLen (14)
  142. //
  143. // 叶条目多出来的 slot 是「行号」: datapage 的槽位目录保证活行槽位号稳定
  144. // (见 datapage 的不变式注释), 命中叶后 ReadRowBySlotPK 就能 O(1) 取行, 不必再在页内
  145. // 扫一遍找 pkID。
  146. //
  147. // 因为键与 pid 都落在固定偏移上, btKey/btPID 对两种条目都成立; 但**绝不能用「末 4 字节」
  148. // 当 pid** —— 对 14 字节的叶条目那读出来的是 dpID 低 2 字节 + slot。slot 只能走 btSlot。
  149. const (
  150. btIntEntryLen = 12 // 内部节点: [key(8B)][childPID(4B BE)]
  151. btLeafEntryLen = 14 // 主键叶: [key(8B)][dpID(4B BE)][slot(2B BE)]
  152. )
  153. func btKey(e []byte) []byte { return e[:8] }
  154. func btPID(e []byte) uint32 { return binary.BigEndian.Uint32(e[8:12]) }
  155. func btSetPID(e []byte, p uint32) { binary.BigEndian.PutUint32(e[8:12], p) }
  156. // btSlot 读主键叶条目的槽位号 (1 基; 0 = 无效)。仅对叶条目有效。
  157. func btSlot(e []byte) uint16 { return binary.BigEndian.Uint16(e[12:14]) }
  158. // btSetSlot 写主键叶条目的槽位号。仅对叶条目有效。
  159. func btSetSlot(e []byte, s uint16) { binary.BigEndian.PutUint16(e[12:14], s) }
  160. // btChildPID 读一张「内部节点」条目里的子页号。
  161. //
  162. // 上面那套「键在前 8 字节、pid 在偏移 8」只对**主键**条目成立。二级 B+ 树的条目
  163. // 是变长的 (键长随字段类型走), 布局为:
  164. //
  165. // 二级内部 [key(keyLen)][childPID(4B BE)] entryLen = keyLen+4 → 子页号在**末 4 字节**
  166. // 二级叶子 [key(keyLen)][PKID(8B)] entryLen = keyLen+8 → 末 8 字节是主键号, 非页号
  167. //
  168. // 所以下探内部节点时绝不能一律用 btPID(偏移 8..12): 对二级树那读的是键值尾部
  169. // (复合键) 或全零填充 —— 实测整棵二级树的内部条目在该处恒为 0, 于是 BFS 只收到
  170. // 根页, 整棵子树被永久漏收 (页泄漏, 文件只涨不缩)。
  171. //
  172. // 判定依据只认页头自述的 IndexType (btLoadNode 已保证它就是本页), 不做任何猜测。
  173. func btChildPID(n *btNode, e []byte) uint32 {
  174. if page.IndexType(n.info.IndexType) == page.IndexTypePrimary {
  175. return btPID(e)
  176. }
  177. // BTree / 旧 Str: 子页号在条目末 4 字节。
  178. return sbtChildPID(e)
  179. }
  180. func btCmp(a, b []byte) int {
  181. ml := len(a)
  182. if len(b) < ml {
  183. ml = len(b)
  184. }
  185. for i := 0; i < ml; i++ {
  186. if a[i] < b[i] {
  187. return -1
  188. }
  189. if a[i] > b[i] {
  190. return 1
  191. }
  192. }
  193. if len(a) < len(b) {
  194. return -1
  195. }
  196. if len(a) > len(b) {
  197. return 1
  198. }
  199. return 0
  200. }
  201. func btRoute(n *btNode, key []byte) uint32 {
  202. cnt := int(n.info.Count)
  203. if cnt <= 0 {
  204. return 0 // 防御: 空节点不应作为路由节点
  205. }
  206. // 二分查找: 找最后一个 entry.key <= key 的位置 (与二级 B+ 树统一 <= 约定)
  207. left, right := 0, cnt-1
  208. result := 0
  209. for left <= right {
  210. mid := (left + right) / 2
  211. if btCmp(btKey(btEntry(n, mid)), key) <= 0 {
  212. result = mid
  213. left = mid + 1 // 继续向右找
  214. } else {
  215. right = mid - 1
  216. }
  217. }
  218. return btPID(btEntry(n, result))
  219. }
  220. // maxLeafEntries 单个叶节点最大条目数
  221. func maxLeafEntries(entryLen int) int {
  222. threshold := btSplitThreshold()
  223. available := threshold - page.IndexHeadSize
  224. if available <= 0 {
  225. return 1
  226. }
  227. return available / entryLen
  228. }
  229. // BuildFromSorted 从已排序的叶条目批量构建 B+ 树 (自底向上)
  230. // leafEntries: 每个条目 = [key(8B) + dpID(4B BE) + slot(2B BE)], 已按 key 升序排列
  231. // 返回新树根 pageID
  232. //
  233. // 内存优化: 避免逐行 B+ 树搜索 (每次 btLoad 16KB), 改为顺序创建所有节点
  234. func BuildFromSorted(leafEntries [][]byte, handle page.HandleDb) uint32 {
  235. if len(leafEntries) == 0 {
  236. return 0
  237. }
  238. el := btLeafEntryLen // [key(8) + dpID(4) + slot(2)]
  239. maxPerLeaf := maxLeafEntries(el)
  240. // ---- 构建叶节点层 ----
  241. type childInfo struct {
  242. pid uint32
  243. firstKey []byte // 该子树第一个 key (用于 > 路由)
  244. }
  245. var children []childInfo
  246. var prevLeafPID uint32
  247. // 将条目分组到叶节点 (带链表链接)
  248. for i := 0; i < len(leafEntries); {
  249. end := i + maxPerLeaf
  250. if end > len(leafEntries) {
  251. end = len(leafEntries)
  252. }
  253. leaf := btNew(handle, true, el)
  254. for j := i; j < end; j++ {
  255. src := leafEntries[j]
  256. dst := btEntry(leaf, int(leaf.info.Count))
  257. copy(dst, src)
  258. leaf.info.Count++
  259. leaf.info.Hdr.UseSize += uint32(el)
  260. }
  261. // 设置叶节点链表链接
  262. leaf.info.Hdr.PrePage = prevLeafPID
  263. if prevLeafPID != 0 {
  264. // 更新前一个叶节点的 NextPage
  265. // 注意: 前一个叶节点已经保存, 需要重新加载并更新
  266. // 但我们可以在保存前就设置好 NextPage
  267. }
  268. btSave(handle, leaf)
  269. // 更新前一个叶节点的 NextPage 指向当前叶
  270. if prevLeafPID != 0 {
  271. prevLeaf := btLoad(handle, prevLeafPID)
  272. if prevLeaf != nil {
  273. prevLeaf.info.Hdr.NextPage = leaf.pageID()
  274. btSave(handle, prevLeaf)
  275. }
  276. }
  277. prevLeafPID = leaf.pageID()
  278. children = append(children, childInfo{
  279. pid: leaf.pageID(),
  280. firstKey: leafEntries[i][:8],
  281. })
  282. i = end
  283. }
  284. if len(children) == 1 {
  285. return children[0].pid
  286. }
  287. // ---- 自底向上构建内部节点 ----
  288. // 路由约定 (btRoute): 找最后一个 entry.key <= searchKey
  289. // 内部节点条目: entry[i] = (child[i].firstKey, child[i].pid)
  290. // 即 entry[i].key = 该子树最左叶的第一个 key
  291. intEl := btIntEntryLen
  292. maxPerInt := maxLeafEntries(intEl)
  293. for len(children) > 1 {
  294. var parents []childInfo
  295. for i := 0; i < len(children); {
  296. end := i + maxPerInt
  297. if end > len(children) {
  298. end = len(children)
  299. }
  300. node := btNew(handle, false, intEl)
  301. for j := i; j < end; j++ {
  302. e := make([]byte, intEl)
  303. // 分隔键 = 该 child 的第一个 key
  304. copy(e[:intEl-4], children[j].firstKey)
  305. btSetPID(e, children[j].pid)
  306. dst := btEntry(node, int(node.info.Count))
  307. copy(dst, e)
  308. node.info.Count++
  309. node.info.Hdr.UseSize += uint32(intEl)
  310. }
  311. btSave(handle, node)
  312. parents = append(parents, childInfo{
  313. pid: node.pageID(),
  314. firstKey: children[i].firstKey,
  315. })
  316. i = end
  317. }
  318. children = parents
  319. }
  320. return children[0].pid
  321. }
  322. func btChildIdx(n *btNode, key []byte) int {
  323. cnt := int(n.info.Count)
  324. // 二分查找: 找最后一个 entry.key <= key 的位置 (与二级 B+ 树统一 <= 约定)
  325. left, right := 0, cnt-1
  326. result := 0
  327. for left <= right {
  328. mid := (left + right) / 2
  329. if btCmp(btKey(btEntry(n, mid)), key) <= 0 {
  330. result = mid
  331. left = mid + 1 // 继续向右找
  332. } else {
  333. right = mid - 1
  334. }
  335. }
  336. return result
  337. }
  338. func btInsEntry(n *btNode, entry []byte, pos int) {
  339. el := int(n.info.EntryLen)
  340. cnt := int(n.info.Count)
  341. // 整段后移一格 (块拷贝): 逐条目循环移位在千条目级叶上每插一条要搬千次 14B,
  342. // 批量灌叶路径实测占 ~4% CPU; Go 的 copy 是 memmove 语义, 区间重叠安全。
  343. copy(n.entries[(pos+1)*el:(cnt+1)*el], n.entries[pos*el:cnt*el])
  344. copy(n.entries[pos*el:(pos+1)*el], entry)
  345. n.info.Count++
  346. n.info.Hdr.UseSize += uint32(el)
  347. if n.isLeaf() {
  348. btReportUnsorted("insEntry", n)
  349. }
  350. }
  351. func btRmEntry(n *btNode, pos int) {
  352. el := int(n.info.EntryLen)
  353. cnt := int(n.info.Count)
  354. for i := pos; i < cnt-1; i++ {
  355. copy(btEntry(n, i), btEntry(n, i+1))
  356. }
  357. n.info.Count--
  358. n.info.Hdr.UseSize -= uint32(el)
  359. if n.isLeaf() {
  360. btReportUnsorted("rmEntry", n)
  361. }
  362. }
  363. // btLeafFind 在叶节点内二分查找等于 key 的条目。
  364. // 叶条目按 key 升序排列 (B+ 树不变式), 主键唯一, 因此可安全二分。
  365. func btLeafFind(n *btNode, key []byte) (int, bool) {
  366. cnt := int(n.info.Count)
  367. left, right := 0, cnt-1
  368. for left <= right {
  369. mid := (left + right) / 2
  370. cmp := btCmp(btKey(btEntry(n, mid)), key)
  371. if cmp == 0 {
  372. return mid, true
  373. } else if cmp < 0 {
  374. left = mid + 1
  375. } else {
  376. right = mid - 1
  377. }
  378. }
  379. return 0, false
  380. }
  381. // btLeafPos 返回第一个 key >= 目标 key 的条目下标 (lower_bound), 用于插入定位。
  382. // 二分实现, 语义与线性扫描完全一致 (未命中时返回 cnt)。
  383. func btLeafPos(n *btNode, key []byte) int {
  384. cnt := int(n.info.Count)
  385. left, right := 0, cnt
  386. for left < right {
  387. mid := (left + right) / 2
  388. if btCmp(btKey(btEntry(n, mid)), key) < 0 {
  389. left = mid + 1
  390. } else {
  391. right = mid
  392. }
  393. }
  394. return left
  395. }
  396. // btLeftmostKey 获取子树最左叶的第一个 key(用于 promoKey)
  397. func btLeftmostKey(handle page.HandleDb, nodePID uint32) []byte {
  398. cur := nodePID
  399. for {
  400. n := btLoad(handle, cur)
  401. if n == nil {
  402. return nil
  403. }
  404. if n.isLeaf() {
  405. if n.info.Count == 0 {
  406. return nil
  407. }
  408. e := btEntry(n, 0)
  409. key := make([]byte, len(btKey(e)))
  410. copy(key, btKey(e))
  411. return key
  412. }
  413. // 非叶节点,继续向左下遍历
  414. cur = btPID(btEntry(n, 0))
  415. }
  416. }
  417. // btFirstLeaf 沿最左分支下降, 返回以 pid 为根的子树中最左叶子页 ID (找不到返回 0)。
  418. func btFirstLeaf(handle page.HandleDb, pid uint32) uint32 {
  419. for pid != 0 {
  420. n := btLoad(handle, pid)
  421. if n == nil {
  422. return 0
  423. }
  424. if n.isLeaf() {
  425. return pid
  426. }
  427. if n.info.Count == 0 {
  428. return 0
  429. }
  430. pid = btPID(btEntry(n, 0))
  431. }
  432. return 0
  433. }
  434. // btLastLeaf 沿最右分支下降, 返回以 pid 为根的子树中最右叶子页 ID (找不到返回 0)。
  435. func btLastLeaf(handle page.HandleDb, pid uint32) uint32 {
  436. for pid != 0 {
  437. n := btLoad(handle, pid)
  438. if n == nil {
  439. return 0
  440. }
  441. if n.isLeaf() {
  442. return pid
  443. }
  444. if n.info.Count == 0 {
  445. return 0
  446. }
  447. pid = btPID(btEntry(n, int(n.info.Count)-1))
  448. }
  449. return 0
  450. }
  451. // btUnlinkSubtreeLeaves 把以 pid 为根的子树所覆盖的那一段叶子, 从叶子双向链表中整体摘除。
  452. // 仅在该子树已被确认 "完全为空" 时调用。
  453. func btUnlinkSubtreeLeaves(handle page.HandleDb, pid uint32) {
  454. first := btFirstLeaf(handle, pid)
  455. if first == 0 {
  456. return
  457. }
  458. last := btLastLeaf(handle, pid)
  459. fl := btLoad(handle, first)
  460. ll := btLoad(handle, last)
  461. if fl == nil || ll == nil {
  462. return
  463. }
  464. pre := fl.info.Hdr.PrePage
  465. next := ll.info.Hdr.NextPage
  466. if pre != 0 {
  467. pr := btLoad(handle, pre)
  468. if pr != nil {
  469. pr.info.Hdr.NextPage = next
  470. btSave(handle, pr)
  471. }
  472. }
  473. if next != 0 {
  474. nl := btLoad(handle, next)
  475. if nl != nil {
  476. nl.info.Hdr.PrePage = pre
  477. btSave(handle, nl)
  478. }
  479. }
  480. }
  481. // ======================== B+ 树搜索 ========================
  482. // PKSearchStatus 主键查找的三态结果。
  483. //
  484. // 为什么必须区分"未命中"与"失败": B+ 树不变量成立时, 正常下降到达叶子且键不在,
  485. // 就能断言【全树都不存在该键】—— 键是全局有序的, 该键只会落在被路由到的那片叶子里。
  486. // 旧实现把这两种情况都塞进 found=false, 调用方 (SearchByPK) 只能一律兜底全叶扫描,
  487. // 于是"查一个不存在的主键"是 O(叶数), 而插入查重 (rowStoreHasPK) 每行都要查一次
  488. // 不存在的键 —— 显式主键的批量导入因此退化成 O(N²) (实测 100 万行 ≈1 小时跑不完)。
  489. type PKSearchStatus uint8
  490. const (
  491. // PKFail 下降中断 (页读不出 / 空路由节点): 树结构异常, 结果未知, 允许兜底扫描。
  492. PKFail PKSearchStatus = iota
  493. // PKHit 命中, dpID 与 slot 有效。
  494. PKHit
  495. // PKMiss 正常到达叶子且键不在: 键一定不存在于整棵树。
  496. PKMiss
  497. )
  498. // BTreePKSearchEx 定位主键 (三态版), 语义见 PKSearchStatus。
  499. func BTreePKSearchEx(rootPID uint32, key []byte, handle page.HandleDb) (uint32, uint16, PKSearchStatus) {
  500. if rootPID == 0 {
  501. return 0, 0, PKMiss // 空树: 键必不存在
  502. }
  503. cur := rootPID
  504. for {
  505. n := btLoad(handle, cur)
  506. if n == nil {
  507. return 0, 0, PKFail
  508. }
  509. if n.isLeaf() {
  510. pos, ok := btLeafFind(n, key)
  511. if !ok {
  512. return 0, 0, PKMiss
  513. }
  514. e := btEntry(n, pos)
  515. return btPID(e), btSlot(e), PKHit
  516. }
  517. next := btRoute(n, key)
  518. if next == 0 {
  519. return 0, 0, PKFail // 空路由节点: 结构异常, 不再下探
  520. }
  521. cur = next
  522. }
  523. }
  524. // BTreePKSearch 定位主键, 返回 (dpID, slot, ok)。
  525. //
  526. // slot 是 datapage 的槽位号 (1 基), 直接来自叶条目 —— 拿到后用
  527. // ReadRowBySlotPK(dpID, slot, key) 就能 O(1) 取行, 不必再在页内按 pkID 扫一遍
  528. // (叶条目的键就是 pkID, 见 PrimaryHandler.Insert)。
  529. //
  530. // ok=false 同时覆盖"未命中"与"路由失败"; 需要区分二者的调用方 (SearchByPK 等
  531. // 热路径) 请改用 BTreePKSearchEx。本函数留给测试与演示代码。
  532. func BTreePKSearch(rootPID uint32, key []byte, handle page.HandleDb) (uint32, uint16, bool) {
  533. dpID, slot, st := BTreePKSearchEx(rootPID, key, handle)
  534. return dpID, slot, st == PKHit
  535. }
  536. // ======================== B+ 树叶子节点定位 ========================
  537. // BTreeFindLeftmostLeaf 从根节点路由到最左叶节点,返回叶节点 pageID
  538. func BTreeFindLeftmostLeaf(rootPID uint32, handle page.HandleDb) uint32 {
  539. cur := rootPID
  540. for {
  541. n := btLoad(handle, cur)
  542. if n == nil {
  543. return 0
  544. }
  545. if n.isLeaf() {
  546. return n.info.Hdr.PageID
  547. }
  548. cur = btPID(btEntry(n, 0))
  549. }
  550. }
  551. // BTreeFindRightmostLeaf 从根节点路由到最右叶节点,返回叶节点 pageID
  552. func BTreeFindRightmostLeaf(rootPID uint32, handle page.HandleDb) uint32 {
  553. cur := rootPID
  554. for {
  555. n := btLoad(handle, cur)
  556. if n == nil {
  557. return 0
  558. }
  559. if n.isLeaf() {
  560. return n.info.Hdr.PageID
  561. }
  562. cur = btPID(btEntry(n, int(n.info.Count)-1))
  563. }
  564. }
  565. // ======================== B+ 树范围扫描 ========================
  566. // BTreePKRangeScan 主键 B+ 树范围扫描
  567. func BTreePKRangeScan(rootPID uint32, handle page.HandleDb,
  568. startKey, endKey []byte, startInc, endInc bool, reverse bool,
  569. callback func(row []byte) bool) error {
  570. if rootPID == 0 {
  571. return nil
  572. }
  573. if !reverse {
  574. // 正向: 路由到 startKey 所在叶, 沿 NextPage 遍历
  575. cur := rootPID
  576. for {
  577. n := btLoad(handle, cur)
  578. if n == nil {
  579. return nil
  580. }
  581. if n.isLeaf() {
  582. break
  583. }
  584. cur = btRoute(n, startKey)
  585. }
  586. for cur != 0 {
  587. n := btLoad(handle, cur)
  588. if n == nil {
  589. break
  590. }
  591. for i := 0; i < int(n.info.Count); i++ {
  592. entry := btEntry(n, i)
  593. key := btKey(entry)
  594. if endKey != nil {
  595. cmp := btCmp(key, endKey)
  596. if cmp > 0 || (cmp == 0 && !endInc) {
  597. return nil
  598. }
  599. }
  600. if startKey != nil {
  601. cmp := btCmp(key, startKey)
  602. if cmp < 0 || (cmp == 0 && !startInc) {
  603. continue
  604. }
  605. }
  606. // 叶条目直接给出 (dpID, slot): O(1) 取行, 不再在页内按 pkID 扫。
  607. dp, err := datapage.Load(handle, btPID(entry))
  608. if err != nil {
  609. continue
  610. }
  611. if row, ok := dp.ReadRowBySlotPK(uint32(btSlot(entry)), key); ok {
  612. if !callback(row) {
  613. return nil
  614. }
  615. }
  616. }
  617. cur = n.info.Hdr.NextPage
  618. }
  619. } else {
  620. // 反向: 路由到 endKey 所在叶(或最右叶), 沿 PrePage 遍历
  621. cur := rootPID
  622. if endKey != nil {
  623. for {
  624. n := btLoad(handle, cur)
  625. if n == nil {
  626. return nil
  627. }
  628. if n.isLeaf() {
  629. break
  630. }
  631. cur = btRoute(n, endKey)
  632. }
  633. } else {
  634. cur = BTreeFindRightmostLeaf(rootPID, handle)
  635. }
  636. for cur != 0 {
  637. n := btLoad(handle, cur)
  638. if n == nil {
  639. break
  640. }
  641. for i := int(n.info.Count) - 1; i >= 0; i-- {
  642. entry := btEntry(n, i)
  643. key := btKey(entry)
  644. if startKey != nil {
  645. cmp := btCmp(key, startKey)
  646. if cmp < 0 || (cmp == 0 && !startInc) {
  647. return nil
  648. }
  649. }
  650. if endKey != nil {
  651. cmp := btCmp(key, endKey)
  652. if cmp > 0 || (cmp == 0 && !endInc) {
  653. continue
  654. }
  655. }
  656. // 反向同理: 叶条目给出 (dpID, slot), O(1) 取行。
  657. dp, err := datapage.Load(handle, btPID(entry))
  658. if err != nil {
  659. continue
  660. }
  661. if row, ok := dp.ReadRowBySlotPK(uint32(btSlot(entry)), key); ok {
  662. if !callback(row) {
  663. return nil
  664. }
  665. }
  666. }
  667. cur = n.info.Hdr.PrePage
  668. }
  669. }
  670. return nil
  671. }
  672. // ======================== B+ 树插入 ========================
  673. // BTreePKInsert 插入主键索引条目 (key = pkID 的 8 字节 BigEndian 编码)。
  674. //
  675. // slot 是 datapage 槽位号 (1 基), 来自 InsertRowSortedSlot / AppendRowSlot —— 叶条目把它
  676. // 一并存下, 之后的查找/范围扫描就能 O(1) 取行 (槽位不变式见 datapage 注释)。
  677. //
  678. // 返回 (newRoot, dupDPID, dup, err): dup=true 表示 key 已存在 (主键冲突), dupDPID 是
  679. // 已存在行所在的 DataPage。
  680. func BTreePKInsert(rootPID uint32, key []byte, dpID uint32, slot uint16, handle page.HandleDb) (uint32, uint32, bool, error) {
  681. if rootPID == 0 {
  682. leaf := btNew(handle, true, btLeafEntryLen)
  683. e := make([]byte, btLeafEntryLen)
  684. copy(e[:8], key)
  685. btSetPID(e, dpID)
  686. btSetSlot(e, slot)
  687. btInsEntry(leaf, e, 0)
  688. btSave(handle, leaf)
  689. newRoot := leaf.pageID()
  690. leaf.release()
  691. return newRoot, 0, false, nil
  692. }
  693. var path []btPathEntry
  694. cur := rootPID
  695. for {
  696. n := btLoad(handle, cur)
  697. if n == nil {
  698. return rootPID, 0, false, errors.New("load failed")
  699. }
  700. if n.isLeaf() {
  701. pos, found := btLeafFind(n, key)
  702. if found {
  703. foundPID := btPID(btEntry(n, pos))
  704. n.release()
  705. return rootPID, foundPID, true, nil
  706. }
  707. el := btLeafEntryLen
  708. ne := make([]byte, el)
  709. copy(ne[:8], key)
  710. btSetPID(ne, dpID)
  711. btSetSlot(ne, slot)
  712. ip := btLeafPos(n, key)
  713. if int(n.info.Hdr.UseSize)+el <= btSplitThreshold() {
  714. btInsEntry(n, ne, ip)
  715. btSave(handle, n)
  716. if ip == 0 {
  717. // 新键成为本叶最小键: 沿路径向上同步 "父条目键 == 子树最小键"
  718. for i := len(path) - 1; i >= 0; i-- {
  719. p := path[i]
  720. pb := btLoad(handle, p.nodePID)
  721. if pb == nil || p.childIdx < 0 || p.childIdx >= int(pb.info.Count) {
  722. break
  723. }
  724. copy(btEntry(pb, p.childIdx)[:8], key)
  725. btSave(handle, pb)
  726. pb.release()
  727. if p.childIdx != 0 {
  728. break
  729. }
  730. }
  731. }
  732. n.release()
  733. return rootPID, 0, false, nil
  734. }
  735. cnt := int(n.info.Count)
  736. total := cnt + 1
  737. all := make([]byte, total*el)
  738. if ip > 0 {
  739. copy(all, n.entries[:ip*el])
  740. }
  741. copy(all[ip*el:], ne)
  742. if cnt > ip {
  743. copy(all[(ip+1)*el:], n.entries[ip*el:cnt*el])
  744. }
  745. // 分裂点: 左叶保留 [0,mid), 右叶保留 [mid,total)。
  746. // 主键顺序追加(自增)时走 SequentialRight, mid = total-1 (右叶只留 1 条), 不再对半分。
  747. mid := splitPoint(total, PKSplitCfg, ip == cnt)
  748. // <= 路由约定: 左子叶所有 key 必须严格小于右子叶第一个 key (promoKey)
  749. // 如果 all[mid-1].key == all[mid].key, 右移 mid 直到分隔
  750. for mid < total-1 {
  751. if btCmp(btKey(all[(mid-1)*el:mid*el]), btKey(all[mid*el:(mid+1)*el])) < 0 {
  752. break
  753. }
  754. mid++
  755. }
  756. rl := btNew(handle, true, el)
  757. rc := total - mid
  758. copy(rl.entries, all[mid*el:total*el])
  759. rl.info.Count = uint32(rc)
  760. rl.info.Hdr.UseSize = uint32(page.IndexHeadSize) + uint32(rc*el)
  761. rl.info.Hdr.NextPage = n.info.Hdr.NextPage
  762. rl.info.Hdr.PrePage = n.info.Hdr.PageID
  763. n.info.Hdr.NextPage = rl.info.Hdr.PageID
  764. copy(n.entries, all[:mid*el])
  765. n.info.Count = uint32(mid)
  766. n.info.Hdr.UseSize = uint32(page.IndexHeadSize) + uint32(mid*el)
  767. btSave(handle, n)
  768. btSave(handle, rl)
  769. if rl.info.Hdr.NextPage != 0 {
  770. nl := btLoad(handle, rl.info.Hdr.NextPage)
  771. if nl != nil {
  772. nl.info.Hdr.PrePage = rl.info.Hdr.PageID
  773. btSave(handle, nl)
  774. nl.release()
  775. }
  776. }
  777. // 使用右子树最左叶的第一个 key 作为 promoKey
  778. pk := btLeftmostKey(handle, rl.info.Hdr.PageID)
  779. nr, err := btPromote(handle, rootPID, path, n.info.Hdr.PageID, pk, rl.info.Hdr.PageID)
  780. n.release()
  781. rl.release()
  782. return nr, 0, false, err
  783. }
  784. ci := btChildIdx(n, key)
  785. cpid := btPID(btEntry(n, ci))
  786. path = append(path, btPathEntry{nodePID: cur, childIdx: ci})
  787. n.release()
  788. cur = cpid
  789. }
  790. }
  791. func btPromote(handle page.HandleDb, rootPID uint32, path []btPathEntry, leftPID uint32, promoKey []byte, rightPID uint32) (uint32, error) {
  792. if len(path) == 0 {
  793. el := btIntEntryLen
  794. nr := btNew(handle, false, el)
  795. leftKey := btLeftmostKey(handle, leftPID)
  796. le := make([]byte, el)
  797. copy(le[:8], leftKey)
  798. btSetPID(le, leftPID)
  799. re := make([]byte, el)
  800. copy(re[:8], promoKey)
  801. btSetPID(re, rightPID)
  802. btInsEntry(nr, le, 0)
  803. btInsEntry(nr, re, 1)
  804. btSave(handle, nr)
  805. newRoot := nr.pageID()
  806. nr.release()
  807. return newRoot, nil
  808. }
  809. pi := len(path) - 1
  810. parent := btLoad(handle, path[pi].nodePID)
  811. if parent == nil {
  812. return rootPID, errors.New("parent load failed")
  813. }
  814. ci := path[pi].childIdx
  815. el := int(parent.info.EntryLen)
  816. // ⭐ FIX: 叶子分裂后, 左子树的最小 key 可能已变化
  817. // 必须更新 parent[ci].key = 左子树的新最小 key
  818. newLeftKey := btLeftmostKey(handle, leftPID)
  819. if newLeftKey == nil {
  820. parent.release()
  821. return rootPID, errors.New("leftmost key failed")
  822. }
  823. // 更新 parent[ci] 的 key 和 PID (内部节点条目: 键在偏移 0..8, 子页号在 8..12)
  824. copy(btEntry(parent, ci)[:8], newLeftKey)
  825. btSetPID(btEntry(parent, ci), leftPID)
  826. // 更新 key 后, parent[ci..] 可能不再有序
  827. // 需要将 parent[ci] 冒泡到正确位置, 然后插入 (promoKey, rightPID)
  828. // 1. 冒泡 parent[ci] 到正确位置 (如果 newLeftKey > parent[ci+1].key)
  829. for i := ci; i+1 < int(parent.info.Count); i++ {
  830. if btCmp(btKey(btEntry(parent, i)), btKey(btEntry(parent, i+1))) <= 0 {
  831. break // 已有序
  832. }
  833. // 交换 parent[i] 和 parent[i+1]
  834. tmp := make([]byte, el)
  835. copy(tmp, btEntry(parent, i))
  836. copy(btEntry(parent, i), btEntry(parent, i+1))
  837. copy(btEntry(parent, i+1), tmp)
  838. }
  839. // 2. 在排序位置插入 (promoKey, rightPID)
  840. newPos := int(parent.info.Count)
  841. for i := ci; i < int(parent.info.Count); i++ {
  842. if btCmp(btKey(btEntry(parent, i)), promoKey) >= 0 {
  843. newPos = i
  844. break
  845. }
  846. }
  847. for i := int(parent.info.Count); i > newPos; i-- {
  848. copy(btEntry(parent, i), btEntry(parent, i-1))
  849. }
  850. ne := make([]byte, el)
  851. copy(ne[:8], promoKey)
  852. btSetPID(ne, rightPID)
  853. copy(btEntry(parent, newPos), ne)
  854. parent.info.Count++
  855. parent.info.Hdr.UseSize += uint32(el)
  856. for i := 1; i < int(parent.info.Count); i++ {
  857. if btPID(btEntry(parent, i-1)) == btPID(btEntry(parent, i)) {
  858. println("!!! DUP PID in promote parent=", int(parent.info.Hdr.PageID), "at", i, "pid=", int(btPID(btEntry(parent, i))))
  859. }
  860. }
  861. if newPos > 0 && btCmp(btKey(btEntry(parent, newPos-1)), btKey(btEntry(parent, newPos))) > 0 {
  862. println("!!! promote unsorted at newPos=", newPos, "parent=", int(parent.info.Hdr.PageID))
  863. }
  864. if int(parent.info.Hdr.UseSize)+el <= btSplitThreshold() {
  865. btSave(handle, parent)
  866. parent.release()
  867. return rootPID, nil
  868. }
  869. cnt := int(parent.info.Count)
  870. // 分裂点: 左节点保留 [0,mid), 右节点保留 [mid,cnt)。
  871. // 顺序追加(新条目落在最右)时沿用主键的 SequentialRight 策略。
  872. mid := splitPoint(cnt, PKSplitCfg, newPos == cnt-1)
  873. rn := btNew(handle, false, el)
  874. rc := cnt - mid
  875. copy(rn.entries, parent.entries[mid*el:cnt*el])
  876. rn.info.Count = uint32(rc)
  877. rn.info.Hdr.UseSize = uint32(page.IndexHeadSize) + uint32(rc*el)
  878. parent.info.Count = uint32(mid)
  879. parent.info.Hdr.UseSize = uint32(page.IndexHeadSize) + uint32(mid*el)
  880. btSave(handle, parent)
  881. btSave(handle, rn)
  882. pk2 := make([]byte, 8)
  883. copy(pk2, btKey(btEntry(rn, 0)))
  884. ret, err := btPromote(handle, rootPID, path[:pi], parent.info.Hdr.PageID, pk2, rn.info.Hdr.PageID)
  885. parent.release()
  886. rn.release()
  887. return ret, err
  888. }
  889. // ======================== B+ 树删除 (递归 + 合并/借位) ========================
  890. func BTreePKDelete(rootPID uint32, key []byte, handle page.HandleDb) (uint32, uint32, error) {
  891. if rootPID == 0 {
  892. return 0, 0, page.ErrNotFound
  893. }
  894. delDP, err := btDeleteRec(rootPID, key, nil, handle)
  895. if err != nil {
  896. return rootPID, delDP, err
  897. }
  898. // 收尾归一化:
  899. // - 根为内部节点且只剩 1 个子节点 -> 用该子节点作为新根 (继续向上收缩)
  900. // - 根为内部节点且已无条目 (整棵树被删空) -> 换成一个全新的空叶子,
  901. // 避免留下 "空内部节点" 这种会让查找读到脏数据的状态
  902. // 归一化过程中被跳过的旧根 (降高时旧根整体退役; 删空时旧根被换成新建空叶)。
  903. // 收集后在本函数末尾才归还, 避免 btNew 在同一轮里把这些页又分配回来。
  904. var retiredRoots []uint32
  905. for {
  906. n := btLoad(handle, rootPID)
  907. if n == nil || n.isLeaf() {
  908. break
  909. }
  910. if n.info.Count == 1 {
  911. childPID := btPID(btEntry(n, 0))
  912. if childPID == 0 {
  913. break
  914. }
  915. retiredRoots = append(retiredRoots, rootPID)
  916. rootPID = childPID
  917. continue
  918. }
  919. if n.info.Count == 0 {
  920. // 换成的必须是「空叶子」, 条目长度用叶布局 (14B), 不能用内部节点的 EntryLen
  921. leaf := btNew(handle, true, btLeafEntryLen)
  922. btSave(handle, leaf)
  923. retiredRoots = append(retiredRoots, rootPID)
  924. rootPID = leaf.pageID()
  925. }
  926. break
  927. }
  928. // 只归还退役的旧根自身: Count==1 时其子树仍是新根 (可达), 绝不能按子树回收;
  929. // Count==0 时该节点已是空内部节点, 其子页在上面的递归里已随空子树摘链归还。
  930. for _, pid := range retiredRoots {
  931. btReleasePage(handle, pid)
  932. }
  933. return rootPID, delDP, nil
  934. }
  935. func btCheckSorted(n *btNode) (int, bool) {
  936. cnt := int(n.info.Count)
  937. for i := 1; i < cnt; i++ {
  938. if btCmp(btKey(btEntry(n, i-1)), btKey(btEntry(n, i))) > 0 {
  939. return i, true
  940. }
  941. }
  942. return -1, false
  943. }
  944. // btDebugSorted 叶有序性校验开关。开着时 btInsEntry/btRmEntry 每次条目变更
  945. // 都整叶 O(count) 扫一遍 —— 排障脚手架, 批量灌叶路径上占 ~9% CPU, 生产关闭。
  946. // 排障时改回 true 即可恢复"乱序即打印"的行为。
  947. const btDebugSorted = false
  948. func btReportUnsorted(tag string, n *btNode) {
  949. if !btDebugSorted || n == nil || !n.isLeaf() {
  950. return
  951. }
  952. if bad, ok := btCheckSorted(n); ok {
  953. el := int(n.info.EntryLen)
  954. prev := btEntry(n, bad-1)
  955. cur := btEntry(n, bad)
  956. println("!!! UNSORTED", tag, "pid=", int(n.info.Hdr.PageID), "cnt=", int(n.info.Count),
  957. "el=", el, "bad=", bad, "prev=", int(btKey(prev)[7]), "cur=", int(btKey(cur)[7]))
  958. }
  959. }
  960. func btDeleteRec(nodePID uint32, key []byte, path []btPathEntry, handle page.HandleDb) (uint32, error) {
  961. node := btLoad(handle, nodePID)
  962. if node == nil {
  963. return nodePID, page.ErrNotFound
  964. }
  965. el := int(node.info.EntryLen)
  966. if node.isLeaf() {
  967. pos, ok := btLeafFind(node, key)
  968. if !ok {
  969. return nodePID, page.ErrNotFound
  970. }
  971. delDP := btPID(btEntry(node, pos))
  972. btRmEntry(node, pos)
  973. if node.info.Count == 0 && len(path) == 0 {
  974. btSave(handle, node)
  975. return delDP, nil
  976. }
  977. btSave(handle, node)
  978. if len(path) > 0 && node.info.Count > 0 {
  979. btTryMerge(handle, path, node, el)
  980. }
  981. return delDP, nil
  982. }
  983. ci := btChildIdx(node, key)
  984. childPID := btPID(btEntry(node, ci))
  985. newPath := make([]btPathEntry, len(path)+1)
  986. copy(newPath, path)
  987. newPath[len(path)] = btPathEntry{nodePID: nodePID, childIdx: ci}
  988. delDP, err := btDeleteRec(childPID, key, newPath, handle)
  989. if err != nil {
  990. return delDP, err
  991. }
  992. node = btLoad(handle, nodePID)
  993. if node == nil {
  994. return delDP, errors.New("reload node failed")
  995. }
  996. if node.info.Count > 0 {
  997. // 仅当原 child 条目仍在本节点的 ci 位置时才处理
  998. // (若该条目已被合并移除, 则说明子树已被兄弟吸收, 无需再处理)
  999. if ci >= 0 && ci < int(node.info.Count) && btPID(btEntry(node, ci)) == childPID {
  1000. child := btLoad(handle, childPID)
  1001. if child != nil {
  1002. if child.info.Count == 0 {
  1003. // 注意: 传的是**子节点**的条目长度, 不是本节点的 el —— 叶条目 (14B:
  1004. // key8+dpID4+slot2) 与内部条目 (12B) 长度不同, 借位/合并时按父节点的 12B
  1005. // 去搬运叶条目会截掉 slot 字节, 让被搬的行失效。
  1006. btHandleUnderflow(node, ci, int(child.info.EntryLen), handle)
  1007. // 借位/合并会直接改写本节点并保存, 必须重新加载
  1008. node = btLoad(handle, nodePID)
  1009. if node == nil {
  1010. return delDP, errors.New("reload node failed after underflow")
  1011. }
  1012. } else {
  1013. // 维护不变式: 父条目键 == 子树最小键
  1014. // (删除叶子的最小键后, 父条目键会变旧)
  1015. copy(btEntry(node, ci)[:8], btKey(btEntry(child, 0)))
  1016. }
  1017. }
  1018. }
  1019. }
  1020. // 归一化: 内部节点若只剩一个 "已空" 的子节点 (没有兄弟可合并), 则该节点本身
  1021. // 也变空。此时必须把该空子树整段从叶子链摘除并让本节点变为空 (Count=0),
  1022. // 由上层继续向上收缩。否则会残留 "空内部节点", 破坏路由/查找。
  1023. if !node.isLeaf() && node.info.Count == 1 {
  1024. c0 := btPID(btEntry(node, 0))
  1025. cn := btLoad(handle, c0)
  1026. if cn != nil && cn.info.Count == 0 {
  1027. btUnlinkSubtreeLeaves(handle, c0)
  1028. node.info.Count = 0
  1029. node.info.Hdr.UseSize = uint32(page.IndexHeadSize)
  1030. btSave(handle, node)
  1031. // c0 子树整段摘链后不再被任何父条目或叶子链引用, 连同其内部节点
  1032. // 一并归还 —— 否则每删空一棵子树就漏掉一批索引页。
  1033. btReleaseSubtree(handle, c0)
  1034. return delDP, nil
  1035. }
  1036. }
  1037. if node.info.Count == 0 {
  1038. btSave(handle, node)
  1039. return delDP, nil
  1040. }
  1041. btSave(handle, node)
  1042. return delDP, nil
  1043. }
  1044. func btTryMerge(handle page.HandleDb, path []btPathEntry, node *btNode, el int) {
  1045. if len(path) == 0 {
  1046. return
  1047. }
  1048. nodePID := node.info.Hdr.PageID
  1049. pp := path[len(path)-1].nodePID
  1050. parent := btLoad(handle, pp)
  1051. if parent == nil {
  1052. return
  1053. }
  1054. ci := -1
  1055. for i := 0; i < int(parent.info.Count); i++ {
  1056. if btPID(btEntry(parent, i)) == node.info.Hdr.PageID {
  1057. ci = i
  1058. break
  1059. }
  1060. }
  1061. if ci < 0 {
  1062. return
  1063. }
  1064. if ci > 0 {
  1065. leftPID := btPID(btEntry(parent, ci-1))
  1066. left := btLoad(handle, leftPID)
  1067. if left != nil && left.isLeaf() {
  1068. combined := uint32(page.IndexHeadSize) + (left.info.Count+node.info.Count)*uint32(el)
  1069. threshold := uint32(btSplitThreshold())
  1070. if combined <= threshold {
  1071. if leftPID == node.info.Hdr.PageID {
  1072. println("!!! SELF tryMerge ci>0 pid=", int(leftPID))
  1073. }
  1074. rc := int(node.info.Count)
  1075. copy(left.entries[int(left.info.Count)*el:], node.entries[:rc*el])
  1076. left.info.Count += node.info.Count
  1077. left.info.Hdr.UseSize += uint32(rc * el)
  1078. left.info.Hdr.NextPage = node.info.Hdr.NextPage
  1079. btSave(handle, left)
  1080. btReportUnsorted("tryMergeIntoLeft", left)
  1081. if left.info.Hdr.NextPage != 0 {
  1082. nl := btLoad(handle, left.info.Hdr.NextPage)
  1083. if nl != nil {
  1084. nl.info.Hdr.PrePage = leftPID
  1085. btSave(handle, nl)
  1086. }
  1087. }
  1088. btRmEntry(parent, ci)
  1089. btSave(handle, parent)
  1090. // node 的条目已全部并入 left, 且指向 node 的父条目已被摘除,
  1091. // 叶子链也已改接到 left 的 NextPage —— node 页至此彻底不可达。
  1092. btReleasePage(handle, nodePID)
  1093. return
  1094. }
  1095. }
  1096. }
  1097. if ci < int(parent.info.Count)-1 {
  1098. rightPID := btPID(btEntry(parent, ci+1))
  1099. right := btLoad(handle, rightPID)
  1100. if right != nil && right.isLeaf() {
  1101. combined := uint32(page.IndexHeadSize) + (node.info.Count+right.info.Count)*uint32(el)
  1102. threshold := uint32(btSplitThreshold())
  1103. if combined <= threshold {
  1104. if rightPID == node.info.Hdr.PageID {
  1105. println("!!! SELF tryMerge right pid=", int(rightPID))
  1106. }
  1107. rc := int(right.info.Count)
  1108. copy(node.entries[int(node.info.Count)*el:], right.entries[:rc*el])
  1109. node.info.Count += right.info.Count
  1110. node.info.Hdr.UseSize += uint32(rc * el)
  1111. node.info.Hdr.NextPage = right.info.Hdr.NextPage
  1112. btSave(handle, node)
  1113. btReportUnsorted("tryMergeAbsorbRight", node)
  1114. if node.info.Hdr.NextPage != 0 {
  1115. nl := btLoad(handle, node.info.Hdr.NextPage)
  1116. if nl != nil {
  1117. nl.info.Hdr.PrePage = node.info.Hdr.PageID
  1118. btSave(handle, nl)
  1119. }
  1120. }
  1121. btRmEntry(parent, ci+1)
  1122. btSave(handle, parent)
  1123. // right 的条目已全部并入 node, 且指向 right 的父条目已摘除,
  1124. // 叶子链也已改接到 node 的 NextPage —— right 页至此彻底不可达。
  1125. btReleasePage(handle, rightPID)
  1126. return
  1127. }
  1128. }
  1129. }
  1130. }
  1131. func btHandleUnderflow(parent *btNode, childIdx int, el int, handle page.HandleDb) {
  1132. childPID := btPID(btEntry(parent, childIdx))
  1133. child := btLoad(handle, childPID)
  1134. if child == nil {
  1135. return
  1136. }
  1137. if childIdx > 0 {
  1138. leftPID := btPID(btEntry(parent, childIdx-1))
  1139. leftSib := btLoad(handle, leftPID)
  1140. if leftSib != nil && int(leftSib.info.Count) > 1 {
  1141. borrowed := make([]byte, el)
  1142. copy(borrowed, btEntry(leftSib, int(leftSib.info.Count)-1))
  1143. btRmEntry(leftSib, int(leftSib.info.Count)-1)
  1144. btInsEntry(child, borrowed, 0)
  1145. copy(btEntry(parent, childIdx)[:8], btKey(btEntry(child, 0)))
  1146. btSave(handle, leftSib)
  1147. btReportUnsorted("borrowLeft-child", child)
  1148. btReportUnsorted("borrowLeft-leftSib", leftSib)
  1149. btSave(handle, child)
  1150. btSave(handle, parent)
  1151. return
  1152. }
  1153. }
  1154. if childIdx < int(parent.info.Count)-1 {
  1155. rightPID := btPID(btEntry(parent, childIdx+1))
  1156. rightSib := btLoad(handle, rightPID)
  1157. if rightSib != nil && int(rightSib.info.Count) > 1 {
  1158. borrowed := make([]byte, el)
  1159. copy(borrowed, btEntry(rightSib, 0))
  1160. btRmEntry(rightSib, 0)
  1161. // 右兄弟的首条被追加到 child 尾部: child 的最小键不变
  1162. btInsEntry(child, borrowed, int(child.info.Count))
  1163. copy(btEntry(parent, childIdx)[:8], btKey(btEntry(child, 0)))
  1164. // 右兄弟失去首条后最小键变化, 必须同步其父条目
  1165. copy(btEntry(parent, childIdx+1)[:8], btKey(btEntry(rightSib, 0)))
  1166. btSave(handle, rightSib)
  1167. btReportUnsorted("borrowRight-child", child)
  1168. btReportUnsorted("borrowRight-rightSib", rightSib)
  1169. btSave(handle, child)
  1170. btSave(handle, parent)
  1171. return
  1172. }
  1173. }
  1174. if childIdx > 0 {
  1175. btMergeChildren(handle, parent, childIdx-1, el)
  1176. } else if childIdx < int(parent.info.Count)-1 {
  1177. btMergeChildren(handle, parent, childIdx, el)
  1178. }
  1179. }
  1180. func btMergeChildren(handle page.HandleDb, parent *btNode, sepIdx int, el int) {
  1181. leftPID := btPID(btEntry(parent, sepIdx))
  1182. rightPID := btPID(btEntry(parent, sepIdx+1))
  1183. left := btLoad(handle, leftPID)
  1184. right := btLoad(handle, rightPID)
  1185. if left == nil || right == nil {
  1186. return
  1187. }
  1188. // 注意: 本实现的父条目键是 "子树最小键的副本", 不是经典 B+ 树里独立的分隔键。
  1189. // 因此合并内部节点时直接把右兄弟的条目追加到左兄弟即可,
  1190. // 绝不能把 parent[sepIdx] 下放 (那会写出一条指向左兄弟自身的自引用条目)。
  1191. rc := int(right.info.Count)
  1192. if leftPID == rightPID {
  1193. println("!!! SELF btMergeChildren pid=", int(leftPID), "sepIdx=", sepIdx)
  1194. }
  1195. copy(left.entries[int(left.info.Count)*el:], right.entries[:rc*el])
  1196. left.info.Count += right.info.Count
  1197. left.info.Hdr.UseSize += uint32(rc * el)
  1198. if left.isLeaf() {
  1199. left.info.Hdr.NextPage = right.info.Hdr.NextPage
  1200. }
  1201. btSave(handle, left)
  1202. btReportUnsorted("mergeChildren-left", left)
  1203. if left.isLeaf() && left.info.Hdr.NextPage != 0 {
  1204. nl := btLoad(handle, left.info.Hdr.NextPage)
  1205. if nl != nil {
  1206. nl.info.Hdr.PrePage = leftPID
  1207. btSave(handle, nl)
  1208. }
  1209. }
  1210. btRmEntry(parent, sepIdx+1)
  1211. btSetPID(btEntry(parent, sepIdx), leftPID)
  1212. // 左节点若原本是空节点, 合并后其最小键变为右兄弟的最小键, 必须同步父条目键,
  1213. // 否则会出现 "父键 != 子树最小键", 破坏路由。
  1214. if left.info.Count > 0 {
  1215. copy(btEntry(parent, sepIdx)[:8], btKey(btEntry(left, 0)))
  1216. }
  1217. btSave(handle, parent)
  1218. // right 的条目已全部并入 left, 父条目 sepIdx+1 已摘除 (叶子链在上面的
  1219. // NextPage/PrePage 重接中已绕过 right) —— right 子树至此彻底不可达。
  1220. btReleasePage(handle, rightPID)
  1221. }
  1222. // ======================== DataPage 操作 ========================
  1223. // FindSpaceInLeaf 在叶节点中找到有空间的 DataPage
  1224. func FindSpaceInLeaf(handle page.HandleDb, rootPID uint32, rowSize uint32, key []byte) (uint32, bool) {
  1225. cur := rootPID
  1226. for {
  1227. n := btLoad(handle, cur)
  1228. if n == nil {
  1229. return 0, false
  1230. }
  1231. if n.isLeaf() {
  1232. pos := btLeafPos(n, key)
  1233. if pos >= int(n.info.Count) {
  1234. pos = int(n.info.Count) - 1
  1235. }
  1236. if pos < 0 {
  1237. return 0, false
  1238. }
  1239. dpID := btPID(btEntry(n, pos))
  1240. dp, err := datapage.Load(handle, dpID)
  1241. if err == nil && !dp.IsFull(rowSize) {
  1242. return dpID, true
  1243. }
  1244. if n.info.Hdr.PrePage != 0 {
  1245. prevN := btLoad(handle, n.info.Hdr.PrePage)
  1246. if prevN != nil && prevN.isLeaf() && prevN.info.Count > 0 {
  1247. prevDPID := btPID(btEntry(prevN, int(prevN.info.Count)-1))
  1248. prevDP, err := datapage.Load(handle, prevDPID)
  1249. if err == nil && !prevDP.IsFull(rowSize) {
  1250. return prevDPID, true
  1251. }
  1252. }
  1253. }
  1254. if n.info.Hdr.NextPage != 0 {
  1255. nextN := btLoad(handle, n.info.Hdr.NextPage)
  1256. if nextN != nil && nextN.isLeaf() && nextN.info.Count > 0 {
  1257. nextDPID := btPID(btEntry(nextN, 0))
  1258. nextDP, err := datapage.Load(handle, nextDPID)
  1259. if err == nil && !nextDP.IsFull(rowSize) {
  1260. return nextDPID, true
  1261. }
  1262. }
  1263. }
  1264. return 0, false
  1265. }
  1266. cur = btRoute(n, key)
  1267. }
  1268. }
  1269. // LastLeafDPID 路由到目标叶子, 返回其最后一个条目指向的 DataPage ID —— 即"当前正在
  1270. // 追加的那一页"。不判断是否装得下: 调用方拿到它之后可以按配置整理一次再决定是否新开页
  1271. // (见 datapage.CompactForInsert)。
  1272. //
  1273. // 只检查路由目标叶子, 不跨叶子查找 (保证 B+ 树不变性)。
  1274. func LastLeafDPID(handle page.HandleDb, rootPID uint32, key []byte) (uint32, bool) {
  1275. cur := rootPID
  1276. for {
  1277. n := btLoad(handle, cur)
  1278. if n == nil {
  1279. return 0, false
  1280. }
  1281. if n.isLeaf() {
  1282. if n.info.Count == 0 {
  1283. return 0, false
  1284. }
  1285. return btPID(btEntry(n, int(n.info.Count)-1)), true
  1286. }
  1287. cur = btRoute(n, key)
  1288. }
  1289. }
  1290. // FindLastLeafDP 路由到目标叶子, 检查最后一个条目的 DataPage 是否有空间
  1291. // 只检查路由目标叶子, 不跨叶子查找 (保证 B+ 树不变性)
  1292. func FindLastLeafDP(handle page.HandleDb, rootPID uint32, rowSize uint32, key []byte) (uint32, bool) {
  1293. dpID, ok := LastLeafDPID(handle, rootPID, key)
  1294. if !ok {
  1295. return 0, false
  1296. }
  1297. dp, err := datapage.Load(handle, dpID)
  1298. if err != nil || dp.IsFull(rowSize) {
  1299. return 0, false
  1300. }
  1301. return dpID, true
  1302. }
  1303. // GetDataPageIDs 返回所有 DataPage ID (去重, 用于全表扫描)
  1304. func GetDataPageIDs(handle page.HandleDb, rootPID uint32) []uint32 {
  1305. if rootPID == 0 {
  1306. return nil
  1307. }
  1308. var dpIDs []uint32
  1309. seen := make(map[uint32]bool)
  1310. walked := make(map[uint32]bool) // 叶链成环保护: 坏 next 会让下面这个 for 永远转不完
  1311. cur := rootPID
  1312. for {
  1313. n := btLoadNode(handle, cur)
  1314. if n == nil {
  1315. break
  1316. }
  1317. if n.isLeaf() {
  1318. break
  1319. }
  1320. cur = btChildPID(n, btEntry(n, 0))
  1321. }
  1322. for cur != 0 {
  1323. if walked[cur] {
  1324. break
  1325. }
  1326. walked[cur] = true
  1327. n := btLoadNode(handle, cur)
  1328. if n == nil {
  1329. break
  1330. }
  1331. for i := 0; i < int(n.info.Count); i++ {
  1332. dpID := btPID(btEntry(n, i))
  1333. if dpID == 0 || seen[dpID] {
  1334. continue
  1335. }
  1336. // 合法性兜底: 条目错位解析出来的"页号"绝不能进入回收列表
  1337. if handle.ValidPage != nil && !handle.ValidPage(dpID) {
  1338. continue
  1339. }
  1340. seen[dpID] = true
  1341. dpIDs = append(dpIDs, dpID)
  1342. }
  1343. cur = n.info.Hdr.NextPage
  1344. }
  1345. return dpIDs
  1346. }
  1347. // FindDataPageIDByPK 定位存放 pkID=pkKeyBE 那一行的 DataPage ID。
  1348. // 用途: 当行因 schema 演进变长、页内已装不下 (RebuildWith 返回 errRowTooLarge) 时,
  1349. // 需要先定位旧页才能把行搬到新页并改写 B+ 树叶条目。
  1350. //
  1351. // 实现: 沿叶链逐叶二分找键 (叶键就是 pkID), 命中即拿到 (dpID, slot), 再用
  1352. // ReadRowBySlotPK 校验该槽位确实是这一行 (陈旧槽位防御) 才返回。比旧实现「每叶每条
  1353. // 目都 Load 一次 DataPage 再整页扫 pkID」少掉全部 DataPage 全页扫描。
  1354. func FindDataPageIDByPK(rootPID uint32, handle page.HandleDb, pkKeyBE []byte) (uint32, bool) {
  1355. if rootPID == 0 {
  1356. return 0, false
  1357. }
  1358. cur := btFirstLeaf(handle, rootPID)
  1359. for cur != 0 {
  1360. n := btLoad(handle, cur)
  1361. if n == nil {
  1362. break
  1363. }
  1364. if pos, ok := btLeafFind(n, pkKeyBE); ok {
  1365. e := btEntry(n, pos)
  1366. dpID := btPID(e)
  1367. if dp, err := datapage.Load(handle, dpID); err == nil {
  1368. if _, ok := dp.ReadRowBySlotPK(uint32(btSlot(e)), pkKeyBE); ok {
  1369. return dpID, true
  1370. }
  1371. }
  1372. }
  1373. cur = n.info.Hdr.NextPage
  1374. }
  1375. return 0, false
  1376. }
  1377. // ScanAllLeavesForPK 扫描所有叶子查找 pkID(LE)。
  1378. // 这是 BTreePKSearch 之外的兜底路径: 树结构异常 (路由坏掉) 时仍能捞到行。
  1379. func ScanAllLeavesForPK(rootPID uint32, handle page.HandleDb, pkKeyBE []byte) ([]byte, bool) {
  1380. if rootPID == 0 {
  1381. return nil, false
  1382. }
  1383. cur := btFirstLeaf(handle, rootPID)
  1384. for cur != 0 {
  1385. n := btLoad(handle, cur)
  1386. if n == nil {
  1387. break
  1388. }
  1389. // 叶内二分: 键即 pkID, 命中就直接拿到 (dpID, slot)
  1390. if pos, ok := btLeafFind(n, pkKeyBE); ok {
  1391. e := btEntry(n, pos)
  1392. if dp, err := datapage.Load(handle, btPID(e)); err == nil {
  1393. if rowData, ok := dp.ReadRowBySlotPK(uint32(btSlot(e)), pkKeyBE); ok {
  1394. return rowData, true
  1395. }
  1396. }
  1397. }
  1398. cur = n.info.Hdr.NextPage
  1399. }
  1400. return nil, false
  1401. }
  1402. // ScanAndDelete 扫描所有叶子按 indexKey 删除
  1403. func ScanAndDelete(rootPID uint32, handle page.HandleDb, indexKey, pkKeyBE []byte) error {
  1404. if rootPID == 0 {
  1405. return page.ErrNotFound
  1406. }
  1407. cur := rootPID
  1408. for {
  1409. n := btLoad(handle, cur)
  1410. if n == nil {
  1411. return page.ErrNotFound
  1412. }
  1413. if n.isLeaf() {
  1414. break
  1415. }
  1416. cur = btPID(btEntry(n, 0))
  1417. }
  1418. for cur != 0 {
  1419. n := btLoad(handle, cur)
  1420. if n == nil {
  1421. break
  1422. }
  1423. for i := 0; i < int(n.info.Count); i++ {
  1424. e := btEntry(n, i)
  1425. // 叶键就是 pkID: 只处理键匹配的条目, 其余条目所在的页根本不用碰
  1426. if btCmp(btKey(e), indexKey) != 0 {
  1427. continue
  1428. }
  1429. dp, err := datapage.Load(handle, btPID(e))
  1430. if err == nil {
  1431. slotID := uint32(btSlot(e))
  1432. deleted := false
  1433. if _, ok := dp.ReadRowBySlotPK(slotID, indexKey); ok {
  1434. // 槽位命中 → O(1) 删除
  1435. deleted = dp.DeleteBySlot(slotID)
  1436. } else if dp.DeleteByIndexKey(indexKey, 8) {
  1437. deleted = true
  1438. } else if dp.DeleteByPK(pkKeyBE) {
  1439. deleted = true
  1440. }
  1441. if deleted {
  1442. if dp.AllDeleted() {
  1443. newRoot, _, err := BTreePKDelete(rootPID, indexKey, handle)
  1444. if err != nil {
  1445. return err
  1446. }
  1447. _ = newRoot
  1448. } else {
  1449. dp.Save()
  1450. }
  1451. return nil
  1452. }
  1453. }
  1454. }
  1455. cur = n.info.Hdr.NextPage
  1456. }
  1457. return page.ErrNotFound
  1458. }
  1459. // ScanAndDeleteByPK 扫描所有叶子按 pkID 删除
  1460. func ScanAndDeleteByPK(rootPID uint32, handle page.HandleDb, pkKeyBE []byte) error {
  1461. if rootPID == 0 {
  1462. return page.ErrNotFound
  1463. }
  1464. cur := rootPID
  1465. for {
  1466. n := btLoad(handle, cur)
  1467. if n == nil {
  1468. return page.ErrNotFound
  1469. }
  1470. if n.isLeaf() {
  1471. break
  1472. }
  1473. cur = btPID(btEntry(n, 0))
  1474. }
  1475. for cur != 0 {
  1476. n := btLoad(handle, cur)
  1477. if n == nil {
  1478. break
  1479. }
  1480. for i := 0; i < int(n.info.Count); i++ {
  1481. e := btEntry(n, i)
  1482. if btCmp(btKey(e), pkKeyBE) != 0 {
  1483. continue
  1484. }
  1485. dp, err := datapage.Load(handle, btPID(e))
  1486. if err == nil {
  1487. slotID := uint32(btSlot(e))
  1488. deleted := false
  1489. if _, ok := dp.ReadRowBySlotPK(slotID, pkKeyBE); ok {
  1490. deleted = dp.DeleteBySlot(slotID)
  1491. } else if dp.DeleteByPK(pkKeyBE) {
  1492. deleted = true
  1493. }
  1494. if deleted {
  1495. if dp.AllDeleted() {
  1496. newRoot, _, err := BTreePKDelete(rootPID, pkKeyBE, handle)
  1497. if err != nil {
  1498. return err
  1499. }
  1500. _ = newRoot
  1501. } else {
  1502. dp.Save()
  1503. }
  1504. return nil
  1505. }
  1506. }
  1507. }
  1508. cur = n.info.Hdr.NextPage
  1509. }
  1510. return page.ErrNotFound
  1511. }
  1512. // ScanAndUpdate 扫描所有叶子按 pkID 更新
  1513. func ScanAndUpdate(rootPID uint32, handle page.HandleDb, pkKeyBE, storeRowData []byte) error {
  1514. if rootPID == 0 {
  1515. return page.ErrNotFound
  1516. }
  1517. cur := rootPID
  1518. for {
  1519. n := btLoad(handle, cur)
  1520. if n == nil {
  1521. return page.ErrNotFound
  1522. }
  1523. if n.isLeaf() {
  1524. break
  1525. }
  1526. cur = btPID(btEntry(n, 0))
  1527. }
  1528. for cur != 0 {
  1529. n := btLoad(handle, cur)
  1530. if n == nil {
  1531. break
  1532. }
  1533. for i := 0; i < int(n.info.Count); i++ {
  1534. e := btEntry(n, i)
  1535. if btCmp(btKey(e), pkKeyBE) != 0 {
  1536. continue
  1537. }
  1538. dp, err := datapage.Load(handle, btPID(e))
  1539. if err == nil {
  1540. slotID := uint32(btSlot(e))
  1541. if err := dp.UpdateBySlot(slotID, storeRowData); err == nil {
  1542. return nil
  1543. } else if err := dp.RebuildWithSlot(slotID, storeRowData); err == nil {
  1544. // 行长变化 (schema 演进后旧行补新字段) → 页内重建, 支持变长
  1545. return nil
  1546. }
  1547. }
  1548. }
  1549. cur = n.info.Hdr.NextPage
  1550. }
  1551. return page.ErrNotFound
  1552. }
  1553. // BTreeValidate 检查 B+ 树内部节点条目排序
  1554. func BTreeValidate(handle page.HandleDb, rootPID uint32) []string {
  1555. var issues []string
  1556. if rootPID == 0 {
  1557. return issues
  1558. }
  1559. var check func(pid uint32, depth int)
  1560. check = func(pid uint32, depth int) {
  1561. n := btLoad(handle, pid)
  1562. if n == nil {
  1563. issues = append(issues, fmt.Sprintf("d=%d: load fail pid=%d", depth, pid))
  1564. return
  1565. }
  1566. cnt := int(n.info.Count)
  1567. if n.isLeaf() {
  1568. for i := 1; i < cnt; i++ {
  1569. pk := btKey(btEntry(n, i-1))
  1570. ck := btKey(btEntry(n, i))
  1571. if btCmp(pk, ck) >= 0 {
  1572. issues = append(issues, fmt.Sprintf("d=%d leaf=%d: unsorted [%d]>=[%d]", depth, pid, i-1, i))
  1573. }
  1574. }
  1575. return
  1576. }
  1577. for i := 1; i < cnt; i++ {
  1578. pk := btKey(btEntry(n, i-1))
  1579. ck := btKey(btEntry(n, i))
  1580. if btCmp(pk, ck) > 0 {
  1581. issues = append(issues, fmt.Sprintf("d=%d int=%d: UNSORTED [%d]>[%d]", depth, pid, i-1, i))
  1582. }
  1583. }
  1584. for i := 0; i < cnt; i++ {
  1585. childPID := btPID(btEntry(n, i))
  1586. check(childPID, depth+1)
  1587. }
  1588. }
  1589. check(rootPID, 0)
  1590. return issues
  1591. }
  1592. // BTreeTraceSearch 搜索并打印路由路径
  1593. func BTreeTraceSearch(rootPID uint32, key []byte, handle page.HandleDb) {
  1594. if rootPID == 0 {
  1595. fmt.Println("Tree is empty")
  1596. return
  1597. }
  1598. cur := rootPID
  1599. depth := 0
  1600. for {
  1601. n := btLoad(handle, cur)
  1602. if n == nil {
  1603. fmt.Printf(" d=%d: LOAD FAIL pid=%d\n", depth, cur)
  1604. return
  1605. }
  1606. cnt := int(n.info.Count)
  1607. if n.isLeaf() {
  1608. fk := uint64(0)
  1609. lk := uint64(0)
  1610. if cnt > 0 {
  1611. fk = binary.BigEndian.Uint64(btKey(btEntry(n, 0)))
  1612. lk = binary.BigEndian.Uint64(btKey(btEntry(n, cnt-1)))
  1613. }
  1614. pos, found := btLeafFind(n, key)
  1615. fmt.Printf(" d=%d LEAF pid=%d cnt=%d first=%d last=%d pos=%d found=%v\n",
  1616. depth, cur, cnt, fk, lk, pos, found)
  1617. return
  1618. }
  1619. fmt.Printf(" d=%d INT pid=%d cnt=%d [", depth, cur, cnt)
  1620. for i := 0; i < cnt; i++ {
  1621. k := binary.BigEndian.Uint64(btKey(btEntry(n, i)))
  1622. p := btPID(btEntry(n, i))
  1623. if i > 0 {
  1624. fmt.Printf(", ")
  1625. }
  1626. if i < 3 || i >= cnt-2 {
  1627. fmt.Printf("(k=%d,p=%d)", k, p)
  1628. } else if i == 3 {
  1629. fmt.Printf("...")
  1630. }
  1631. }
  1632. fmt.Printf("]\n")
  1633. ci := btChildIdx(n, key)
  1634. fmt.Printf(" btChildIdx(%d) = %d\n", binary.BigEndian.Uint64(key), ci)
  1635. cur = btPID(btEntry(n, ci))
  1636. depth++
  1637. }
  1638. }
  1639. // CollectAllPageIDs 收集 B+ 树所有页 ID (包括内部节点和叶子节点)
  1640. // 用于 DropTable 时释放所有 B+ 树占用的页
  1641. func CollectAllPageIDs(handle page.HandleDb, rootPID uint32) []uint32 {
  1642. if rootPID == 0 {
  1643. return nil
  1644. }
  1645. var pages []uint32
  1646. seen := map[uint32]bool{} // 去重 + 成环保护: 同一子页被多个父条目引用会让 BFS 无限膨胀
  1647. // BFS 遍历所有节点
  1648. queue := []uint32{rootPID}
  1649. for len(queue) > 0 {
  1650. pid := queue[0]
  1651. queue = queue[1:]
  1652. if seen[pid] {
  1653. continue
  1654. }
  1655. seen[pid] = true
  1656. pages = append(pages, pid)
  1657. // 必须用校验版加载: 非 B+ 页被强行按偏移解析时, Count 会是行键这类大整数,
  1658. // btPID 随之吐出从未分配过的垃圾页号 —— 它们会被 FreePage 直接写进空闲链。
  1659. n := btLoadNode(handle, pid)
  1660. if n == nil {
  1661. continue
  1662. }
  1663. if !n.isLeaf() {
  1664. // 内部节点: 收集所有子节点
  1665. for i := 0; i < int(n.info.Count); i++ {
  1666. childPID := btChildPID(n, btEntry(n, i))
  1667. if childPID == 0 {
  1668. continue
  1669. }
  1670. if handle.ValidPage != nil && !handle.ValidPage(childPID) {
  1671. continue
  1672. }
  1673. queue = append(queue, childPID)
  1674. }
  1675. }
  1676. }
  1677. return pages
  1678. }
  1679. // CollectLeafEntries 返回整棵树所有叶子条目 (副本), 顺序为先序遍历。
  1680. //
  1681. // 主键索引的每条叶子条目形如 [key(8)][dpID(4)][slot(2)] (见 btLeafEntryLen), 即「每行一条」。
  1682. // 该函数供诊断与回归测试使用: 统计条目数并与存活行数比对, 就能发现删除后残留在树里的
  1683. // 陈旧条目 (它们指向随后被回收复用的页, 是 not-data-page / freeLive 的根源)。
  1684. func CollectLeafEntries(handle page.HandleDb, rootPID uint32) [][]byte {
  1685. if rootPID == 0 {
  1686. return nil
  1687. }
  1688. var out [][]byte
  1689. queue := []uint32{rootPID}
  1690. seen := map[uint32]bool{}
  1691. for len(queue) > 0 {
  1692. pid := queue[0]
  1693. queue = queue[1:]
  1694. if seen[pid] {
  1695. continue
  1696. }
  1697. seen[pid] = true
  1698. n := btLoad(handle, pid)
  1699. if n == nil {
  1700. continue
  1701. }
  1702. if n.isLeaf() {
  1703. for i := 0; i < int(n.info.Count); i++ {
  1704. e := btEntry(n, i)
  1705. cp := make([]byte, len(e))
  1706. copy(cp, e)
  1707. out = append(out, cp)
  1708. }
  1709. continue
  1710. }
  1711. for i := 0; i < int(n.info.Count); i++ {
  1712. // 与 CollectAllPageIDs 同理: 内部条目子页号偏移随索引类型而变。
  1713. childPID := btChildPID(n, btEntry(n, i))
  1714. if childPID != 0 {
  1715. queue = append(queue, childPID)
  1716. }
  1717. }
  1718. }
  1719. return out
  1720. }
  1721. // ======================== 主键叶批量插入 ========================
  1722. // PKBulkItem 主键 B+ 树叶条目原料: [pkID(8B BE)] → (数据页, 槽位)。
  1723. type PKBulkItem struct {
  1724. Key [8]byte // pkID BigEndian
  1725. DPID uint32
  1726. Slot uint16
  1727. }
  1728. // btDescendLeaf 从 rootPID 沿 key 下降到路由目标叶 (持有节点缓冲, 调用方负责 release)。
  1729. func btDescendLeaf(handle page.HandleDb, rootPID uint32, key []byte) *btNode {
  1730. cur := rootPID
  1731. for {
  1732. n := btLoad(handle, cur)
  1733. if n == nil {
  1734. return nil
  1735. }
  1736. if n.isLeaf() {
  1737. return n
  1738. }
  1739. cur = btRoute(n, key)
  1740. n.release()
  1741. }
  1742. }
  1743. // BTreePKBulkInsert 批量插入主键叶条目。items 必须按 Key 升序 (调用方保证;
  1744. // 自增主键的批量插入天然满足)。
  1745. //
  1746. // 与逐条 BTreePKInsert 的区别只有"叶节点驻内存": 排序批量下目标叶单调右移,
  1747. // 驻留叶加载一次、页内定位插入、装满才落盘 —— 逐条路径每条都要"下降加载整页 +
  1748. // 整页回写" (16KB×2 memcpy/条), 批量路径摊到 ~每千条一次。
  1749. //
  1750. // 正确性关键 (与 SBTBulkInsert 同一套约定): 驻留叶的可用区间是 [叶首键, 上界),
  1751. // 上界 = 右邻叶首键 (叶链 NextPage)。判定"该不该留在当前叶"必须用上界 ——
  1752. // 用"大于叶末键"会在条目越过右邻叶首键时仍往左叶追加, 破坏父层路由。
  1753. // 单条 BTreePKInsert 兜底只发生在三种边角: 叶满要分裂、条目成为叶最小键
  1754. // (父键同步)、链尾之后还有更大键 —— 分裂/promote/父键同步全套复用既有逻辑。
  1755. //
  1756. // 返回新根 (与 BTreePKInsert 一致, 树未改根时等于 rootPID)。
  1757. func BTreePKBulkInsert(rootPID uint32, items []PKBulkItem, handle page.HandleDb) (uint32, error) {
  1758. if len(items) == 0 {
  1759. return rootPID, nil
  1760. }
  1761. el := btLeafEntryLen
  1762. th := btSplitThreshold()
  1763. var leaf *btNode
  1764. var upper []byte // 驻留叶键上界 (右邻叶首键的副本); nil = 最右叶 (上界无穷)
  1765. // upperOf 计算叶的上界。副本必须复制出来 —— 右邻叶还回缓冲池后内存会被复用。
  1766. // 空叶 (健康树上不应出现) 一律当"无上界", 交给后续单条兜底修正。
  1767. upperOf := func(n *btNode) []byte {
  1768. if n.info.Hdr.NextPage == 0 {
  1769. return nil
  1770. }
  1771. nl := btLoad(handle, n.info.Hdr.NextPage)
  1772. if nl == nil || nl.info.Count == 0 {
  1773. if nl != nil {
  1774. nl.release()
  1775. }
  1776. return nil
  1777. }
  1778. up := make([]byte, 8)
  1779. copy(up, btKey(btEntry(nl, 0)))
  1780. nl.release()
  1781. return up
  1782. }
  1783. // flushLeaf 把驻留叶落盘 (单条兜底 / 批次结束前必须先做: 兜底路径会自己 btLoad
  1784. // 这张页, 不落盘就会读到池里的陈旧副本, 驻留期间的插入全丢)。
  1785. flushLeaf := func() {
  1786. if leaf != nil {
  1787. btSave(handle, leaf)
  1788. if rootPID == 0 {
  1789. rootPID = leaf.pageID()
  1790. }
  1791. leaf.release()
  1792. leaf = nil
  1793. }
  1794. }
  1795. // descend 按 key 从根下降到路由目标叶, 并取上界。
  1796. descend := func(key []byte) error {
  1797. n := btDescendLeaf(handle, rootPID, key)
  1798. if n == nil {
  1799. return errors.New("pk bulk insert: load failed")
  1800. }
  1801. leaf = n
  1802. upper = upperOf(n)
  1803. return nil
  1804. }
  1805. // stepTo 沿叶链从 from 走到第一张非空叶 (或链尾) 并驻留。
  1806. stepTo := func(from uint32) error {
  1807. cur := from
  1808. for {
  1809. nd := btLoad(handle, cur)
  1810. if nd == nil {
  1811. return errors.New("pk bulk insert: chain load failed")
  1812. }
  1813. if nd.info.Count > 0 || nd.info.Hdr.NextPage == 0 {
  1814. leaf = nd
  1815. upper = upperOf(nd)
  1816. return nil
  1817. }
  1818. cur = nd.info.Hdr.NextPage // 空叶没有承载键, 直接跳过
  1819. nd.release()
  1820. }
  1821. }
  1822. for i := range items {
  1823. it := &items[i]
  1824. if leaf == nil {
  1825. if rootPID == 0 {
  1826. leaf = btNew(handle, true, btLeafEntryLen)
  1827. upper = nil
  1828. } else if err := descend(it.Key[:]); err != nil {
  1829. return rootPID, err
  1830. }
  1831. }
  1832. // 越过驻留叶上界: 目标叶在右侧。落盘驻留叶后沿叶链右移 (比重新下降省
  1833. // 2/3 页加载); 链走尽了重新下降, 让路由把后续大键送进(新的)最右叶。
  1834. for upper != nil && btCmp(it.Key[:], upper) >= 0 {
  1835. next := leaf.info.Hdr.NextPage
  1836. flushLeaf()
  1837. if next == 0 {
  1838. break
  1839. }
  1840. if err := stepTo(next); err != nil {
  1841. return rootPID, err
  1842. }
  1843. }
  1844. if leaf == nil {
  1845. if err := descend(it.Key[:]); err != nil {
  1846. return rootPID, err
  1847. }
  1848. }
  1849. if int(leaf.info.Hdr.UseSize)+el > th {
  1850. // 叶满: 落盘驻留叶, 单条兜底 (分裂/promote/叶链维护全套)
  1851. flushLeaf()
  1852. nr, _, _, err := BTreePKInsert(rootPID, it.Key[:], it.DPID, it.Slot, handle)
  1853. if err != nil {
  1854. return rootPID, err
  1855. }
  1856. rootPID = nr
  1857. continue
  1858. }
  1859. cnt := int(leaf.info.Count)
  1860. ip, _ := btLeafFind(leaf, it.Key[:])
  1861. if ip == 0 && cnt > 0 {
  1862. // 新条目将成为叶最小键: "父条目键 == 子树最小键"不变式需要沿路径
  1863. // 向上同步, 交给单条路径 (整批有序, 只可能发生在批首条目)
  1864. flushLeaf()
  1865. nr, _, _, err := BTreePKInsert(rootPID, it.Key[:], it.DPID, it.Slot, handle)
  1866. if err != nil {
  1867. return rootPID, err
  1868. }
  1869. rootPID = nr
  1870. continue
  1871. }
  1872. e := make([]byte, el)
  1873. copy(e[:8], it.Key[:])
  1874. btSetPID(e, it.DPID)
  1875. btSetSlot(e, it.Slot)
  1876. btInsEntry(leaf, e, ip)
  1877. }
  1878. flushLeaf()
  1879. return rootPID, nil
  1880. }