db.md 5.6 KB

RaftDB 存储引擎文档

RaftDB 内置了一个专为 Raft 状态机设计的高性能、线程安全的嵌入式键值(KV)存储引擎。它采用了 扁平有序数组 (Flat Sorted Array) 作为核心内存索引,结合 倒排索引 (Inverted Index) 支持全文检索,并使用 追加写日志 (Append-Only Log) 保证数据的持久化。

1. 核心特性

  • 极致性能 (High Performance):
    • 内存索引: 使用基于有序数组的二分查找,读取性能极高,无指针开销。
    • 追加写入: 所有写操作(Put/Delete)均追加到文件末尾,充分利用磁盘顺序写带宽。
  • 高级查询能力 (Advanced Querying):
    • 前缀扫描: 得益于有序索引,支持高效的 key like "prefix*" 前缀范围扫描。
    • 全文检索: 内置倒排索引,支持 value like "*token*" 的模糊查询,无需全表扫描。
    • SQL-Like 语法: 支持 LIMIT, OFFSET 以及复杂的组合过滤条件(例如 key="k1" AND value like "v*")。
  • 数据安全与一致性 (Safety & Consistency):
    • CRC32 校验: 每条记录都包含 CRC32 校验和,防止磁盘静默错误导致的数据损坏。
    • 快照隔离: 支持生成全量的一致性快照,用于 Raft 日志压缩。

2. 架构设计

2.1 核心索引:扁平有序数组 (Memory - Primary Index)

不同于传统的 B-Tree 或 Hash Map,本引擎采用了 Flat Sorted Array 结构:

  • 数据结构:
    • keyBuf: 一个巨大的单一 []byte 缓冲,紧凑存储所有 Key 的字符串数据。
    • items: 一个切片,存储轻量级的元数据结构(包含 Key 的偏移量、长度,以及 Value 在磁盘上的偏移量、CommitIndex)。
  • 优势:
    • 极度紧凑: 极大地减少了内存碎片和每个 Key 的额外开销。
    • 缓存友好: 连续的内存布局使得 CPU 缓存命中率极高。
    • GC 友好: 几乎不产生指针,大幅降低了 Go GC 的扫描压力。

2.2 辅助索引:全文倒排索引 (Memory - Secondary Index)

为了支持 value like 查询,引擎维护了一个倒排索引:

  • 映射关系: Token (词) -> Key Set (包含该词的 Key 集合)。
  • 分词策略: 简单的按非字母数字字符分割。
  • 查询优化: 当查询包含 value like 时,查询优化器会自动通过倒排索引筛选 Key,避免昂贵的全表 Value 读取。

2.3 存储层 (Disk Storage)

  • Append-Only Log: 数据存储在 values.data 文件中。
    • 写入 (Put): 追加 [Header][Key][Value]
    • 删除 (Delete): 追加一个墓碑(Tombstone)记录。
  • 元数据 (Metadata): meta.state 文件独立存储 LastCommitIndex,允许系统快速启动而无需重放整个日志。
  • 缓存 (Caching): 内置简单的 LRU 策略缓存,缓存最近读取的 Value,减少热点数据的磁盘 IO。

3. 一致性与持久化

3.1 持久性 (Durability)

  • Sync 机制: 引擎提供 Sync() 方法,调用系统调用 fsync 强制将 OS 页缓存刷入磁盘。
  • Raft 集成: Raft 在执行快照(Snapshot)前会强制调用 Sync(),确保在删除 Raft Log 之前,数据已经物理持久化。

3.2 数据完整性 (Integrity)

  • 校验和: 每条记录写入时计算 CRC32,读取时验证。
  • 故障恢复: 启动扫描时,如果发现文件尾部有损坏或写入不完整的记录(常见于断电崩溃),引擎会自动截断尾部脏数据,保证数据库状态的一致性。

3.3 快照与恢复 (Snapshot & Restore)

这是 Raft Log Compaction 的核心机制:

  • Snapshot (快照): 锁定内存索引,将当前所有有效的 Key-Value 对序列化为一个紧凑的二进制流。这个流代表了某一时刻(CommitIndex)的完整状态机状态。
  • Restore (恢复):
    1. 关闭当前数据库。
    2. Truncate (清空) 数据文件。
    3. 从快照流中重建数据文件和内存索引。
    4. 强制刷盘。 此过程通常用于落后太多的 Follower 节点快速追赶 Leader。

4. 查询语言 (Query Language)

引擎支持一种简化的 SQL 风格查询语法,用于调试和数据检查:

-- 精确查找 (Point Lookup)
key = "user:1001"

-- 前缀范围查询 (Prefix Search)
key like "user:*"

-- 全文模糊查询 (Full-Text Search, 需要开启 ValueIndex)
value like "error"

-- 组合过滤 (Complex Filtering)
key like "log:*" AND value like "*critical*"

-- 分页 (Pagination)
key like "user:*" LIMIT 10 OFFSET 20

-- 基于 Raft Index 过滤
CommitIndex > 1000

5. 配置说明

// 初始化引擎时可以配置选项
// 例如:开启全文索引(会消耗更多内存)
engine, err := db.NewEngine(path, db.WithValueIndex(true))

6. 性能特征

  • 写入: O(1) 磁盘追加 + O(N) 内存插入(数组移动)。
    • : 由于使用有序数组,插入涉及内存移动,因此该引擎更适合读多写少批量写入的场景。
  • 读取: O(log N) 内存二分查找 + 1次 磁盘 Seek。
  • 扫描: O(log N) 定位起点 + O(M) 顺序扫描 M 个元素。
  • 全文搜索: O(1) 倒排索引查找 + O(K) 结果集过滤。

7. 局限性

  • 内存限制: 虽然 Value 存储在磁盘,但所有 Key 和索引元数据必须能放入内存。
  • 空间回收: 由于采用 Append-Only 模式,删除操作不会立即释放磁盘空间。空间回收依赖于 Raft 的 Snapshot/Restore 机制(Restore 会重写数据文件,从而清理垃圾数据)。