hash_handler.go 42 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300
  1. package index
  2. import (
  3. "TsDB2/DB/core/page"
  4. "bytes"
  5. "encoding/binary"
  6. "errors"
  7. "sort"
  8. "unsafe"
  9. )
  10. // =============================================================================
  11. // Hash 索引 — 磁盘持久化「线性哈希 (Linear Hashing)」二级索引
  12. //
  13. // 为什么不用「整体翻倍 + 全表重哈希」:
  14. // 旧实现一旦有任意一个桶超阈值, 就把桶数翻倍并把所有条目收进内存重新路由。
  15. // 路由是 hash & (bucketCount-1), 同一个 key 的 hash 相同, 翻倍后仍旧全部落到
  16. // 同一个桶; 于是每插入一行都会再次超阈值、再次翻倍 → 桶数指数膨胀直至卡死。
  17. // 低基数字段 (Age/Dept 只有几十个值) 必然踩中。
  18. //
  19. // 线性哈希的做法:
  20. // - 维护 level / splitPointer。桶号 = h & (2^level-1), 若结果 < splitPointer
  21. // 则再看下一位: h & (2^(level+1)-1)。可用桶数 = 2^level + splitPointer。
  22. // - 每次插入最多分裂「splitPointer 指向的那一个桶」, 把它的条目按新增的那一位
  23. // 一分为二 (原地/增量), 绝不重哈希全表, 也绝不因重复 key 无限翻倍。
  24. // - 是否分裂由「全局负载因子」决定 (平均每桶条目数), 而非单个热点桶的绝对条数,
  25. // 因此重复 key 只会让该桶的溢出链变长 (查找仍 O(链长)), 不会引发无意义分裂。
  26. // - 每次删除最多撤销 (合并) 一个桶, 同样只动两个桶。
  27. //
  28. // 页面布局
  29. // ---------------------------------------------------------------------------
  30. // 元数据页 (即 entryPage, 注册为 IndexDataIndex):
  31. // [0, 32) page.IndexHead
  32. // [32,36) magic 格式标识, 用于识别旧版本/不兼容布局
  33. // [36,40) level
  34. // [40,44) splitPointer
  35. // [44,48) totalEntries 活跃条目总数 (驱动负载因子)
  36. // [48,52) dirStartPID 桶目录首页
  37. // [52,56) dirPageCount 桶目录页数
  38. // [56,60) keyLen 索引键字节长度 (条目长度 = keyLen + 8)
  39. // [60,64) dirTailPID 桶目录链尾页 (整条目录页链可 O(1) 回收)
  40. //
  41. // 桶目录页 (多页, 用 PageHdr.NextPage 串链; 槽位区从 PageHdrSize 开始):
  42. // [0, 20) page.PageHdr
  43. // [20, ..) 槽位数组, 每槽 12 字节: [链头(4)][链尾(4)][链长(4)]
  44. // 槽 i 描述桶 i 的溢出链 (链头/链尾页 ID 与链长; 0/0/0 = 空桶)
  45. // 每页槽位数 = (16384 - 20) / 12 = 1362
  46. // 关键: 链尾与链长随链头一起持久化 —— 回收整条桶链只需 (head, tail, count)
  47. // 一次拼接 (O(1)), 不必读链内任何一页。
  48. //
  49. // 桶页 (同一桶的溢出链, 用 PageHdr.NextPage 串链 —— 与空闲链同一字段):
  50. // [0, 20) page.PageHdr (NextPage 即同桶下一页)
  51. // [20,24) BucketID
  52. // [24,28) Count 本页活跃条目数
  53. // [28, ..) 条目, 每条 keyLen + 8 字节: [key(keyLen)][pkID(8B BE, 最高位=删除标记)]
  54. // 关键一: 条目里保存「原始 key」, 因此等值查找可做精确比较 (无碰撞误判),
  55. // 范围扫描也能做真正的键边界过滤。
  56. // 关键二: 桶页的 next 就是公共头 PageHdr.NextPage, 与空闲链完全同一字段 ——
  57. // 整条桶链可直接交给 FreeChain 拼接进空闲链, 无需逐页改写回收。
  58. //
  59. // 页回收策略:
  60. // 分裂/合并/压缩一律「复用旧页」而不是释放后重申请 (freePage 会把页写回空闲
  61. // 链表, 而与页缓存的一致性无保证, 直接释放有让陈旧缓存覆盖空闲链表的风险)。
  62. // 具体做法: 用旧链的页去承载新链, 用不完的页挂到链尾作为空页 (仍可达、可复用),
  63. // 保证不产生孤儿页 → DB 文件不会随分裂/删除无限膨胀。
  64. //
  65. // 范围扫描:
  66. // 哈希不保序, 但条目存了原始 key, 因此 RangeScan 做「(等值则定位单桶 / 否则
  67. // 全桶扫描) + 键边界过滤 + 按 key 排序」, 结果正确且满足 ORDER BY 语义。
  68. // =============================================================================
  69. const (
  70. hashMagic = uint32(0x54484833) // "THH3" (v3: 桶页统一 PageHdr.NextPage, 目录槽记录链尾/链长)
  71. hashMinLevel = 4 // 最小层级
  72. hashMinBucketCount = 1 << hashMinLevel // 最小桶数 = 16
  73. hashMaxBuckets = 1 << 22 // 桶数上限 (目录约 48MB: 12B/槽)
  74. // 分裂/合并阈值都是「平均每桶活跃条目」的上界, 由 hashSplitThreshold /
  75. // hashMergeThreshold 依据桶页容量推导, 不是单纯的绝对值。
  76. hashLoadFactorCap = 400 // 分裂阈值上限 (窄键沿用此历史值)
  77. hashMergeRatio = 4 // 合并阈值 = 分裂阈值 / 4, 与分裂阈值构成滞后防抖区间
  78. hashKeyMinLen = 1
  79. hashKeyMaxLen = 4096
  80. hashDeletedFlag = uint64(1) << 63 // pkID 最高位 = 软删除标记
  81. )
  82. // 元数据页字段偏移
  83. const (
  84. hashMetaFixedOff = page.IndexHeadSize // 32
  85. hashMetaMagicOff = hashMetaFixedOff + 0
  86. hashMetaLevelOff = hashMetaFixedOff + 4
  87. hashMetaSplitOff = hashMetaFixedOff + 8
  88. hashMetaTotalOff = hashMetaFixedOff + 12
  89. hashMetaDirOff = hashMetaFixedOff + 16
  90. hashMetaDirPagesOff = hashMetaFixedOff + 20
  91. hashMetaKeyLenOff = hashMetaFixedOff + 24
  92. hashMetaDirTailOff = hashMetaFixedOff + 28 // 目录链尾页 (原 reserved 字段)
  93. hashMetaSize = 32
  94. )
  95. // 桶页头部: 公共 PageHdr + BucketID + Count (next 用公共头 PageHdr.NextPage)
  96. const hashBucketHdrSize = int(page.PageHdrSize) + 8 // 20 + 4 + 4 = 28
  97. // 桶目录: 槽位从 PageHdrSize 开始, 每槽 12 字节 [head][tail][count]
  98. const (
  99. hashDirSlotSize = 12
  100. hashDirSlotsPerPage = (page.IndexPageSize - int(page.PageHdrSize)) / hashDirSlotSize
  101. )
  102. // 编译期布局校验: 桶页头必须恰好宽 28 字节
  103. var _ [unsafe.Sizeof(hashBucketHdr{}) - 28]struct{}
  104. var _ [28 - unsafe.Sizeof(hashBucketHdr{})]struct{}
  105. var (
  106. errHashFormatIncompatible = errors.New("hash index format incompatible: drop and recreate the index")
  107. errHashKeyLenMismatch = errors.New("hash index key length mismatch with index definition")
  108. errHashMetaInvalid = errors.New("hash index metadata invalid")
  109. errHashCorrupted = errors.New("hash index corrupted")
  110. )
  111. // hashDirSlot 一条桶溢出链在目录槽里的完整描述。
  112. // 链尾与链长和链头一起持久化: 回收整条桶链时无需再遍历链, (Head, Tail, Count)
  113. // 可直接交给 handle.FreeChain 一次拼接进空闲链。
  114. type hashDirSlot struct {
  115. Head uint32
  116. Tail uint32
  117. Count uint32
  118. }
  119. // hashBucketHdr 桶页头部 (从页缓冲区 unsafe 解析)。
  120. // next 指针不再自持, 统一用公共头 Hdr.NextPage —— 与空闲链同一字段, 便于整链回收。
  121. type hashBucketHdr struct {
  122. Hdr page.PageHdr
  123. BucketID uint32
  124. Count uint32
  125. }
  126. // hashMetaData Hash 索引元数据 (内存表示)
  127. type hashMetaData struct {
  128. Level uint32 // 层级, 桶数基数 = 1 << Level
  129. SplitPointer uint32 // 本轮已分裂桶数, 0 <= SplitPointer < 1<<Level
  130. TotalEntries uint32 // 活跃条目总数
  131. DirStartPID uint32 // 桶目录首页
  132. DirTailPID uint32 // 桶目录链尾页 (整条目录链可 O(1) 回收)
  133. DirPageCount uint32 // 桶目录页数
  134. KeyLen uint32 // 索引键长度
  135. EntryLen uint32 // 条目长度 = KeyLen + 8
  136. }
  137. // bucketCount 当前可用桶数: 2^Level + SplitPointer
  138. func (md *hashMetaData) bucketCount() uint32 {
  139. return (uint32(1) << md.Level) + md.SplitPointer
  140. }
  141. // bucketCap 单个桶页可容纳的条目数
  142. func (md *hashMetaData) bucketCap() uint32 {
  143. if md.EntryLen == 0 {
  144. return 0
  145. }
  146. return uint32(page.IndexPageSize-hashBucketHdrSize) / md.EntryLen
  147. }
  148. // hashSplitThreshold 分裂阈值: 平均每桶活跃条目超过它才分裂一个桶。
  149. //
  150. // 取 min(hashLoadFactorCap, bucketCap*3/4) 而非固定 400:
  151. // 窄键 (8B int, 单页可容 1022 条) 时 400 远未写满, 保留历史值, 分裂不激进;
  152. // 宽键时桶页容量骤降 —— 默认 string 是 256byte 列, 单页仅容 61 条, 若仍按
  153. // 400 分裂, 平均每桶要摊成 6~7 页溢出链, 等值查找退化成 O(链长), 与哈希
  154. // 索引的卖点相悖。按容量的 3/4 收敛后, 分裂发生在「单页将满」之前,
  155. // 每个桶基本停留在一页内。
  156. func hashSplitThreshold(md *hashMetaData) uint64 {
  157. cap := uint64(md.bucketCap())
  158. if cap == 0 {
  159. return hashLoadFactorCap
  160. }
  161. if t := cap * 3 / 4; t < hashLoadFactorCap {
  162. return t
  163. }
  164. return hashLoadFactorCap
  165. }
  166. // hashMergeThreshold 合并阈值: 平均每桶活跃条目低于它才撤销一个桶。
  167. // 与分裂阈值保持 1:4 的固定比值 (历史 400/100), 中间区间不做任何增删桶,
  168. // 避免在阈值附近反复分裂/合并抖动。下限取 1, 防止超宽键下退化为 0 导致
  169. // 「>= 0 恒真」而永不合并。
  170. func hashMergeThreshold(md *hashMetaData) uint64 {
  171. if t := hashSplitThreshold(md) / hashMergeRatio; t >= 1 {
  172. return t
  173. }
  174. return 1
  175. }
  176. // =============================================================================
  177. // Hash / 路由
  178. // =============================================================================
  179. // hashOf 计算索引键的 64 位哈希 (FNV-1a + murmur3 雪崩混合, 低位分布均匀)
  180. // 线性哈希取的是哈希的低位, 因此雪崩混合是必要的。
  181. func hashOf(key []byte) uint64 {
  182. const (
  183. offset64 = 14695981039346656037
  184. prime64 = 1099511628211
  185. )
  186. h := uint64(offset64)
  187. for _, b := range key {
  188. h ^= uint64(b)
  189. h *= prime64
  190. }
  191. h ^= h >> 33
  192. h *= 0xff51afd7ed558ccd
  193. h ^= h >> 33
  194. h *= 0xc4ceb9fe1a85ec53
  195. h ^= h >> 33
  196. return h
  197. }
  198. // hashBucketIdx 线性哈希路由: 返回 [0, 2^Level + split) 内的桶号
  199. func hashBucketIdx(h uint64, level, split uint32) uint32 {
  200. idx := uint32(h) & ((uint32(1) << level) - 1)
  201. if idx < split {
  202. idx = uint32(h) & ((uint32(1) << (level + 1)) - 1)
  203. }
  204. return idx
  205. }
  206. // =============================================================================
  207. // 元数据页读写
  208. // =============================================================================
  209. func readHashMeta(handle page.HandleDb, metaPID uint32) (*hashMetaData, error) {
  210. buf := make([]byte, page.IndexPageSize)
  211. if err := handle.ReadPage(metaPID, buf); err != nil {
  212. return nil, err
  213. }
  214. if binary.LittleEndian.Uint32(buf[hashMetaMagicOff:]) != hashMagic {
  215. return nil, errHashFormatIncompatible
  216. }
  217. md := &hashMetaData{
  218. Level: binary.LittleEndian.Uint32(buf[hashMetaLevelOff:]),
  219. SplitPointer: binary.LittleEndian.Uint32(buf[hashMetaSplitOff:]),
  220. TotalEntries: binary.LittleEndian.Uint32(buf[hashMetaTotalOff:]),
  221. DirStartPID: binary.LittleEndian.Uint32(buf[hashMetaDirOff:]),
  222. DirTailPID: binary.LittleEndian.Uint32(buf[hashMetaDirTailOff:]),
  223. DirPageCount: binary.LittleEndian.Uint32(buf[hashMetaDirPagesOff:]),
  224. KeyLen: binary.LittleEndian.Uint32(buf[hashMetaKeyLenOff:]),
  225. }
  226. if md.Level < hashMinLevel || md.Level > 30 ||
  227. md.KeyLen < hashKeyMinLen || md.KeyLen > hashKeyMaxLen ||
  228. md.SplitPointer >= (uint32(1)<<md.Level) {
  229. return nil, errHashMetaInvalid
  230. }
  231. md.EntryLen = md.KeyLen + 8
  232. return md, nil
  233. }
  234. func writeHashMeta(handle page.HandleDb, metaPID uint32, md *hashMetaData) error {
  235. buf := make([]byte, page.IndexPageSize)
  236. // 新分配的页可能尚未落盘, 读失败是允许的; 页头显式重建, 不依赖页内残留数据
  237. _ = handle.ReadPage(metaPID, buf)
  238. hdr := (*page.IndexHead)(unsafe.Pointer(&buf[0]))
  239. hdr.Hdr.Type = page.IndexDataIndex
  240. hdr.Hdr.PageID = metaPID
  241. hdr.Hdr.PrePage = 0
  242. hdr.Hdr.NextPage = 0
  243. hdr.Hdr.UseSize = uint32(hashMetaFixedOff + hashMetaSize)
  244. binary.LittleEndian.PutUint32(buf[hashMetaMagicOff:], hashMagic)
  245. binary.LittleEndian.PutUint32(buf[hashMetaLevelOff:], md.Level)
  246. binary.LittleEndian.PutUint32(buf[hashMetaSplitOff:], md.SplitPointer)
  247. binary.LittleEndian.PutUint32(buf[hashMetaTotalOff:], md.TotalEntries)
  248. binary.LittleEndian.PutUint32(buf[hashMetaDirOff:], md.DirStartPID)
  249. binary.LittleEndian.PutUint32(buf[hashMetaDirPagesOff:], md.DirPageCount)
  250. binary.LittleEndian.PutUint32(buf[hashMetaKeyLenOff:], md.KeyLen)
  251. binary.LittleEndian.PutUint32(buf[hashMetaDirTailOff:], md.DirTailPID)
  252. return handle.SavePage(metaPID, buf)
  253. }
  254. // initHashTable 首次初始化 Hash 表 (元数据页 + 初始桶目录)
  255. func initHashTable(handle page.HandleDb, metaPID *uint32, keyLen int) error {
  256. if keyLen < hashKeyMinLen || keyLen > hashKeyMaxLen {
  257. return errHashKeyLenMismatch
  258. }
  259. pid := handle.NewPage()
  260. md := &hashMetaData{
  261. Level: hashMinLevel,
  262. SplitPointer: 0,
  263. TotalEntries: 0,
  264. DirStartPID: 0,
  265. DirPageCount: 0,
  266. KeyLen: uint32(keyLen),
  267. EntryLen: uint32(keyLen) + 8,
  268. }
  269. if err := hashDirEnsure(handle, md, md.bucketCount()); err != nil {
  270. return err
  271. }
  272. if err := writeHashMeta(handle, pid, md); err != nil {
  273. return err
  274. }
  275. *metaPID = pid
  276. return nil
  277. }
  278. // =============================================================================
  279. // 桶目录 (槽位数组, 多页以 PageHdr.NextPage 串链)
  280. // =============================================================================
  281. func hashDirPageIndex(idx uint32) (uint32, uint32) {
  282. pp := uint32(hashDirSlotsPerPage)
  283. return idx / pp, idx % pp
  284. }
  285. // hashDirPage 返回第 pageIdx 个目录页的页ID与缓冲
  286. func hashDirPage(handle page.HandleDb, md *hashMetaData, pageIdx uint32) (uint32, []byte, error) {
  287. cur := md.DirStartPID
  288. if cur == 0 {
  289. return 0, nil, errHashMetaInvalid
  290. }
  291. for i := uint32(0); i < pageIdx; i++ {
  292. buf := make([]byte, page.IndexPageSize)
  293. if err := handle.ReadPage(cur, buf); err != nil {
  294. return 0, nil, err
  295. }
  296. cur = page.HdrOf(buf).NextPage
  297. if cur == 0 {
  298. return 0, nil, errHashMetaInvalid
  299. }
  300. }
  301. buf := make([]byte, page.IndexPageSize)
  302. if err := handle.ReadPage(cur, buf); err != nil {
  303. return 0, nil, err
  304. }
  305. return cur, buf, nil
  306. }
  307. // hashDirSlotBase 返回桶 idx 的槽在目录页缓冲区内的字节偏移
  308. func hashDirSlotBase(off uint32) int {
  309. return int(page.PageHdrSize) + int(off)*hashDirSlotSize
  310. }
  311. // hashDirGetSlot 读取桶 idx 的整条链描述 (head, tail, count)
  312. func hashDirGetSlot(handle page.HandleDb, md *hashMetaData, idx uint32) (hashDirSlot, error) {
  313. pi, off := hashDirPageIndex(idx)
  314. _, buf, err := hashDirPage(handle, md, pi)
  315. if err != nil {
  316. return hashDirSlot{}, err
  317. }
  318. base := hashDirSlotBase(off)
  319. return hashDirSlot{
  320. Head: binary.LittleEndian.Uint32(buf[base:]),
  321. Tail: binary.LittleEndian.Uint32(buf[base+4:]),
  322. Count: binary.LittleEndian.Uint32(buf[base+8:]),
  323. }, nil
  324. }
  325. // hashDirGet 只取链头 (查找路径只需要链头)
  326. func hashDirGet(handle page.HandleDb, md *hashMetaData, idx uint32) (uint32, error) {
  327. s, err := hashDirGetSlot(handle, md, idx)
  328. if err != nil {
  329. return 0, err
  330. }
  331. return s.Head, nil
  332. }
  333. // hashDirSetSlot 写回桶 idx 的整条链描述
  334. func hashDirSetSlot(handle page.HandleDb, md *hashMetaData, idx uint32, s hashDirSlot) error {
  335. pi, off := hashDirPageIndex(idx)
  336. pid, buf, err := hashDirPage(handle, md, pi)
  337. if err != nil {
  338. return err
  339. }
  340. base := hashDirSlotBase(off)
  341. binary.LittleEndian.PutUint32(buf[base:], s.Head)
  342. binary.LittleEndian.PutUint32(buf[base+4:], s.Tail)
  343. binary.LittleEndian.PutUint32(buf[base+8:], s.Count)
  344. return handle.SavePage(pid, buf)
  345. }
  346. // hashDirEnsure 保证桶目录容量 >= needSlots 个槽
  347. func hashDirEnsure(handle page.HandleDb, md *hashMetaData, needSlots uint32) error {
  348. needPages := (needSlots + uint32(hashDirSlotsPerPage) - 1) / uint32(hashDirSlotsPerPage)
  349. for md.DirPageCount < needPages {
  350. pid := handle.NewPage()
  351. buf := make([]byte, page.IndexPageSize)
  352. hdr := page.HdrOf(buf)
  353. hdr.Type = page.IndexDataIndex
  354. hdr.PageID = pid
  355. hdr.PrePage = 0
  356. hdr.NextPage = 0
  357. hdr.UseSize = page.PageHdrSize
  358. if err := handle.SavePage(pid, buf); err != nil {
  359. return err
  360. }
  361. if md.DirStartPID == 0 {
  362. md.DirStartPID = pid
  363. } else {
  364. // O(1) 接到目录链尾: 尾页 ID 就存在元数据里, 不必从头遍历
  365. tail := md.DirTailPID
  366. if tail == 0 {
  367. // 兜底: 元数据缺链尾记录时退回遍历定位
  368. t, _, err := hashDirPage(handle, md, md.DirPageCount-1)
  369. if err != nil {
  370. return err
  371. }
  372. tail = t
  373. }
  374. lastBuf := make([]byte, page.IndexPageSize)
  375. if err := handle.ReadPage(tail, lastBuf); err != nil {
  376. return err
  377. }
  378. page.HdrOf(lastBuf).NextPage = pid
  379. if err := handle.SavePage(tail, lastBuf); err != nil {
  380. return err
  381. }
  382. }
  383. md.DirTailPID = pid
  384. md.DirPageCount++
  385. }
  386. return nil
  387. }
  388. // =============================================================================
  389. // 桶页
  390. // =============================================================================
  391. func hashReadBucketPage(handle page.HandleDb, pid uint32) (*hashBucketHdr, []byte, error) {
  392. buf := make([]byte, page.IndexPageSize)
  393. if err := handle.ReadPage(pid, buf); err != nil {
  394. return nil, nil, err
  395. }
  396. return (*hashBucketHdr)(unsafe.Pointer(&buf[0])), buf, nil
  397. }
  398. // hashInitBucketPage 按空桶页初始化页头 (条目区从 hashBucketHdrSize 开始)。
  399. // next 写在公共头 PageHdr.NextPage 上 —— 与空闲链同字段, 因此整条桶链可直接
  400. // 交给 FreeChain 拼接进空闲链, 回收时无需逐页改写。
  401. func hashInitBucketPage(buf []byte, pid, bucketID, next uint32) {
  402. hdr := (*hashBucketHdr)(unsafe.Pointer(&buf[0]))
  403. hdr.Hdr.Type = page.IndexDataIndex
  404. hdr.Hdr.PageID = pid
  405. hdr.Hdr.PrePage = 0
  406. hdr.Hdr.NextPage = next
  407. hdr.Hdr.UseSize = uint32(hashBucketHdrSize)
  408. hdr.BucketID = bucketID
  409. hdr.Count = 0
  410. }
  411. // hashBucketSlots 安全计算桶页物理条目数 (UseSize 损坏时返回 0)
  412. func hashBucketSlots(hdr *hashBucketHdr, entryLen uint32) uint32 {
  413. us := hdr.Hdr.UseSize
  414. if us < uint32(hashBucketHdrSize) || us > page.IndexPageSize || entryLen == 0 {
  415. return 0
  416. }
  417. n := (us - uint32(hashBucketHdrSize)) / entryLen
  418. limit := (page.IndexPageSize - uint32(hashBucketHdrSize)) / entryLen
  419. if n > limit {
  420. n = limit
  421. }
  422. return n
  423. }
  424. // hashGatherBucket 遍历桶链: 收集所有活跃条目(平铺, 每条 EntryLen 字节)与全部页ID
  425. // 页ID列表用于后续复用这些页, 因此空页也会被收集进来 (保持链的可达性)
  426. func hashGatherBucket(handle page.HandleDb, head uint32, entryLen uint32) (flat []byte, count int, pages []uint32, err error) {
  427. cur := head
  428. for cur != 0 {
  429. hdr, buf, rerr := hashReadBucketPage(handle, cur)
  430. if rerr != nil {
  431. return nil, 0, nil, rerr
  432. }
  433. pages = append(pages, cur)
  434. n := hashBucketSlots(hdr, entryLen)
  435. for i := uint32(0); i < n; i++ {
  436. off := hashBucketHdrSize + int(i*entryLen)
  437. ent := buf[off : off+int(entryLen)]
  438. if binary.BigEndian.Uint64(ent[int(entryLen)-8:])&hashDeletedFlag != 0 {
  439. continue
  440. }
  441. flat = append(flat, ent...)
  442. count++
  443. }
  444. cur = hdr.Hdr.NextPage
  445. }
  446. return flat, count, pages, nil
  447. }
  448. // hashWriteChain 用页池 pool 为 entries 构建桶链, 返回链描述 (head, tail, 链长)
  449. // 与未用完的页。pool 中的页仍处于已分配状态, 页数不够时 NewPage 不会返回它们。
  450. func hashWriteChain(handle page.HandleDb, bucketID uint32, flat []byte, count int,
  451. md *hashMetaData, pool []uint32) (head, tail uint32, chain int, leftover []uint32, err error) {
  452. capPerPage := int(md.bucketCap())
  453. if capPerPage <= 0 {
  454. return 0, 0, 0, nil, errHashMetaInvalid
  455. }
  456. if count == 0 {
  457. return 0, 0, 0, pool, nil
  458. }
  459. numPages := (count + capPerPage - 1) / capPerPage
  460. ids := make([]uint32, numPages)
  461. for i := 0; i < numPages; i++ {
  462. if i < len(pool) {
  463. ids[i] = pool[i]
  464. } else {
  465. ids[i] = handle.NewPage()
  466. }
  467. }
  468. if numPages <= len(pool) {
  469. leftover = pool[numPages:]
  470. }
  471. entryLen := int(md.EntryLen)
  472. for i := 0; i < numPages; i++ {
  473. var next uint32
  474. if i+1 < numPages {
  475. next = ids[i+1]
  476. }
  477. buf := make([]byte, page.IndexPageSize)
  478. hashInitBucketPage(buf, ids[i], bucketID, next)
  479. hdr := (*hashBucketHdr)(unsafe.Pointer(&buf[0]))
  480. start := i * capPerPage
  481. n := count - start
  482. if n > capPerPage {
  483. n = capPerPage
  484. }
  485. off := hashBucketHdrSize
  486. for j := 0; j < n; j++ {
  487. src := (start + j) * entryLen
  488. copy(buf[off:off+entryLen], flat[src:src+entryLen])
  489. off += entryLen
  490. }
  491. hdr.Count = uint32(n)
  492. hdr.Hdr.UseSize = uint32(off)
  493. if err := handle.SavePage(ids[i], buf); err != nil {
  494. return 0, 0, 0, nil, err
  495. }
  496. }
  497. return ids[0], ids[numPages-1], numPages, leftover, nil
  498. }
  499. // hashLinkEmptyPages 把 pages 接到链 (head, tail, chainCount) 的尾部, 返回新的链描述。
  500. // 有了链尾就无需遍历找尾; 空链 (head==0) 时用 pages 自建一条链。
  501. func hashLinkEmptyPages(handle page.HandleDb, head, tail, chainCount, bucketID uint32,
  502. pages []uint32) (hashDirSlot, error) {
  503. if len(pages) == 0 {
  504. return hashDirSlot{Head: head, Tail: tail, Count: chainCount}, nil
  505. }
  506. for i, pid := range pages {
  507. var next uint32
  508. if i+1 < len(pages) {
  509. next = pages[i+1]
  510. }
  511. buf := make([]byte, page.IndexPageSize)
  512. hashInitBucketPage(buf, pid, bucketID, next)
  513. if err := handle.SavePage(pid, buf); err != nil {
  514. return hashDirSlot{}, err
  515. }
  516. }
  517. if head == 0 {
  518. return hashDirSlot{Head: pages[0], Tail: pages[len(pages)-1], Count: uint32(len(pages))}, nil
  519. }
  520. lastBuf := make([]byte, page.IndexPageSize)
  521. if err := handle.ReadPage(tail, lastBuf); err != nil {
  522. return hashDirSlot{}, err
  523. }
  524. page.HdrOf(lastBuf).NextPage = pages[0]
  525. if err := handle.SavePage(tail, lastBuf); err != nil {
  526. return hashDirSlot{}, err
  527. }
  528. return hashDirSlot{Head: head, Tail: pages[len(pages)-1], Count: chainCount + uint32(len(pages))}, nil
  529. }
  530. // hashScatter 按桶号重建若干条桶链: 复用 pool 中的页, 多余的页挂到最小桶号的链尾
  531. // 分裂、合并、压缩都走这一条路径, 因此不会产生孤儿页
  532. func hashScatter(handle page.HandleDb, md *hashMetaData, groups map[uint32][]byte, pool []uint32) error {
  533. if len(groups) == 0 {
  534. return nil
  535. }
  536. order := make([]uint32, 0, len(groups))
  537. for k := range groups {
  538. order = append(order, k)
  539. }
  540. sort.Slice(order, func(i, j int) bool { return order[i] < order[j] })
  541. leftover := pool
  542. for _, bid := range order {
  543. flat := groups[bid]
  544. head, tail, chain, lo, err := hashWriteChain(handle, bid, flat, len(flat)/int(md.EntryLen), md, leftover)
  545. if err != nil {
  546. return err
  547. }
  548. leftover = lo
  549. if err := hashDirSetSlot(handle, md, bid, hashDirSlot{Head: head, Tail: tail, Count: uint32(chain)}); err != nil {
  550. return err
  551. }
  552. }
  553. if len(leftover) > 0 {
  554. bid := order[0]
  555. s, err := hashDirGetSlot(handle, md, bid)
  556. if err != nil {
  557. return err
  558. }
  559. ns, err := hashLinkEmptyPages(handle, s.Head, s.Tail, s.Count, bid, leftover)
  560. if err != nil {
  561. return err
  562. }
  563. if err := hashDirSetSlot(handle, md, bid, ns); err != nil {
  564. return err
  565. }
  566. }
  567. return nil
  568. }
  569. // hashAppendEntry 向桶 idx 追加一个条目, 并同步该桶的链描述。
  570. // 有链尾后不必再从头找空隙: 尾页未满就地追加, 满了才新建页接到链尾。
  571. func hashAppendEntry(handle page.HandleDb, md *hashMetaData, idx uint32, entry []byte) error {
  572. s, err := hashDirGetSlot(handle, md, idx)
  573. if err != nil {
  574. return err
  575. }
  576. if s.Head == 0 {
  577. pid := handle.NewPage()
  578. buf := make([]byte, page.IndexPageSize)
  579. hashInitBucketPage(buf, pid, idx, 0)
  580. copy(buf[hashBucketHdrSize:], entry)
  581. hdr := (*hashBucketHdr)(unsafe.Pointer(&buf[0]))
  582. hdr.Count = 1
  583. hdr.Hdr.UseSize = uint32(hashBucketHdrSize) + md.EntryLen
  584. if err := handle.SavePage(pid, buf); err != nil {
  585. return err
  586. }
  587. return hashDirSetSlot(handle, md, idx, hashDirSlot{Head: pid, Tail: pid, Count: 1})
  588. }
  589. tailBuf := make([]byte, page.IndexPageSize)
  590. if err := handle.ReadPage(s.Tail, tailBuf); err != nil {
  591. return err
  592. }
  593. tailHdr := (*hashBucketHdr)(unsafe.Pointer(&tailBuf[0]))
  594. if tailHdr.Hdr.UseSize+md.EntryLen <= page.IndexPageSize {
  595. copy(tailBuf[int(tailHdr.Hdr.UseSize):], entry)
  596. tailHdr.Count++
  597. tailHdr.Hdr.UseSize += md.EntryLen
  598. return handle.SavePage(s.Tail, tailBuf)
  599. }
  600. pid := handle.NewPage()
  601. buf := make([]byte, page.IndexPageSize)
  602. hashInitBucketPage(buf, pid, idx, 0)
  603. copy(buf[hashBucketHdrSize:], entry)
  604. hdr := (*hashBucketHdr)(unsafe.Pointer(&buf[0]))
  605. hdr.Count = 1
  606. hdr.Hdr.UseSize = uint32(hashBucketHdrSize) + md.EntryLen
  607. if err := handle.SavePage(pid, buf); err != nil {
  608. return err
  609. }
  610. tailHdr.Hdr.NextPage = pid
  611. if err := handle.SavePage(s.Tail, tailBuf); err != nil {
  612. return err
  613. }
  614. return hashDirSetSlot(handle, md, idx, hashDirSlot{Head: s.Head, Tail: pid, Count: s.Count + 1})
  615. }
  616. // hashFindEntry 返回桶链中第一个 key 匹配且未删除的 pkID
  617. func hashFindEntry(handle page.HandleDb, head uint32, md *hashMetaData, key []byte) (uint64, bool) {
  618. entryLen := md.EntryLen
  619. keyLen := int(md.KeyLen)
  620. cur := head
  621. for cur != 0 {
  622. hdr, buf, err := hashReadBucketPage(handle, cur)
  623. if err != nil {
  624. return 0, false
  625. }
  626. n := hashBucketSlots(hdr, entryLen)
  627. for i := uint32(0); i < n; i++ {
  628. off := hashBucketHdrSize + int(i*entryLen)
  629. ent := buf[off : off+int(entryLen)]
  630. pk := binary.BigEndian.Uint64(ent[int(entryLen)-8:])
  631. if pk&hashDeletedFlag != 0 {
  632. continue
  633. }
  634. if bytes.Equal(ent[:keyLen], key) {
  635. return pk, true
  636. }
  637. }
  638. cur = hdr.Hdr.NextPage
  639. }
  640. return 0, false
  641. }
  642. // hashCollect 遍历桶链, 对活跃且 match 命中的条目调用 emit
  643. func hashCollect(handle page.HandleDb, head uint32, md *hashMetaData,
  644. match func(key []byte, pkID uint64) bool,
  645. emit func(key []byte, pkID uint64)) error {
  646. entryLen := md.EntryLen
  647. keyLen := int(md.KeyLen)
  648. cur := head
  649. for cur != 0 {
  650. hdr, buf, err := hashReadBucketPage(handle, cur)
  651. if err != nil {
  652. return err
  653. }
  654. n := hashBucketSlots(hdr, entryLen)
  655. for i := uint32(0); i < n; i++ {
  656. off := hashBucketHdrSize + int(i*entryLen)
  657. ent := buf[off : off+int(entryLen)]
  658. pk := binary.BigEndian.Uint64(ent[int(entryLen)-8:])
  659. if pk&hashDeletedFlag != 0 {
  660. continue
  661. }
  662. if match(ent[:keyLen], pk) {
  663. emit(ent[:keyLen], pk)
  664. }
  665. }
  666. cur = hdr.Hdr.NextPage
  667. }
  668. return nil
  669. }
  670. // hashDeleteMatch 软删除桶链中第一个 match 命中的活跃条目
  671. // 同时统计整条链的物理条目数与活跃条目数 (用于判断是否需要压缩)
  672. func hashDeleteMatch(handle page.HandleDb, head uint32, md *hashMetaData,
  673. match func(key []byte, pk uint64) bool) (deleted bool, physical, active int, err error) {
  674. entryLen := md.EntryLen
  675. keyLen := int(md.KeyLen)
  676. cur := head
  677. for cur != 0 {
  678. hdr, buf, rerr := hashReadBucketPage(handle, cur)
  679. if rerr != nil {
  680. return false, 0, 0, rerr
  681. }
  682. n := hashBucketSlots(hdr, entryLen)
  683. marked := false
  684. for i := uint32(0); i < n; i++ {
  685. off := hashBucketHdrSize + int(i*entryLen)
  686. ent := buf[off : off+int(entryLen)]
  687. pkOff := int(entryLen) - 8
  688. pk := binary.BigEndian.Uint64(ent[pkOff:])
  689. physical++
  690. if pk&hashDeletedFlag != 0 {
  691. continue
  692. }
  693. active++
  694. if !deleted && match(ent[:keyLen], pk) {
  695. binary.BigEndian.PutUint64(ent[pkOff:], pk|hashDeletedFlag)
  696. if hdr.Count > 0 {
  697. hdr.Count--
  698. }
  699. deleted = true
  700. marked = true
  701. }
  702. }
  703. if marked {
  704. if err := handle.SavePage(cur, buf); err != nil {
  705. return deleted, physical, active, err
  706. }
  707. }
  708. cur = hdr.Hdr.NextPage
  709. }
  710. return deleted, physical, active, nil
  711. }
  712. // =============================================================================
  713. // 分裂 / 合并 / 压缩
  714. // =============================================================================
  715. // trySplit 按全局负载因子判断, 分裂 splitPointer 指向的桶 (增量, 每次最多一个)
  716. func (h *HashHandler) trySplit(handle page.HandleDb, metaPID uint32, md *hashMetaData) error {
  717. if md.bucketCount() >= hashMaxBuckets {
  718. return nil
  719. }
  720. if uint64(md.TotalEntries) <= uint64(md.bucketCount())*hashSplitThreshold(md) {
  721. return nil
  722. }
  723. src := md.SplitPointer
  724. newIdx := (uint32(1) << md.Level) + src
  725. srcHead, err := hashDirGet(handle, md, src)
  726. if err != nil {
  727. return err
  728. }
  729. flat, _, pool, err := hashGatherBucket(handle, srcHead, md.EntryLen)
  730. if err != nil {
  731. return err
  732. }
  733. // 先推进指针, 之后按新参数重新路由
  734. md.SplitPointer++
  735. if md.SplitPointer == (uint32(1) << md.Level) {
  736. md.Level++
  737. md.SplitPointer = 0
  738. }
  739. if err := hashDirEnsure(handle, md, md.bucketCount()); err != nil {
  740. return err
  741. }
  742. groups := map[uint32][]byte{src: nil, newIdx: nil}
  743. entryLen := int(md.EntryLen)
  744. for off := 0; off+entryLen <= len(flat); off += entryLen {
  745. ent := flat[off : off+entryLen]
  746. idx := hashBucketIdx(hashOf(ent[:md.KeyLen]), md.Level, md.SplitPointer)
  747. if idx != src && idx != newIdx {
  748. // 不变式被破坏: 该桶的条目只可能落到这两个桶
  749. return errHashCorrupted
  750. }
  751. groups[idx] = append(groups[idx], ent...)
  752. }
  753. if err := hashScatter(handle, md, groups, pool); err != nil {
  754. return err
  755. }
  756. return writeHashMeta(handle, metaPID, md)
  757. }
  758. // tryMerge 按全局负载因子判断, 撤销最后一个桶并并入其伙伴桶 (每次最多一个)
  759. func (h *HashHandler) tryMerge(handle page.HandleDb, metaPID uint32, md *hashMetaData) error {
  760. if md.bucketCount() <= hashMinBucketCount {
  761. return nil
  762. }
  763. if uint64(md.TotalEntries) >= uint64(md.bucketCount())*hashMergeThreshold(md) {
  764. return nil
  765. }
  766. // 回退指针
  767. if md.SplitPointer > 0 {
  768. md.SplitPointer--
  769. } else {
  770. md.Level--
  771. md.SplitPointer = (uint32(1) << md.Level) - 1
  772. }
  773. partner := md.SplitPointer
  774. src := (uint32(1) << md.Level) + partner
  775. pHead, err := hashDirGet(handle, md, partner)
  776. if err != nil {
  777. return err
  778. }
  779. sHead, err := hashDirGet(handle, md, src)
  780. if err != nil {
  781. return err
  782. }
  783. pFlat, _, pPool, err := hashGatherBucket(handle, pHead, md.EntryLen)
  784. if err != nil {
  785. return err
  786. }
  787. sFlat, _, sPool, err := hashGatherBucket(handle, sHead, md.EntryLen)
  788. if err != nil {
  789. return err
  790. }
  791. pool := append(append(make([]uint32, 0, len(pPool)+len(sPool)), pPool...), sPool...)
  792. groups := map[uint32][]byte{src: nil, partner: nil}
  793. entryLen := int(md.EntryLen)
  794. combined := append(append(make([]byte, 0, len(pFlat)+len(sFlat)), pFlat...), sFlat...)
  795. for off := 0; off+entryLen <= len(combined); off += entryLen {
  796. ent := combined[off : off+entryLen]
  797. idx := hashBucketIdx(hashOf(ent[:md.KeyLen]), md.Level, md.SplitPointer)
  798. if idx != partner {
  799. return errHashCorrupted
  800. }
  801. groups[partner] = append(groups[partner], ent...)
  802. }
  803. if err := hashScatter(handle, md, groups, pool); err != nil {
  804. return err
  805. }
  806. return writeHashMeta(handle, metaPID, md)
  807. }
  808. // compactBucket 就地重写某个桶链 (复用原页), 控制软删除墓碑的堆积
  809. func (h *HashHandler) compactBucket(handle page.HandleDb, md *hashMetaData, idx, head uint32) error {
  810. flat, _, pool, err := hashGatherBucket(handle, head, md.EntryLen)
  811. if err != nil {
  812. return err
  813. }
  814. return hashScatter(handle, md, map[uint32][]byte{idx: flat}, pool)
  815. }
  816. // =============================================================================
  817. // HashHandler — 实现 IndexHandler 接口
  818. // =============================================================================
  819. // HashHandler 线性哈希二级索引 (IndexTypeHash)
  820. // 条目 = [key(keyLen) + pkID(8B BE)], 软删除用 pkID 最高位标记
  821. type HashHandler struct{}
  822. func init() {
  823. Compose.RegisterHandler(page.IndexTypeHash, &HashHandler{})
  824. }
  825. func (h *HashHandler) Insert(handle page.HandleDb, entryPage *uint32, indexKey []byte, pkID uint64, rowData []byte, keyLen int) (uint32, error) {
  826. if *entryPage == 0 {
  827. if err := initHashTable(handle, entryPage, keyLen); err != nil {
  828. return 0, err
  829. }
  830. }
  831. md, err := readHashMeta(handle, *entryPage)
  832. if err != nil {
  833. return 0, err
  834. }
  835. if uint32(keyLen) != md.KeyLen || len(indexKey) != int(md.KeyLen) {
  836. return 0, errHashKeyLenMismatch
  837. }
  838. idx := hashBucketIdx(hashOf(indexKey), md.Level, md.SplitPointer)
  839. entry := make([]byte, md.EntryLen)
  840. copy(entry, indexKey)
  841. binary.BigEndian.PutUint64(entry[int(md.EntryLen)-8:], pkID&^hashDeletedFlag)
  842. if err := hashAppendEntry(handle, md, idx, entry); err != nil {
  843. return 0, err
  844. }
  845. md.TotalEntries++
  846. if err := writeHashMeta(handle, *entryPage, md); err != nil {
  847. return 0, err
  848. }
  849. // 分裂错误必须上抛, 否则会留下半迁移状态却报告成功
  850. if err := h.trySplit(handle, *entryPage, md); err != nil {
  851. return 0, err
  852. }
  853. return *entryPage, nil
  854. }
  855. func (h *HashHandler) Search(handle page.HandleDb, entryPage uint32, indexKey []byte, keyLen int, resolve RowResolver) ([]byte, bool) {
  856. if entryPage == 0 {
  857. return nil, false
  858. }
  859. md, err := readHashMeta(handle, entryPage)
  860. if err != nil {
  861. return nil, false
  862. }
  863. if uint32(keyLen) != md.KeyLen || len(indexKey) != int(md.KeyLen) {
  864. return nil, false
  865. }
  866. idx := hashBucketIdx(hashOf(indexKey), md.Level, md.SplitPointer)
  867. head, err := hashDirGet(handle, md, idx)
  868. if err != nil || head == 0 {
  869. return nil, false
  870. }
  871. pk, found := hashFindEntry(handle, head, md, indexKey)
  872. if !found {
  873. return nil, false
  874. }
  875. // 二级索引不存行数据: 回表由索引自己完成
  876. if resolve != nil {
  877. if row, ok := resolve(pk); ok {
  878. return row, true
  879. }
  880. return nil, false
  881. }
  882. buf := make([]byte, 8)
  883. binary.BigEndian.PutUint64(buf, pk)
  884. return buf, true
  885. }
  886. // SearchAll 返回该 key 的全部 pkID (二级索引允许多行同 key)
  887. // 说明: 不在 IndexHandler 接口内, 作为 hash 索引的能力扩展提供给上层按需断言
  888. func (h *HashHandler) SearchAll(handle page.HandleDb, entryPage uint32, indexKey []byte, keyLen int) ([]uint64, bool) {
  889. if entryPage == 0 {
  890. return nil, false
  891. }
  892. md, err := readHashMeta(handle, entryPage)
  893. if err != nil {
  894. return nil, false
  895. }
  896. if uint32(keyLen) != md.KeyLen || len(indexKey) != int(md.KeyLen) {
  897. return nil, false
  898. }
  899. idx := hashBucketIdx(hashOf(indexKey), md.Level, md.SplitPointer)
  900. head, err := hashDirGet(handle, md, idx)
  901. if err != nil || head == 0 {
  902. return nil, false
  903. }
  904. var out []uint64
  905. err = hashCollect(handle, head, md,
  906. func(k []byte, _ uint64) bool { return bytes.Equal(k, indexKey) },
  907. func(_ []byte, pk uint64) { out = append(out, pk) })
  908. if err != nil || len(out) == 0 {
  909. return nil, false
  910. }
  911. return out, true
  912. }
  913. // deleteWith 在匹配 key 的桶链上软删除第一个 match 命中的条目, 并完成
  914. // 计数维护 / 墓碑压缩 / 桶合并 等收尾工作
  915. func (h *HashHandler) deleteWith(handle page.HandleDb, entryPage *uint32, indexKey []byte, keyLen int,
  916. match func(key []byte, pk uint64) bool) error {
  917. if *entryPage == 0 {
  918. return page.ErrNotFound
  919. }
  920. md, err := readHashMeta(handle, *entryPage)
  921. if err != nil {
  922. return err
  923. }
  924. if uint32(keyLen) != md.KeyLen || len(indexKey) != int(md.KeyLen) {
  925. return page.ErrNotFound
  926. }
  927. idx := hashBucketIdx(hashOf(indexKey), md.Level, md.SplitPointer)
  928. head, err := hashDirGet(handle, md, idx)
  929. if err != nil {
  930. return err
  931. }
  932. if head == 0 {
  933. return page.ErrNotFound
  934. }
  935. deleted, physical, active, err := hashDeleteMatch(handle, head, md, match)
  936. if err != nil {
  937. return err
  938. }
  939. if !deleted {
  940. return page.ErrNotFound
  941. }
  942. if md.TotalEntries > 0 {
  943. md.TotalEntries--
  944. }
  945. // 墓碑占多数且超过一页容量时就地压缩
  946. if active*2 < physical && physical > int(md.bucketCap()) {
  947. if err := h.compactBucket(handle, md, idx, head); err != nil {
  948. return err
  949. }
  950. }
  951. if err := writeHashMeta(handle, *entryPage, md); err != nil {
  952. return err
  953. }
  954. if err := h.tryMerge(handle, *entryPage, md); err != nil {
  955. return err
  956. }
  957. return nil
  958. }
  959. func (h *HashHandler) Delete(handle page.HandleDb, entryPage *uint32, indexKey []byte, pkID uint64, keyLen int) error {
  960. // 二级索引允许重复 key, 用 (key, pkID) 精确定位本行的条目; pkID 未知(为 0)时按 key 删第一个
  961. return h.deleteWith(handle, entryPage, indexKey, keyLen,
  962. func(k []byte, pk uint64) bool {
  963. return bytes.Equal(k, indexKey) && (pkID == 0 || pk == pkID)
  964. })
  965. }
  966. func (h *HashHandler) Update(handle page.HandleDb, entryPage *uint32, oldKey, newKey, rowData []byte, pkID uint64, keyLen int) error {
  967. // 二级索引条目只存 [key + pkID]: 键未变则无事可做, 键变了则删旧插新
  968. if bytes.Equal(oldKey, newKey) {
  969. return nil
  970. }
  971. if oldKey != nil {
  972. if err := h.Delete(handle, entryPage, oldKey, pkID, len(oldKey)); err != nil && err != page.ErrNotFound {
  973. return err
  974. }
  975. }
  976. _, err := h.Insert(handle, entryPage, newKey, pkID, rowData, len(newKey))
  977. return err
  978. }
  979. // IsRowStore Hash 二级索引不承载行数据
  980. func (h *HashHandler) IsRowStore() bool { return false }
  981. // ResolveRow Hash 二级索引无法按 pkID 取行, 由调用方的回表函数负责
  982. func (h *HashHandler) ResolveRow(handle page.HandleDb, entryPage uint32, pkID uint64, cache *[]DPRange) ([]byte, bool) {
  983. return nil, false
  984. }
  985. func (h *HashHandler) GetDataPageIDs(handle page.HandleDb, entryPage uint32) []uint32 {
  986. // Hash 二级索引没有数据页 (叶层直接存 pkID)
  987. return nil
  988. }
  989. func (h *HashHandler) ReadDataPageRows(handle page.HandleDb, dpID uint32) [][]byte {
  990. return nil
  991. }
  992. // CollectPageIDs 实现 index.PageCollector: 枚举该 Hash 索引占用的全部页 ——
  993. // 元数据页 + 桶目录页链 + 每个桶的溢出链。
  994. //
  995. // 供索引整体被换根后归还旧结构。上层无法自己做这件事: 哈希的三种页 (元数据/
  996. // 目录/桶) 头部各不相同, 按 B+ 节点的 IndexHead 去解析会把 BucketID/NextPage
  997. // 当成 EntryLen/IsLeaf, 遍历出一串垃圾页号 —— 而那些页号很可能仍然可达, 还进
  998. // 空闲链表就是静默数据损坏。
  999. //
  1000. // 任何一步解析失败都立即停手并只返回已确认的部分 (宁可泄漏): 例如元数据魔数
  1001. // 对不上 (旧版本布局 / 页损坏) 时直接返回 nil, 连元数据页都不还。
  1002. func (h *HashHandler) CollectPageIDs(handle page.HandleDb, entryPage uint32) []uint32 {
  1003. if entryPage == 0 {
  1004. return nil
  1005. }
  1006. md, err := readHashMeta(handle, entryPage)
  1007. if err != nil {
  1008. return nil
  1009. }
  1010. var out []uint32
  1011. seen := make(map[uint32]bool)
  1012. add := func(pid uint32) bool {
  1013. if pid == 0 || seen[pid] {
  1014. return false
  1015. }
  1016. seen[pid] = true
  1017. out = append(out, pid)
  1018. return true
  1019. }
  1020. add(entryPage)
  1021. // 同一桶的溢出页统一用公共头 PageHdr.NextPage 串链
  1022. walkBucket := func(head uint32) {
  1023. for add(head) {
  1024. hdr, _, rerr := hashReadBucketPage(handle, head)
  1025. if rerr != nil {
  1026. return
  1027. }
  1028. head = hdr.Hdr.NextPage
  1029. }
  1030. }
  1031. // 目录页之间用 PageHdr.NextPage 串链; 顺带把各槽位指向的桶链走完。
  1032. // 桶数之外的槽位是 0 (目录页按零值初始化), 会被 add 跳过。
  1033. dir := md.DirStartPID
  1034. for dir != 0 && add(dir) {
  1035. buf := make([]byte, page.IndexPageSize)
  1036. if err := handle.ReadPage(dir, buf); err != nil {
  1037. break
  1038. }
  1039. for slot := 0; slot < hashDirSlotsPerPage; slot++ {
  1040. off := int(page.PageHdrSize) + slot*hashDirSlotSize
  1041. if off+hashDirSlotSize > len(buf) {
  1042. break
  1043. }
  1044. walkBucket(binary.LittleEndian.Uint32(buf[off:]))
  1045. }
  1046. dir = page.HdrOf(buf).NextPage
  1047. }
  1048. return out
  1049. }
  1050. // CollectChains 实现 index.ChainCollector: 以「整链」而非「逐页」汇报该 Hash 索引
  1051. // 占用的全部页 —— 一条链 = (head, tail, 链长)。回收方对每条链只需一次 FreeChain
  1052. // (O(1) 拼接进空闲链), 完全不必读链内任何一页。
  1053. //
  1054. // 之所以能这么做: 桶页的 next 已经统一到公共头 PageHdr.NextPage, 与空闲链同一
  1055. // 字段; 且每条桶链的 (head, tail, count) 都持久化在目录槽里。因此本方法只读
  1056. // 元数据页 + 桶目录页, 成本与桶链长度无关。
  1057. func (h *HashHandler) CollectChains(handle page.HandleDb, entryPage uint32) []PageChain {
  1058. if entryPage == 0 {
  1059. return nil
  1060. }
  1061. md, err := readHashMeta(handle, entryPage)
  1062. if err != nil {
  1063. return nil
  1064. }
  1065. out := make([]PageChain, 0, 8)
  1066. seen := make(map[uint32]bool)
  1067. addChain := func(head, tail, count uint32) {
  1068. if head == 0 || count == 0 {
  1069. return
  1070. }
  1071. if seen[head] {
  1072. return
  1073. }
  1074. seen[head] = true
  1075. if tail == 0 {
  1076. tail = head
  1077. }
  1078. out = append(out, PageChain{Head: head, Tail: tail, Count: count})
  1079. }
  1080. // 元数据页: 单页链
  1081. addChain(entryPage, entryPage, 1)
  1082. // 桶目录页链 + 每条桶溢出链 (链头/链尾/链长全部来自目录槽, 不读桶页)
  1083. if md.DirStartPID != 0 && md.DirPageCount > 0 {
  1084. dirTail := md.DirTailPID
  1085. if dirTail == 0 {
  1086. dirTail = md.DirStartPID
  1087. }
  1088. addChain(md.DirStartPID, dirTail, md.DirPageCount)
  1089. dir := md.DirStartPID
  1090. for dir != 0 {
  1091. buf := make([]byte, page.IndexPageSize)
  1092. if err := handle.ReadPage(dir, buf); err != nil {
  1093. break
  1094. }
  1095. for slot := 0; slot < hashDirSlotsPerPage; slot++ {
  1096. off := int(page.PageHdrSize) + slot*hashDirSlotSize
  1097. if off+hashDirSlotSize > len(buf) {
  1098. break
  1099. }
  1100. addChain(binary.LittleEndian.Uint32(buf[off:]),
  1101. binary.LittleEndian.Uint32(buf[off+4:]),
  1102. binary.LittleEndian.Uint32(buf[off+8:]))
  1103. }
  1104. next := page.HdrOf(buf).NextPage
  1105. if next == md.DirStartPID {
  1106. break
  1107. }
  1108. dir = next
  1109. }
  1110. }
  1111. return out
  1112. }
  1113. // RangeScan 范围扫描
  1114. // 哈希不保序, 但条目内保存了原始 key, 因此这里做:
  1115. // - 等值条件: 直接定位到桶 (O(链长))
  1116. // - 其它条件: 扫描全部桶 + 键边界过滤, 再按 key 排序 (保证 ORDER BY 语义正确)
  1117. //
  1118. // 每个回调参数为 8 字节 BigEndian 的 pkID (二级索引契约)
  1119. func (h *HashHandler) RangeScan(handle page.HandleDb, entryPage uint32,
  1120. startKey, endKey []byte, startInclusive, endInclusive bool, reverse bool,
  1121. callback func(row []byte) bool) error {
  1122. if entryPage == 0 {
  1123. return nil
  1124. }
  1125. md, err := readHashMeta(handle, entryPage)
  1126. if err != nil {
  1127. return err
  1128. }
  1129. inRange := func(k []byte) bool {
  1130. if startKey != nil {
  1131. c := bytes.Compare(k, startKey)
  1132. if c < 0 || (c == 0 && !startInclusive) {
  1133. return false
  1134. }
  1135. }
  1136. if endKey != nil {
  1137. c := bytes.Compare(k, endKey)
  1138. if c > 0 || (c == 0 && !endInclusive) {
  1139. return false
  1140. }
  1141. }
  1142. return true
  1143. }
  1144. type kvPair struct {
  1145. key []byte
  1146. pk uint64
  1147. }
  1148. var pairs []kvPair
  1149. collect := func(head uint32) error {
  1150. return hashCollect(handle, head, md,
  1151. func(k []byte, _ uint64) bool { return inRange(k) },
  1152. func(k []byte, pk uint64) {
  1153. pairs = append(pairs, kvPair{key: append([]byte(nil), k...), pk: pk})
  1154. })
  1155. }
  1156. equality := startKey != nil && endKey != nil && startInclusive && endInclusive &&
  1157. bytes.Equal(startKey, endKey)
  1158. if equality {
  1159. idx := hashBucketIdx(hashOf(startKey), md.Level, md.SplitPointer)
  1160. head, gerr := hashDirGet(handle, md, idx)
  1161. if gerr != nil {
  1162. return gerr
  1163. }
  1164. if head != 0 {
  1165. if cerr := collect(head); cerr != nil {
  1166. return cerr
  1167. }
  1168. }
  1169. } else {
  1170. n := md.bucketCount()
  1171. for i := uint32(0); i < n; i++ {
  1172. head, gerr := hashDirGet(handle, md, i)
  1173. if gerr != nil {
  1174. return gerr
  1175. }
  1176. if head == 0 {
  1177. continue
  1178. }
  1179. if cerr := collect(head); cerr != nil {
  1180. return cerr
  1181. }
  1182. }
  1183. sort.Slice(pairs, func(i, j int) bool {
  1184. c := bytes.Compare(pairs[i].key, pairs[j].key)
  1185. if reverse {
  1186. return c > 0
  1187. }
  1188. return c < 0
  1189. })
  1190. }
  1191. buf := make([]byte, 8)
  1192. for _, p := range pairs {
  1193. binary.BigEndian.PutUint64(buf, p.pk)
  1194. if !callback(buf) {
  1195. return nil
  1196. }
  1197. }
  1198. return nil
  1199. }