# RaftDB Storage Engine Documentation

RaftDB 内置了一个高性能、线程安全的嵌入式键值存储引擎。该引擎专为 Raft 状态机设计，经过深度优化，采用 **Flat Sorted Array (有序扁平数组)** 作为核心索引结构，并结合了 **倒排索引 (Inverted Index)** 以支持极速全文/模糊检索。

## 1. 核心特性

*   **极速读写 (Blazing Fast)**: 
    *   **Point Lookup**: ~23万 QPS (基于 Binary Search 的内存索引)。
    *   **Insert/Update**: ~40-56万 QPS (Append-only Log + FreeList)。
*   **高级查询 (Advanced Query)**: 
    *   **Prefix/Range**: 基于有序数组的二分查找 + 顺序扫描，内存局部性极佳。
    *   **Full Text Search**: 针对 `value like` 查询引入倒排索引，性能提升 **20倍**。
    *   **Limit Pushdown**: 查询执行器支持 `LIMIT/OFFSET` 下推，扫描满足即停止。
*   **空间复用 (Disk Reuse)**: 内置 Best-Fit 策略的 FreeList，自动回收磁盘空间，无需手动 Compaction。
*   **热点缓存 (Hot Cache)**: 内置 LRU-style 缓存，减少 Syscall。

## 2. 架构设计 (Architecture)

### 2.1 核心索引: Flat Sorted Array (Memory)
摒弃了传统的 Hash Map 或 Tree 结构，采用极致内存优化的 **Flat Sorted Array**。
*   **设计**: 
    *   **Key Buffer**: 所有 Key 紧凑存储在一个 `[]byte` 大数组中，无 Go String Header 开销。
    *   **Item Slice**: 索引项仅存储 `Offset(4B) + Len(2B) + Metadata`，并在内存中保持有序。
*   **优势**: 
    *   **零指针开销**: 消除海量指针和对象头，大幅降低 GC 压力。
    *   **内存极致紧凑**: 索引条目仅占 ~24 字节/Key，百万级 Key 仅需几十 MB。
    *   **缓存友好**: 数组结构对 CPU Cache 极其友好。
    *   **范围查询**: 二分查找定位起点，顺序扫描后续数据，天然支持 Range Scan。

### 2.2 辅助索引: Inverted Index (Memory)
针对 `value like "*token*"` 等模糊查询场景，引擎维护了一个轻量级的倒排索引 (Token -> Keys)。
*   **查询优化**: 当检测到查询包含特定 Token 时，查询规划器会跳过全表扫描，直接通过倒排索引定位 Candidate Keys。

### 2.3 存储层 (Storage Layer)
*   **Append-only Log**: 数据追加写入，保证崩溃恢复能力。
*   **In-Place Update**: 尝试原地更新（若空间足够），减少碎片。
*   **FreeList**: 维护空闲槽位，优先复用。
*   **Page Cache**: 简单的内存缓存层，减少系统调用。

## 3. 安全性与一致性评估 (Safety & Consistency Analysis)

### 3.1 数据持久性 (Durability)
*   **当前机制**: 使用 `WriteAt` 写入操作系统 Page Cache。
*   **风险**: **高**。目前默认未开启 `fsync`。若发生操作系统崩溃或断电，最近写入的数据（尚未刷盘部分）将会丢失。
    *   *注*: 进程级崩溃（Panic/Kill）是安全的，因为 OS 仍掌管 Page Cache。
*   **建议**: 在关键业务场景下，应在写入后显式调用 `Sync()`，但这会显著降低写入吞吐量（从 40w QPS 降至磁盘 IOPS 极限）。

### 3.2 数据完整性 (Integrity)
*   **当前机制**: 引入了 **CRC32 Checksum** 校验每个 Record (Header + Data)。
*   **恢复策略**: 启动时 `Scan` 会验证 CRC。
*   **风险**: **中**。
    *   若日志尾部写入不完整（Partial Write），`Scan` 会自动截断丢弃，保证一致性。
    *   若日志中间发生比特翻转（Bit Rot），`Scan` 会在错误处停止，导致该点**之后的所有数据不可见**。这是一种“宁缺毋滥”的牺牲可用性保一致性的策略。

### 3.3 数据一致性 (Consistency)
*   **索引与存储**: 内存索引在启动时完全通过磁盘日志重构，保证了**强一致性**。
*   **并发可见性**: 
    *   `Set` 操作通过互斥锁串行化，保证了写入线性一致。
    *   **读写并发**: `Query` 并不阻塞 `Set` 的日志写入，但在读取索引时持有 `RLock`。这可能导致短暂的“幻读”或可见性延迟，但不会导致数据错乱。
    *   **风险**: 极低。符合最终一致性模型。

### 3.4 并发模型瓶颈
*   **I/O 阻塞**: `Query` 在遍历索引时（持有 RLock）会进行磁盘 I/O (`ReadValue`)。
*   **影响**: 如果磁盘响应慢，长时间的 `RLock` 会阻塞所有试图获取 `Lock` 的 `Set` 操作（写阻塞）。在慢磁盘或高并发读场景下，写入延迟可能显著增加。

## 4. 性能测试报告 (Benchmark Report)

测试环境: macOS, 10 并发 Workers, 本地磁盘 IO。

### 4.1 综合吞吐量 (Throughput)

| 操作类型 | 数量 | 耗时 | QPS (Ops/sec) | 说明 |
| :--- | :--- | :--- | :--- | :--- |
| **Insert** | 100,000 | ~0.25s | **~399,000** | 写入性能极强 |
| **Insert (Reuse)** | 5,000 | ~0.01s | **~560,000** | 空间复用路径极快 |
| **Update** | 10,000 | ~0.04s | **~252,000** | 原地更新优化生效 |
| **Delete** | 10,000 | ~0.02s | **~420,000** | 标记删除 |

### 4.2 查询性能 (Query Performance)

| 查询类型 | QPS (Ops/sec) | 提升幅度 | 说明 |
| :--- | :--- | :--- | :--- |
| **Point Lookup** | **~228,000** | - | 基准性能，极快。 |
| **Meta Query** | **~78,000** | **2x (vs SkipList)** | 前缀查询 `key like "prefix*"`。有序数组二分查找优势。 |
| **Limit Query** | **~287,000** | **1.6x (vs SkipList)** | `LIMIT` 下推优化，扫描极少数据即返回。 |
| **Full Scan (Val)**| **~581** | **21.5x (vs Scan)** | **倒排索引生效**。从全表 IO 扫描变为内存索引查找。 |

### 4.3 内存占用分析 (Memory Usage)

| 场景 | 内存占用 (10w Keys) | 说明 |
| :--- | :--- | :--- |
| **仅核心索引 (Key Only)** | **5.46 MB** | **Flat Sorted Array 生效**。仅 ~55字节/Key (含Go Runtime开销)，极其紧凑。 |
| **开启全文索引 (With Value Index)** | **73.93 MB** | 倒排索引 (Token -> Keys) 占用主要内存，以空间换取 20x 查询性能。 |

## 5. 使用说明

### 5.1 初始化

```go
import "db"

e, err := db.NewEngine("./my_data")
if err != nil { panic(err) }
defer e.Close()
```

### 5.2 查询示例

```go
// 1. 极速前缀分页
// 引擎在索引上定位 "user." 范围，扫描前 20 条即停止
results, _ := e.Query(`key like "user.*" LIMIT 20`)

// 2. 高性能全文检索
// 引擎利用倒排索引直接定位包含 "error" 的记录，无需扫描全表
results, _ := e.Query(`value like "*error*"`)
```
