flowdb 0.6.1

A high-performance embedded time-series + JSON document storage engine (LSM-tree), with built-in IndexedDB-compatible API.
Documentation
# FlowDB 介绍:一个纯 Rust 的嵌入式 LSM 引擎与 JSON 文档数据库

## 前言

上周在优化一个 IoT 边缘网关的存储层时,我被 RocksDB 的 C 依赖和庞大体量困扰。搜索后发现 FlowDB 这个 Rust 原生项目——**零 C 依赖、零异步运行时、纯 Rust 实现**的嵌入式引擎,而且内置了一个 **IndexedDB 兼容的 JSON 文档层**。

一番测试下来,几个数字让我印象深刻:
- 点查 **600 万 ops/s**,是 RocksDB 的 **11 倍**
- 8 线程并发写 **940 万 ops/s**,是 RocksDB 的 **2 倍**
- 二进制仅 **~680KB**,零 C 依赖,零异步依赖

本文从产品能力、技术实现、性能优势三个角度介绍 FlowDB。如果你在 Rust 社区寻找一个可嵌入的存储引擎,或者对 LSM 的工程优化感兴趣,这篇文章应该对你有帮助。

---

## 一、产品能力:一个引擎,两种用法

FlowDB 提供两层 API:

### 底层:LSM-Tree Key-Value 引擎

```rust
use flowdb::{Engine, Config};

let engine = Engine::open(Config::new("/tmp/mydb"))?;
engine.put(b"key", b"value")?;
let val = engine.get(b"key")?;         // Option<Vec<u8>>
engine.delete(b"key")?;
```

支持前缀扫描、范围扫描、批量写入、迭代器。这是一个完整的 LSM 引擎,但功能定位很克制——不像 RocksDB 那样有数百个配置项。

### 上层:JsonDB 文档层

JsonDB 是构建在 LSM 引擎之上的 JSON 文档数据库,兼容 **IndexedDB API 范式**(object store + index):

```rust
use flowdb::{Engine, Config, JsonDB, Transaction, QueryBuilder};
use serde_json::json;

let engine = Engine::open(Config::new("/tmp/myjsondb"))?;
let jsondb = JsonDB::open(engine)?;

// 创建 store 和索引
let store = jsondb.create_object_store("users", "id")?;
jsondb.create_index("users", "email", true)?;  // unique index

// 写入文档
jsondb.put("users", json!({"id": "u1", "name": "Alice", "email": "alice@x.com", "age": 30}))?;

// 按主键查询
let doc = jsondb.get::<Value>("users", "u1")?;

// 按索引查询
let doc = jsondb.get_by_index::<Value>("users", "email", "alice@x.com")?;

// 范围扫描 + 排序
let results = jsondb.range_by_index::<Value>(
    "users", "age", 20..=40, SortDir::Asc, None
)?;

// 声明式查询 (自动选索引)
let results = QueryBuilder::new("users")
    .where_eq("email", "bob@x.com")
    .collect(&jsondb)?;
```

**核心特性:**
- 多索引支持(唯一/非唯一,单字段/复合)
- 嵌套字段查询(`"address.city"` 点号路径)
- 事务支持(OCC + MVCC,批量原子提交,自动回滚)
- 自动递增主键
- 范围扫描、分页、排序

**典型场景:** 边缘计算、移动端、IoT 设备、嵌入式系统、浏览器引擎(WASM 兼容)。

---

## 二、技术实现:一个「做减法」的架构故事

FlowDB 有趣的地方不在于它加了什么,而在于它 **去掉了什么**。

### 第一刀:砍掉整个 Server 层

早期的 FlowDB 是一个完整的时间序列数据库——HTTP API、UDP 接收、Admin UI、Auth、TOML 配置……v0.2.1 一刀砍掉 40% 代码,回归「嵌入式引擎」定位。

### 第二刀:砍掉异步运行时

v0.3.0 之前,FlowDB 的公共 API 是 async fn,但作者逐一审查发现 **12 个 async 方法中 8 个没有真正 await**。最终全面移除 Tokio,改为 `std::thread::spawn` + `AtomicBool` 停止信号。

现在所有公共 API 都是同步 fn,无异步依赖,二进制缩小 **40%**。

### 砍掉之后留下的架构

```
写入路径:Client → 预编码(无锁) → WriteWorker(Mutex) → WAL + MemTable → (后台flush) SST
读取路径:Client → MemTable → 块索引 → Bloom过滤器 → SST(mmap, LRU缓存)
后台线程:flush / compact / gc / wal_sync (std::thread 循环)
```

### 几个值得一提的设计

**1. Vec 做活跃 memtable**

大部分 LSM(RocksDB、sled)用 BTreeMap 或 SkipList 做 memtable,写入时有排序开销。FlowDB 用 `Vec::push` **O(1) 追加**,freeze 时一次性排序 O(n log n)。延迟排序的代价在主写路径之外。

**2. 锁外预编码**

WAL 记录在**获取写锁之前**完成序列化和 checksum 计算。临界区内只做 buffer 追加 + memtable 插入。

**3. 五级读取级联**

点查穿透路径:MemTable → BlockMetaIndex(二维: 按key + 按时间) → BloomFilter → block 内二分 → LZ4/Zstd 解压。每级都是低成本的过滤。

**4. 按 key 粒度的 Bloom 过滤器**

时间序列场景下,一个 key 可能对应上千个时间戳。传统按 (key, ts) 建 bloom 命中率低,FlowDB 按唯一 key 建 bloom,效果显著提升。

**5. 双模压缩**

flush 用 LZ4(写路径,要快),compaction 用 Zstd(后台,要省)。

**6. 复合索引的类型标记编码**

索引值编码时带上类型 tag(数字 vs 字符串 vs 字节),保证 Lexicographic 排序正确,支持混合字段范围查询。

### JsonDB 的存储布局

JsonDB 和引擎共享 keyspace,通过 key 前缀区分:

```
0x01 {store} \x00 {pk}                  → JSON 文档
0x02 {store} \x00 {index} \x00 {val} \x00 {pk} → 索引条目
0x03 {store}                              → Schema (StoreDef)
0x04 {store}                              → 自增计数器
```

事务实现采用 **OCC + MVCC**——写入缓冲在 HashMap,commit 时通过 `write_internal` 单次原子提交,唯一约束在提交时校验,drop 自动回滚。

---

## 三、性能优势

### 与 RocksDB 的对比

M 系列芯片, 100K records, 128B value:

| 操作 | FlowDB | RocksDB | 倍数 |
|---|---|---|---|
| 顺序写 | 4.5M ops/s | 3.1M ops/s | **1.42x** |
| 8线程并发写 | 9.4M ops/s | 4.7M ops/s | **2.02x** |
| 点查询 | 6.0M ops/s | 549K ops/s | **10.95x** |
| 前缀扫描 ~200条 | 72K ops/s | 11K ops/s | **6.39x** |
| 全表扫描 200K条 | 65 ops/s | 40 ops/s | **1.63x** |

### JsonDB 文档层性能

| 操作 | 吞吐 |
|---|---|
| 单文档写入 | ~121 docs/s |
| 批量写入 (100/批) | ~7,057 docs/s |
| 主键点查 | ~244,741 ops/s |
| 索引等值查找 | ~9,402 queries/s |

(写入瓶颈在 fsync,可用 SyncMode::IntervalMs 或批量提升)

### 为什么点查比 RocksDB 快 11 倍?

1. **Vec memtable** — 读路径上活跃数据无 BTreeMap 遍历开销
2. **Key 级 bloom** — 命中率远高于 (key, ts) 级
3. **无 CGo/CXX 跨语言开销** — 纯 Rust 调用,无需 FFI
4. **配置极简** — 不做 RocksDB 那种上百项调优,默认就快
5. **BlockMetaIndex 二维索引** — 按 key 和按时间双索引加速

---

## 四、适用场景与局限

### 适合
- 需要嵌入式存储的 Rust 应用(边缘计算、IoT、桌面 App)
- 对二进制体积敏感的场景(~680KB)
- 需要 JSON 文档 + 索引但不想引入 MongoDB / Couchbase
- 需要确定性性能,不想被 GC/异步运行时干扰

### 局限
- **单进程**,无网络层,无集群
- **无预写日志的事务性靠 fsync**,高频小事务 fsync 是瓶颈(批量可解)
- 项目较新(v0.6.0),生态尚在建设

---

## 五、快速上手

```toml
[dependencies]
flowdb = "0.6"
```

```rust
use flowdb::{Engine, Config, JsonDB, QueryBuilder};
use serde_json::{json, Value};

let engine = Engine::open(Config::new("/tmp/flowdb_demo"))?;
let db = JsonDB::open(engine)?;

db.create_object_store("notes", "id")?;
db.create_index("notes", "title", false)?;

db.put("notes", json!({"id": "1", "title": "Hello RustCC", "tags": ["rust", "database"]}))?;

let result = QueryBuilder::new("notes")
    .where_eq("title", "Hello RustCC")
    .collect::<Value>(&db)?;

println!("{:?}", result);
```

---

## 写在最后

FlowDB 给我的启发是:**做减法也是一种工程能力**。砍掉 Server 层、砍掉异步、砍掉复杂的配置选项,换来的是极简的 API、可预测的性能、以及真正「嵌入」的轻盈体态。

如果你在做一个需要嵌入式存储的 Rust 项目,或者对 LSM 引擎的 Rust 实现感兴趣,不妨试试 FlowDB。

**GitHub:** https://github.com/restsend/flowdb
**crates.io:** `flowdb`

欢迎 issue / PR / 讨论。