Files
srdb/btree.go

833 lines
24 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package srdb
import (
"encoding/binary"
"fmt"
"os"
"slices"
"sort"
"github.com/edsrzf/mmap-go"
)
/*
B+Tree 存储格式
B+Tree 用于索引 SSTable 和 Index 文件,提供 O(log n) 查询性能。
节点结构 (4096 bytes):
┌─────────────────────────────────────────────────────────────┐
│ Node Header (32 bytes) │
│ ├─ NodeType (1 byte): 0=Internal, 1=Leaf │
│ ├─ KeyCount (2 bytes): 节点中的 key 数量 │
│ ├─ Level (1 byte): 层级 (0=叶子层) │
│ └─ Reserved (28 bytes): 预留空间 │
├─────────────────────────────────────────────────────────────┤
│ Keys Array (variable) │
│ └─ Key[0..KeyCount-1]: int64 (8 bytes each) │
├─────────────────────────────────────────────────────────────┤
│ Values (variable, 取决于节点类型) │
│ │
│ 内部节点 (Internal Node): │
│ └─ Children[0..KeyCount]: int64 (8 bytes each) │
│ - 子节点的文件偏移量 │
│ - Children[i] 包含 < Key[i] 的所有 key │
│ - Children[KeyCount] 包含 >= Key[KeyCount-1] 的 key │
│ │
│ 叶子节点 (Leaf Node): │
│ └─ Data Pairs[0..KeyCount-1]: 交错存储 (12 bytes each) │
│ ├─ DataOffset: int64 (8 bytes) - 数据块的文件偏移量 │
│ └─ DataSize: int32 (4 bytes) - 数据块的大小 │
└─────────────────────────────────────────────────────────────┘
节点头格式 (32 bytes):
Offset | Size | Field | Description
-------|------|------------|----------------------------------
0 | 1 | NodeType | 0=Internal, 1=Leaf
1 | 2 | KeyCount | key 数量 (0 ~ BTreeOrder)
3 | 1 | Level | 层级 (0=叶子层, 1+=内部层)
4 | 28 | Reserved | 预留空间
内部节点布局 (示例: KeyCount=3):
[Header: 32B]
[Keys: Key0(8B), Key1(8B), Key2(8B)]
[Children: Child0(8B), Child1(8B), Child2(8B), Child3(8B)]
查询规则:
- key < Key0 → Child0
- Key0 ≤ key < Key1 → Child1
- Key1 ≤ key < Key2 → Child2
- key ≥ Key2 → Child3
叶子节点布局 (示例: KeyCount=3):
[Header: 32B]
[Keys: Key0(8B), Key1(8B), Key2(8B)]
[Data: (Offset0, Size0), (Offset1, Size1), (Offset2, Size2)]
- 交错存储: Offset0(8B), Size0(4B), Offset1(8B), Size1(4B), Offset2(8B), Size2(4B)
查询规则:
- 找到 key == Key[i]
- 返回 (DataOffsets[i], DataSizes[i])
B+Tree 特性:
- 阶数 (Order): 200 (每个节点最多 200 个 key)
- 节点大小: 4096 bytes (4 KB对齐页大小)
- 高度: log₂₀₀(N) (100万条数据约 3 层)
- 查询复杂度: O(log n)
- 范围查询: 支持(叶子节点有序)
文件布局示例:
SSTable/Index 文件:
[Header: 256B]
[B+Tree Nodes: 4KB each]
├─ Root Node (Internal)
├─ Level 1 Nodes (Internal)
└─ Leaf Nodes
[Data Blocks: variable]
性能优化:
- mmap 零拷贝: 直接从内存映射读取节点
- 节点对齐: 4KB 对齐,利用操作系统页缓存
- 有序存储: 叶子节点有序,支持范围查询
- 紧凑编码: 最小化节点大小,提高缓存命中率
*/
const (
BTreeNodeSize = 4096 // 节点大小 (4 KB)
BTreeOrder = 200 // B+Tree 阶数 (保守估计叶子节点每个entry 20 bytes)
BTreeHeaderSize = 32 // 节点头大小
BTreeNodeTypeInternal = 0 // 内部节点
BTreeNodeTypeLeaf = 1 // 叶子节点
)
// BTreeNode 表示一个 B+Tree 节点 (4 KB)
type BTreeNode struct {
// Header (32 bytes)
NodeType byte // 0=Internal, 1=Leaf
KeyCount uint16 // key 数量
Level byte // 层级 (0=叶子层)
Reserved [28]byte // 预留字段
// Keys (variable, 最多 256 个)
Keys []int64 // key 数组
// Values (variable)
// Internal Node: 子节点指针
Children []int64 // 子节点的文件 offset
// Leaf Node: 数据位置
DataOffsets []int64 // 数据块的文件 offset
DataSizes []int32 // 数据块大小
}
// NewInternalNode 创建内部节点
func NewInternalNode(level byte) *BTreeNode {
return &BTreeNode{
NodeType: BTreeNodeTypeInternal,
Level: level,
Keys: make([]int64, 0, BTreeOrder),
Children: make([]int64, 0, BTreeOrder+1),
}
}
// NewLeafNode 创建叶子节点
func NewLeafNode() *BTreeNode {
return &BTreeNode{
NodeType: BTreeNodeTypeLeaf,
Level: 0,
Keys: make([]int64, 0, BTreeOrder),
DataOffsets: make([]int64, 0, BTreeOrder),
DataSizes: make([]int32, 0, BTreeOrder),
}
}
// Marshal 序列化节点到 4 KB
//
// 布局:
//
// [Header: 32B]
// [Keys: KeyCount * 8B]
// [Values: 取决于节点类型]
// - Internal: Children (KeyCount+1) * 8B
// - Leaf: 交错存储 (Offset, Size) 对,每对 12B共 KeyCount * 12B
//
// 示例叶子节点KeyCount=3
//
// Offset | Size | Content
// -------|------|----------------------------------
// 0 | 1 | NodeType = 1 (Leaf)
// 1 | 2 | KeyCount = 3
// 3 | 1 | Level = 0
// 4 | 28 | Reserved
// 32 | 24 | Keys [100, 200, 300]
// 56 | 8 | DataOffset0 = 1000
// 64 | 4 | DataSize0 = 50
// 68 | 8 | DataOffset1 = 2000
// 76 | 4 | DataSize1 = 60
// 80 | 8 | DataOffset2 = 3000
// 88 | 4 | DataSize2 = 70
// 92 | 4004 | Padding (unused)
func (n *BTreeNode) Marshal() []byte {
buf := make([]byte, BTreeNodeSize)
// 写入 Header (32 bytes)
buf[0] = n.NodeType
binary.LittleEndian.PutUint16(buf[1:3], n.KeyCount)
buf[3] = n.Level
copy(buf[4:32], n.Reserved[:])
// 写入 Keys
offset := BTreeHeaderSize
for _, key := range n.Keys {
if offset+8 > BTreeNodeSize {
break
}
binary.LittleEndian.PutUint64(buf[offset:offset+8], uint64(key))
offset += 8
}
// 写入 Values
if n.NodeType == BTreeNodeTypeInternal {
// Internal Node: 写入子节点指针
for _, child := range n.Children {
if offset+8 > BTreeNodeSize {
break
}
binary.LittleEndian.PutUint64(buf[offset:offset+8], uint64(child))
offset += 8
}
} else {
// Leaf Node: 写入数据位置
for i := 0; i < len(n.Keys); i++ {
if offset+12 > BTreeNodeSize {
break
}
binary.LittleEndian.PutUint64(buf[offset:offset+8], uint64(n.DataOffsets[i]))
offset += 8
binary.LittleEndian.PutUint32(buf[offset:offset+4], uint32(n.DataSizes[i]))
offset += 4
}
}
return buf
}
// UnmarshalBTree 从字节数组反序列化节点
//
// 参数:
//
// data: 4KB 节点数据(通常来自 mmap
//
// 返回:
//
// *BTreeNode: 反序列化后的节点
//
// 零拷贝优化:
// - 直接从 mmap 数据读取,不复制整个节点
// - 只复制必要的字段Keys, Children, DataOffsets, DataSizes
func UnmarshalBTree(data []byte) *BTreeNode {
if len(data) < BTreeNodeSize {
return nil
}
node := &BTreeNode{}
// 读取 Header
node.NodeType = data[0]
node.KeyCount = binary.LittleEndian.Uint16(data[1:3])
node.Level = data[3]
copy(node.Reserved[:], data[4:32])
// 读取 Keys
offset := BTreeHeaderSize
node.Keys = make([]int64, node.KeyCount)
for i := 0; i < int(node.KeyCount); i++ {
if offset+8 > len(data) {
break
}
node.Keys[i] = int64(binary.LittleEndian.Uint64(data[offset : offset+8]))
offset += 8
}
// 读取 Values
if node.NodeType == BTreeNodeTypeInternal {
// Internal Node: 读取子节点指针
childCount := int(node.KeyCount) + 1
node.Children = make([]int64, childCount)
for i := range childCount {
if offset+8 > len(data) {
break
}
node.Children[i] = int64(binary.LittleEndian.Uint64(data[offset : offset+8]))
offset += 8
}
} else {
// Leaf Node: 读取数据位置
node.DataOffsets = make([]int64, node.KeyCount)
node.DataSizes = make([]int32, node.KeyCount)
for i := 0; i < int(node.KeyCount); i++ {
if offset+12 > len(data) {
break
}
node.DataOffsets[i] = int64(binary.LittleEndian.Uint64(data[offset : offset+8]))
offset += 8
node.DataSizes[i] = int32(binary.LittleEndian.Uint32(data[offset : offset+4]))
offset += 4
}
}
return node
}
// IsFull 检查节点是否已满
func (n *BTreeNode) IsFull() bool {
return len(n.Keys) >= BTreeOrder
}
// AddKey 添加 key (仅用于构建)
func (n *BTreeNode) AddKey(key int64) {
n.Keys = append(n.Keys, key)
n.KeyCount = uint16(len(n.Keys))
}
// AddChild 添加子节点 (仅用于内部节点)
func (n *BTreeNode) AddChild(offset int64) error {
if n.NodeType != BTreeNodeTypeInternal {
return fmt.Errorf("AddChild called on leaf node")
}
n.Children = append(n.Children, offset)
return nil
}
// AddData 添加数据位置 (仅用于叶子节点)
func (n *BTreeNode) AddData(key int64, offset int64, size int32) error {
if n.NodeType != BTreeNodeTypeLeaf {
return fmt.Errorf("AddData called on internal node")
}
n.Keys = append(n.Keys, key)
n.DataOffsets = append(n.DataOffsets, offset)
n.DataSizes = append(n.DataSizes, size)
n.KeyCount = uint16(len(n.Keys))
return nil
}
// BTreeBuilder 从下往上构建 B+Tree
//
// 构建流程:
//
// 1. Add(): 添加所有 (key, offset, size) 到叶子节点
// - 当叶子节点满时,创建新的叶子节点
// - 所有叶子节点按 key 有序
//
// 2. Build(): 从叶子层向上构建
// - Level 0: 叶子节点(已创建)
// - Level 1: 为叶子节点创建父节点(内部节点)
// - Level 2+: 递归创建更高层级
// - 最终返回根节点偏移量
//
// 示例100 个 keyOrder=200
// - 叶子层: 1 个叶子节点100 个 key
// - 根节点: 叶子节点本身
//
// 示例500 个 keyOrder=200
// - 叶子层: 3 个叶子节点200, 200, 100 个 key
// - Level 1: 1 个内部节点3 个子节点)
// - 根节点: Level 1 的内部节点
type BTreeBuilder struct {
order int // B+Tree 阶数
file *os.File // 输出文件
offset int64 // 当前写入位置
leafNodes []*BTreeNode // 叶子节点列表
}
// NewBTreeBuilder 创建构建器
func NewBTreeBuilder(file *os.File, startOffset int64) *BTreeBuilder {
return &BTreeBuilder{
order: BTreeOrder,
file: file,
offset: startOffset,
leafNodes: make([]*BTreeNode, 0),
}
}
// Add 添加一个 key-value 对 (数据必须已排序)
func (b *BTreeBuilder) Add(key int64, dataOffset int64, dataSize int32) error {
// 获取或创建当前叶子节点
var leaf *BTreeNode
if len(b.leafNodes) == 0 || b.leafNodes[len(b.leafNodes)-1].IsFull() {
// 创建新的叶子节点
leaf = NewLeafNode()
b.leafNodes = append(b.leafNodes, leaf)
} else {
leaf = b.leafNodes[len(b.leafNodes)-1]
}
// 添加到叶子节点
if err := leaf.AddData(key, dataOffset, dataSize); err != nil {
return err
}
return nil
}
// Build 构建完整的 B+Tree返回根节点的 offset
func (b *BTreeBuilder) Build() (rootOffset int64, err error) {
if len(b.leafNodes) == 0 {
return 0, nil
}
// 1. 写入所有叶子节点,记录它们的 offset
leafOffsets := make([]int64, len(b.leafNodes))
for i, leaf := range b.leafNodes {
leafOffsets[i] = b.offset
data := leaf.Marshal()
_, err := b.file.WriteAt(data, b.offset)
if err != nil {
return 0, err
}
b.offset += BTreeNodeSize
}
// 2. 如果只有一个叶子节点,它就是根
if len(b.leafNodes) == 1 {
return leafOffsets[0], nil
}
// 3. 从下往上构建内部节点
currentLevel := b.leafNodes
currentOffsets := leafOffsets
level := 1
for len(currentLevel) > 1 {
nextLevel, nextOffsets, err := b.buildLevel(currentLevel, currentOffsets, level)
if err != nil {
return 0, err
}
currentLevel = nextLevel
currentOffsets = nextOffsets
level++
}
// 4. 返回根节点的 offset
return currentOffsets[0], nil
}
// buildLevel 构建一层内部节点
func (b *BTreeBuilder) buildLevel(children []*BTreeNode, childOffsets []int64, level int) ([]*BTreeNode, []int64, error) {
var parents []*BTreeNode
var parentOffsets []int64
// 每 order 个子节点创建一个父节点
for i := 0; i < len(children); i += b.order {
end := min(i+b.order, len(children))
// 创建父节点
parent := NewInternalNode(byte(level))
// 添加第一个子节点 (没有对应的 key)
if err := parent.AddChild(childOffsets[i]); err != nil {
return nil, nil, err
}
// 添加剩余的子节点和分隔 key
for j := i + 1; j < end; j++ {
// 分隔 key 是子节点的第一个 key
separatorKey := children[j].Keys[0]
parent.AddKey(separatorKey)
if err := parent.AddChild(childOffsets[j]); err != nil {
return nil, nil, err
}
}
// 写入父节点
parentOffset := b.offset
data := parent.Marshal()
_, err := b.file.WriteAt(data, b.offset)
if err != nil {
return nil, nil, err
}
b.offset += BTreeNodeSize
parents = append(parents, parent)
parentOffsets = append(parentOffsets, parentOffset)
}
return parents, parentOffsets, nil
}
// BTreeReader 用于查询 B+Tree (mmap)
//
// 查询流程:
// 1. 从根节点开始
// 2. 如果是内部节点:
// - 二分查找确定子节点
// - 跳转到子节点继续查找
// 3. 如果是叶子节点:
// - 二分查找 key
// - 返回 (dataOffset, dataSize)
//
// 性能优化:
// - mmap 零拷贝:直接从内存映射读取节点
// - 二分查找O(log KeyCount) 在节点内查找
// - 总复杂度O(log n) = O(height * log Order)
//
// 示例100万条数据Order=200
// - 高度: log₂₀₀(1000000) ≈ 3
// - 查询次数: 3 次节点读取 + 3 次二分查找
type BTreeReader struct {
mmap mmap.MMap
rootOffset int64
}
// NewBTreeReader 创建查询器
func NewBTreeReader(mmap mmap.MMap, rootOffset int64) *BTreeReader {
return &BTreeReader{
mmap: mmap,
rootOffset: rootOffset,
}
}
// Get 查询 key返回数据位置
//
// 参数:
//
// key: 要查询的 key
//
// 返回:
//
// dataOffset: 数据块的文件偏移量
// dataSize: 数据块的大小
// found: 是否找到
//
// 查询流程:
// 1. 从根节点开始遍历
// 2. 内部节点:二分查找确定子节点,跳转
// 3. 叶子节点:二分查找 key返回数据位置
func (r *BTreeReader) Get(key int64) (dataOffset int64, dataSize int32, found bool) {
if r.rootOffset == 0 {
return 0, 0, false
}
nodeOffset := r.rootOffset
for {
// 读取节点 (零拷贝)
if nodeOffset+BTreeNodeSize > int64(len(r.mmap)) {
return 0, 0, false
}
nodeData := r.mmap[nodeOffset : nodeOffset+BTreeNodeSize]
node := UnmarshalBTree(nodeData)
if node == nil {
return 0, 0, false
}
// 叶子节点
if node.NodeType == BTreeNodeTypeLeaf {
// 二分查找
idx := sort.Search(len(node.Keys), func(i int) bool {
return node.Keys[i] >= key
})
if idx < len(node.Keys) && node.Keys[idx] == key {
return node.DataOffsets[idx], node.DataSizes[idx], true
}
return 0, 0, false
}
// 内部节点,继续向下
// keys[i] 是分隔符children[i] 包含 < keys[i] 的数据
// children[i+1] 包含 >= keys[i] 的数据
idx := sort.Search(len(node.Keys), func(i int) bool {
return node.Keys[i] > key
})
// idx 现在指向第一个 > key 的位置
// 我们应该走 children[idx]
if idx >= len(node.Children) {
idx = len(node.Children) - 1
}
nodeOffset = node.Children[idx]
}
}
// GetAllKeys 获取 B+Tree 中所有的 key按升序
func (r *BTreeReader) GetAllKeys() []int64 {
if r.rootOffset == 0 {
return nil
}
var keys []int64
r.traverseLeafNodes(r.rootOffset, func(node *BTreeNode) {
keys = append(keys, node.Keys...)
})
// 显式排序以确保返回的 keys 严格有序
// 虽然 B+Tree 构建时应该已经是有序的,但这是一个安全保障
// 特别是在 compaction 后,确保查询结果正确排序
slices.Sort(keys)
return keys
}
// GetAllKeysDesc 获取 B+Tree 中所有的 key按降序
//
// 性能优化:
// - 从右到左遍历叶子节点
// - 每个叶子节点内从后往前读取 keys
// - 避免额外的排序操作
func (r *BTreeReader) GetAllKeysDesc() []int64 {
if r.rootOffset == 0 {
return nil
}
var keys []int64
r.traverseLeafNodesReverse(r.rootOffset, func(node *BTreeNode) {
// 从后往前添加 keys
for i := len(node.Keys) - 1; i >= 0; i-- {
keys = append(keys, node.Keys[i])
}
})
return keys
}
// KeyCallback 迭代回调函数
//
// 参数:
// - key: 当前的 key序列号
// - dataOffset: 数据块的文件偏移量
// - dataSize: 数据块的大小
//
// 返回:
// - true: 继续迭代
// - false: 停止迭代
type KeyCallback func(key int64, dataOffset int64, dataSize int32) bool
// ForEach 升序迭代所有 key支持提前终止
//
// 使用场景:
// - 需要遍历数据但不想一次性加载所有 keys节省内存
// - 支持条件过滤,找到目标后提前终止
// - 支持外部自定义处理逻辑
//
// 示例:
//
// // 找到第一个 > 100 的 key
// reader.ForEach(func(key int64, offset int64, size int32) bool {
// if key > 100 {
// fmt.Printf("Found: %d\n", key)
// return false // 停止迭代
// }
// return true // 继续
// })
func (r *BTreeReader) ForEach(callback KeyCallback) {
if r.rootOffset == 0 {
return
}
r.forEachInternal(r.rootOffset, callback, false)
}
// ForEachDesc 降序迭代所有 key支持提前终止
//
// 使用场景:
// - 从最新数据开始遍历(时序数据库常见需求)
// - 查找最近的 N 条记录
// - 支持条件过滤和提前终止
//
// 示例:
//
// // 获取最新的 10 条记录
// count := 0
// reader.ForEachDesc(func(key int64, offset int64, size int32) bool {
// fmt.Printf("Key: %d\n", key)
// count++
// return count < 10 // 找到 10 条后停止
// })
func (r *BTreeReader) ForEachDesc(callback KeyCallback) {
if r.rootOffset == 0 {
return
}
r.forEachInternal(r.rootOffset, callback, true)
}
// forEachInternal 内部迭代实现(支持升序和降序)
//
// 性能优化(真正的按需读取):
// - 只读取节点 header32 bytes确定节点类型和 key 数量
// - 对于叶子节点,逐个读取 key、offset、size避免一次性读取所有数据
// - 对于内部节点,逐个读取 child offset支持提前终止
// - 如果回调在第 N 个 key 返回 false只会读取前 N 个 key
//
// 参数:
// - nodeOffset: 当前节点的文件偏移量
// - callback: 回调函数
// - reverse: true=降序, false=升序
//
// 返回:
// - true: 继续迭代
// - false: 停止迭代(外部请求或遍历完成)
func (r *BTreeReader) forEachInternal(nodeOffset int64, callback KeyCallback, reverse bool) bool {
if nodeOffset+BTreeNodeSize > int64(len(r.mmap)) {
return true // 无效节点,继续其他分支
}
nodeData := r.mmap[nodeOffset : nodeOffset+BTreeNodeSize]
// 只读取 header32 bytes
if len(nodeData) < BTreeHeaderSize {
return true
}
nodeType := nodeData[0]
keyCount := int(binary.LittleEndian.Uint16(nodeData[1:3]))
if nodeType == BTreeNodeTypeLeaf {
// 叶子节点:按需逐个读取 key 和 data
// 布局:[Header: 32B][Keys: keyCount*8B][Data: (offset,size) pairs]
keysStartOffset := BTreeHeaderSize
dataStartOffset := keysStartOffset + keyCount*8
if reverse {
// 降序:从后往前读取
for i := keyCount - 1; i >= 0; i-- {
// 读取 key
keyOffset := keysStartOffset + i*8
if keyOffset+8 > len(nodeData) {
break
}
key := int64(binary.LittleEndian.Uint64(nodeData[keyOffset : keyOffset+8]))
// 读取 dataOffset 和 dataSize交错存储每对 12 bytes
dataOffset := dataStartOffset + i*12
if dataOffset+12 > len(nodeData) {
break
}
offset := int64(binary.LittleEndian.Uint64(nodeData[dataOffset : dataOffset+8]))
size := int32(binary.LittleEndian.Uint32(nodeData[dataOffset+8 : dataOffset+12]))
// 调用回调,如果返回 false 则立即停止(真正的按需读取)
if !callback(key, offset, size) {
return false
}
}
} else {
// 升序:从前往后读取
for i := range keyCount {
// 读取 key
keyOffset := keysStartOffset + i*8
if keyOffset+8 > len(nodeData) {
break
}
key := int64(binary.LittleEndian.Uint64(nodeData[keyOffset : keyOffset+8]))
// 读取 dataOffset 和 dataSize
dataOffset := dataStartOffset + i*12
if dataOffset+12 > len(nodeData) {
break
}
offset := int64(binary.LittleEndian.Uint64(nodeData[dataOffset : dataOffset+8]))
size := int32(binary.LittleEndian.Uint32(nodeData[dataOffset+8 : dataOffset+12]))
// 调用回调,如果返回 false 则立即停止
if !callback(key, offset, size) {
return false
}
}
}
return true
}
// 内部节点:按需逐个读取 child offset
// 布局:[Header: 32B][Keys: keyCount*8B][Children: (keyCount+1)*8B]
childCount := keyCount + 1
childrenStartOffset := BTreeHeaderSize + keyCount*8
if reverse {
// 降序:从右到左遍历子节点
for i := childCount - 1; i >= 0; i-- {
childOffset := childrenStartOffset + i*8
if childOffset+8 > len(nodeData) {
break
}
childPtr := int64(binary.LittleEndian.Uint64(nodeData[childOffset : childOffset+8]))
// 递归遍历子树,如果子树请求停止则立即返回
if !r.forEachInternal(childPtr, callback, reverse) {
return false
}
}
} else {
// 升序:从左到右遍历子节点
for i := range childCount {
childOffset := childrenStartOffset + i*8
if childOffset+8 > len(nodeData) {
break
}
childPtr := int64(binary.LittleEndian.Uint64(nodeData[childOffset : childOffset+8]))
// 递归遍历子树
if !r.forEachInternal(childPtr, callback, reverse) {
return false
}
}
}
return true
}
// traverseLeafNodes 遍历所有叶子节点(从左到右)
func (r *BTreeReader) traverseLeafNodes(nodeOffset int64, callback func(*BTreeNode)) {
if nodeOffset+BTreeNodeSize > int64(len(r.mmap)) {
return
}
nodeData := r.mmap[nodeOffset : nodeOffset+BTreeNodeSize]
node := UnmarshalBTree(nodeData)
if node == nil {
return
}
if node.NodeType == BTreeNodeTypeLeaf {
// 叶子节点,执行回调
callback(node)
} else {
// 内部节点,递归遍历所有子节点(从左到右)
for _, childOffset := range node.Children {
r.traverseLeafNodes(childOffset, callback)
}
}
}
// traverseLeafNodesReverse 倒序遍历所有叶子节点(从右到左)
//
// 用于支持倒序查询,性能优化:
// - 避免先获取所有 keys 再反转
// - 直接从最右侧的叶子节点开始遍历
func (r *BTreeReader) traverseLeafNodesReverse(nodeOffset int64, callback func(*BTreeNode)) {
if nodeOffset+BTreeNodeSize > int64(len(r.mmap)) {
return
}
nodeData := r.mmap[nodeOffset : nodeOffset+BTreeNodeSize]
node := UnmarshalBTree(nodeData)
if node == nil {
return
}
if node.NodeType == BTreeNodeTypeLeaf {
// 叶子节点,执行回调
callback(node)
} else {
// 内部节点,递归遍历所有子节点(从右到左)
for i := len(node.Children) - 1; i >= 0; i-- {
r.traverseLeafNodesReverse(node.Children[i], callback)
}
}
}