datapage.go 3.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
  1. package page
  2. import (
  3. "encoding/binary"
  4. "unsafe"
  5. )
  6. // =============================================================================
  7. // DataPageHdr —— 数据页头: 公共页头 + 行计数 + pkID 极值 + 存活/失效统计。
  8. //
  9. // 磁盘布局 (48B):
  10. //
  11. // off 0 PageHdr 20B 公共页头
  12. // off 20 Count 4B 行数 (本机字节序, 含墓碑)
  13. // off 24 Min 8B 页内最小 pkID ★BigEndian 编码★
  14. // off 32 Max 8B 页内最大 pkID ★BigEndian 编码★
  15. // off 40 LiveCnt 4B 未删除行数 (本机字节序)
  16. // off 44 Dead 4B 失效字节数 = Σ(4B前缀 + 墓碑行) (本机字节序)
  17. //
  18. // 行数据区紧接在偏移 48 之后, 每行 [len(4B)][rowData...], len 的最高位是软删除标志。
  19. //
  20. // ⚠️ Min/Max 刻意按 BigEndian 存放 (pkID 本身也按 BE 比较), 所以这里声明成
  21. // [8]byte 而不是 uint64 —— 若声明成 uint64, unsafe 映射在小端机上会读到字节反转
  22. // 的值。读写必须走 GetMin/SetMin/GetMax/SetMax。
  23. //
  24. // LiveCnt/Dead 由所有写路径增量维护 (追加/软删/整理页/页内重建/WAL 行级恢复),
  25. // Load 直接读走 —— 无条件 COUNT(*) 从"逐行走链"降到 O(1) 读页头, MaybeCompact
  26. // 的失效占比判断也不再要求 Load 时全页重算。
  27. //
  28. // 这份布局原先在 wal.go(dpHdrSize/dpCountOffset/dpUseSizeOffset)、transaction.go、
  29. // index/datapage 三处各写一遍, 现在只有这里一处定义。
  30. // =============================================================================
  31. type DataPageHdr struct {
  32. PageHdr
  33. Count uint32 // off 20 行数 (含墓碑)
  34. Min [8]byte // off 24 最小 pkID (BigEndian)
  35. Max [8]byte // off 32 最大 pkID (BigEndian)
  36. LiveCnt uint32 // off 40 未删除行数
  37. Dead uint32 // off 44 失效字节数
  38. }
  39. // DataPageHdrSize 数据页头长度; 行数据区从该偏移开始
  40. const DataPageHdrSize = uint32(unsafe.Sizeof(DataPageHdr{})) // 48
  41. // 编译期布局断言
  42. var (
  43. _ [48]byte = [unsafe.Sizeof(DataPageHdr{})]byte{} // 头必须 48 字节
  44. _ [20]byte = [unsafe.Offsetof(DataPageHdr{}.Count)]byte{} // Count 必须偏移 20
  45. _ [24]byte = [unsafe.Offsetof(DataPageHdr{}.Min)]byte{} // Min 必须偏移 24
  46. _ [32]byte = [unsafe.Offsetof(DataPageHdr{}.Max)]byte{} // Max 必须偏移 32
  47. _ [40]byte = [unsafe.Offsetof(DataPageHdr{}.LiveCnt)]byte{} // LiveCnt 必须偏移 40
  48. _ [44]byte = [unsafe.Offsetof(DataPageHdr{}.Dead)]byte{} // Dead 必须偏移 44
  49. )
  50. // DataHdrOf 把页缓冲区按 DataPageHdr 解析 (页头固定在偏移 0)。
  51. func DataHdrOf(buf []byte) *DataPageHdr {
  52. return (*DataPageHdr)(unsafe.Pointer(&buf[0]))
  53. }
  54. // GetMin 读页内最小 pkID。
  55. func (h *DataPageHdr) GetMin() uint64 { return binary.BigEndian.Uint64(h.Min[:]) }
  56. // SetMin 写页内最小 pkID。
  57. func (h *DataPageHdr) SetMin(v uint64) { binary.BigEndian.PutUint64(h.Min[:], v) }
  58. // GetMax 读页内最大 pkID。
  59. func (h *DataPageHdr) GetMax() uint64 { return binary.BigEndian.Uint64(h.Max[:]) }
  60. // SetMax 写页内最大 pkID。
  61. func (h *DataPageHdr) SetMax(v uint64) { binary.BigEndian.PutUint64(h.Max[:], v) }