bufpool.go 1.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445
  1. package btree
  2. import (
  3. "sync"
  4. "TsDB2/DB/core/page"
  5. )
  6. // nodeBufPool 复用 B+ 节点页缓冲 (IndexPageSize)。
  7. //
  8. // 背景: 插入路径每行要沿 4 棵索引树 (主键 + 各二级索引) 逐层加载节点,
  9. // 旧实现每次 btLoad/sbtLoad/btNew 都 make([]byte, IndexPageSize) —— 实测
  10. // 5 万行灌数分配 ~15GB (约 300KB/行), 默认 GOGC 下平均每 17 行触发一轮
  11. // GC, GC 压力 (标记辅助 + STW) 吃掉约 1/3 的插入吞吐。
  12. //
  13. // 协议: 只在【调用方能证明节点已死】的位置 release; 搜索 / 删除等未标注
  14. // 归还的路径仍然各自分配, 行为与旧实现完全一致, 不影响正确性。
  15. //
  16. // 安全性依赖两点:
  17. // - btPathEntry 只存 nodePID/childIdx, 不持有节点引用;
  18. // - 跨函数传递的键 (btLeftmostKey / promote 的 promoKey/pk2/pkc) 都已
  19. // 拷贝出节点缓冲, 没有别名。
  20. //
  21. // 双重 release 会把同一缓冲交给两个持有者, 因此每个节点必须恰好归还一次;
  22. // 所有归还点都在单函数内的确定位置, 便于逐个核对。
  23. var nodeBufPool = sync.Pool{New: func() any { b := make([]byte, page.IndexPageSize); return &b }}
  24. func nodeBufGet() []byte { return *nodeBufPool.Get().(*[]byte) }
  25. // nodeBufPut 归还缓冲。长度不符的异常缓冲不进池, 宁可交给 GC。
  26. func nodeBufPut(buf []byte) {
  27. if len(buf) != page.IndexPageSize || cap(buf) < page.IndexPageSize {
  28. return
  29. }
  30. b := buf
  31. nodeBufPool.Put(&b)
  32. }
  33. // release 把节点的页缓冲归还池中。调用方必须保证此后不再访问该节点
  34. // (包括 info / entries —— 它们都是 buf 的视图)。
  35. func (n *btNode) release() {
  36. if n != nil {
  37. nodeBufPut(n.buf)
  38. }
  39. }