TurboKV: 极速 Rust 键值存储

Hacker News Top 工具

摘要

TurboKV 是一款用 Rust 编写的快速、嵌入式键值数据库,具有原子批处理、有序范围扫描、可配置持久性和压缩功能,以实现高效的数据存储。

暂无内容
查看原文
查看缓存全文

缓存时间: 2026/08/29 03:32

kingroryg/turbokv 来源:https://github.com/kingroryg/turbokv 一个用 Rust 编写的快速嵌入式键值存储 GitHub (https://github.com/kingroryg/turbokv) 许可协议 Rust (https://www.rust-lang.org) TurboKV 是一个异步嵌入式键值数据库,支持原子批量操作、有序范围扫描、可配置的持久化、压缩和后台压缩。

安装

``console cargo add turbokv cargo add tokio –features full

或者直接添加依赖:
``toml
[dependencies]
turbokv = "0.6"
tokio = { version = "1", features = ["full"] }

TurboKV 持久化的布隆过滤器格式使用硬件 AES 指令。为 x86/x86_64 目标构建时,需设置 RUSTFLAGS="-C target-feature=+aes,+sse2";为 ARM/AArch64 目标构建时,需设置 RUSTFLAGS="-C target-feature=+aes,+neon"。如果二进制文件仅在相同 CPU 型号或特性超集的 CPU 上运行,你也可以使用 -C target-cpu=native。

快速开始

``rust use turbokv::{Db, DbOptions, WriteBatch};

#[tokio::main] async fn main() -> Result<(), Box> { let db = Db::open_with_options(“./my-database”, DbOptions::durable()).await?; db.insert(b“user:1“, b“Ada“).await?; assert_eq!(db.get(b“user:1“).await?, Some(b“Ada“.to_vec()));

let mut batch = WriteBatch::new();
batch.put(b"user:2", b"Grace");
batch.put(b"user:3", b"Linus");
batch.delete(b"user:1");
db.write_batch(&batch).await?;

for (key, value) in db.scan_prefix(b"user:").await? {
    println!(
        "{} = {}",
        String::from_utf8_lossy(&key),
        String::from_utf8_lossy(&value)
    );
}

db.close().await?;
Ok(())

}

可运行示例:
- [`basic`](examples/basic.rs):插入、获取、更新和删除
- [`batch_writes`](examples/batch_writes.rs):原子的 put 和 delete 操作
- [`range_queries`](examples/range_queries.rs):有序的范围扫描和前缀扫描
- [`concurrent`](examples/concurrent.rs):来自 Tokio 任务的共享访问
- [`persistence`](examples/persistence.rs):极端的 WAL 恢复
- [`configuration`](examples/configuration.rs):缓存、内存表和压缩选项

## API 分解

### 持久化预设
| 预设 | 确认边界 | 用例 |
|---|---|---|
| `DbOptions::fast()` | 内存可见性;无 WAL | 缓存和可重现的数据 |
| `DbOptions::durable()` | 追加到 WAL 而不进行每次写入同步 | 进程崩溃恢复;推荐默认设置 |
| `DbOptions::paranoid()` | WAL 组在返回前完成 `sync_all` | 最强模式,取决于文件系统/设备保证 |

一个打开的 `Db` 或 `Engine` 独占拥有其数据目录。使用 `close()` 或 `close_with_status()` 进行干净关闭;丢弃句柄不是干净的关闭契约。

### 数据库操作
键和值是通过 `AsRef<[u8]>` 提供的任意字节序列;字符串需要由调用者编码。变更 API 在返回前会复制其输入。点读和收集读返回拥有所有权的 `Vec` 值。空值是有效数据,与已删除的键不同。

#### 打开与配置
| API | 参数 | 结果与行为 |
|---|---|---|
| `Db::open(path)` | `path: AsRef<Path>` | 使用 `DbOptions::durable()` 打开或创建目录。打开的句柄独占该目录。 |
| `Db::open_with_options(path, options)` | 数据库路径和 `DbOptions` 值 | 使用显式的持久化、内存、缓存和压缩设置打开。拒绝矛盾设置,例如 `sync_writes = true` 同时禁用 WAL。 |
| `DbOptions::fast()` | 无 | 返回无 WAL 预设。 |
| `DbOptions::durable()` | 无 | 返回可进程崩溃恢复的 WAL 预设。 |
| `DbOptions::paranoid()` | 无 | 返回确认前同步预设。 |
| `options.with_compression(compression)` | `Compression` 变体 | 构建器风格的更新,返回修改后的选项。 |

所有预设均以 64 MiB 内存表、64 MiB 块缓存和 LZ4 压缩开始。它们的公共字段可以在打开前调整:
| `DbOptions` 字段 | 含义 |
|---|---|
| `wal_enabled: bool` | 将变更追加到 WAL。禁用它允许在成功刷新或关闭前发生进程崩溃导致的数据丢失。 |
| `sync_writes: bool` | 在确认每个变更组之前等待 WAL 同步屏障。需要 `wal_enabled`。 |
| `memtable_size: usize` | 触发内存表轮换和后台刷新的大约内存字节阈值。 |
| `block_cache_size: usize` | 解压缩 SSTable 块缓存预算(字节)。设置为 `0` 以禁用缓存。 |
| `compression: Compression` | 新写入数据的 SSTable 压缩:`Lz4`、`Snappy`、`Zstd` 或 `None`。现有表保留其编码格式。 |

#### 点操作、批量操作和批次操作
| API | 参数 | 返回值与语义 |
|---|---|---|
| `insert(key, value)` | 字节类型的键和值 | `Result<()>`。插入或替换键。在成功前达到所选持久化边界。 |
| `insert_many(entries)` | 任何 `(key, value)` 对的迭代器 | `Result<()>`。复制完整迭代器并按顺序应用条目;最后一个重复键的值生效。这是一个批量 API,不是一次原子可见性转换。 |
| `get(key)` | 字节类型的键 | `Result<Option<Vec<u8>>>`。对于缺失或已删除的键返回 `None`,对于存储的空值返回 `Some(Vec::new())`。 |
| `remove(key)` | 字节类型的键 | `Result<()>`。写入墓碑标记;允许删除缺失的键。 |
| `contains_key(key)` | 字节类型的键 | `Result<bool>`。解析与 `get` 相同的状态,目前会产生其值的分配。 |
| `write_batch(batch)` | `&WriteBatch` | `Result<()>`。原子性发布所有操作;读者看到的是批次之前的状态或完整的批次。对于重复键,最后一个操作的值生效。 |

启用 WAL 时,单个记录或完整批次必须适应 WAL 的 `u32` 负载长度。失败或已取消的变更可能已经到达 WAL;在重试非幂等操作前,请检查键或重新打开。

`WriteBatch` 拥有每个键和值的副本:
| API | 参数 | 效果 |
|---|---|---|
| `WriteBatch::new()` | 无 | 创建一个空批次。 |
| `WriteBatch::with_capacity(capacity)` | 预期的操作数 | 预分配操作槽,但不分配键或值字节。 |
| `batch.put(key, value)` | 字节类型的键和值 | 追加一个拥有的 put 操作。 |
| `batch.delete(key)` | 字节类型的键 | 追加一个拥有的 delete 操作。 |
| `batch.ops()` | 无 | 借用有序的 `&[BatchOp]` 操作列表。 |
| `batch.len()` / `batch.is_empty()` | 无 | 报告当前操作计数。 |
| `batch.clear()` | 无 | 移除所有操作,同时保留批次分配以供重用。 |

#### 范围扫描和前缀扫描
键按原始字节词法排序。每次扫描捕获一个一致的点时间视图。创建一个扫描可能会冻结一个非空的活动内存表,因此频繁的小扫描可能会增加后续的刷新工作。

| API | 参数 | 返回值与分配 |
|---|---|---|
| `range(start, end)` | 包含的开始键和排除的结束键 | `Result<Iter, DbError>`,`Vec`;急切地分配每个返回的键和值。 |
| `scan_prefix(prefix)` | 字节前缀;空前缀匹配所有内容 | 急切地按顺序收集所有匹配的键/值对。 |
| `range_iter(start, end)` | 相同的 `[start, end)` 边界 | 创建一个 `RangeIter`。迭代器项是 `Result`,因为在推进时可能发现损坏。 |
| `scan_prefix_iter(prefix)` | 字节前缀 | 创建一个 `PrefixIter`,是同一流式实现的别名。 |

推进流式迭代器是同步的,可能执行 mmap 读取、校验和验证、解压缩和缓存锁定。及时丢弃它:迭代器固定其快照读取器和数据库目录的所有权。

| 迭代器或保护 API | 参数 | 结果 |
|---|---|---|
| `iter.count()` | 无 | 消费迭代器并返回 `Result<usize, ScanError>`。 |
| `iter.keys()` | 无 | 消费迭代器并收集拥有的键,而不实例化内存表的值。 |
| `iter.collect_pairs()` | 无 | 消费迭代器并收集拥有的键/值对。 |
| `iter.paginate(offset, limit)` | 要跳过的条目数和要产生的最大条目数 | 返回一个惰性迭代器;跳过的条目会被遍历但它们的内存表值不会被复制。 |
| `guard.key()` | 无 | 借用键而不加载值。 |
| `guard.value()` / `guard.value_len()` | 无 | 借用值,或报告其长度;内存表的值仅在首次请求 `value()` 时复制。 |
| `guard.into_pair()` / `into_key()` / `into_value()` | 无 | 消费保护并返回请求的拥有的字节。 |

#### 持久化、维护和统计
| API | 参数 | 返回值与开销 |
|---|---|---|
| `flush()` | 无 | `Result<()>`。排空待处理的写入,安装 SSTable 和清单,同步 WAL,并回收符合条件的 WAL 段。同时启动的写入可能需要后续刷新。 |
| `compact()` | 无 | `Result<CompactionReport, DbError>`。排空捕获的压缩范围并报告实际文件、字节数、耗时、回收的墓碑标记以及工作是否仍在继续。 |
| `status()` | 无 | 低成本的 `DatabaseStatus` 快照,包含维护失败、重试和写入反压信息。 |
| `logical_stats()` | 无 | `Result<LogicalStats, DbError>` 用于唯一活动键和字节的精确统计。它扫描物理版本,可能执行 I/O。 |
| `physical_stats()` | 无 | 低成本的 `PhysicalStats` 仪表盘和进程生命周期计数器,用于 WAL、内存表、SSTable、缓存、停顿和放大。 |
| `stats()` | 无 | 已弃用的混合物理计数器,为源码兼容性保留。 |
| `close()` | 消费 `Db` | 刷新待处理的写入,停止维护,并在成功时释放所有权。丢弃 `Db` 不是干净关闭的保证。 |
| `close_with_status()` | 消费 `Db` | 结构化的关闭形式;区分存储错误与未解决的刷新或压缩健康问题。 |

大多数数据库方法返回 `DbError`。流式迭代器创建返回 `DbError`,而后续发现的故障作为 `ScanError` 产生。更底层的 `Engine` 和组件配置类型支持高级 API;它们的完整字段和方法契约位于 [crate 文档 (https://docs.rs/turbokv)]。

## 基准测试
基准测试使用 TurboKV 0.6.0、fjall 2.11.2 和 redb 2.6.3 在持久化模式下进行三次重复测试。吞吐量为每秒确认的键数;数值越高越好。

| 工作负载 | 模式 | TurboKV ops/s | fjall ops/s | redb ops/s | TurboKV / fjall |
|---|---|---:|---:|---:|---:|
| 顺序填充(1 个键/事务) | Durable | 1,407,678 | 485,252 | 1,397 (macOS barrier/txn) | 2.901× |
| 随机填充(1 个键/事务) | Durable | 834,137 | 456,924 | 1,549 (macOS barrier/txn) | 1.826× |
| 覆盖写入(1 个键/事务) | Durable | 853,083 | 446,733 | 1,516 (macOS barrier/txn) | 1.910× |
| 顺序批处理(100 个键/事务) | Durable | 2,272,259 | 511,600 | 80,197 | 4.441× |
| 顺序批处理(1,000 个键/事务) | Durable | 2,333,582 | 572,671 | 134,636 | 4.075× |

测试协议:200,000 个确定性的 20 字节键,400 字节值(逻辑输入 84 MB,超过 64 MiB 内存表),单个调用者,原子批量(如表所示),禁用压缩和块缓存,未清除操作系统页缓存。

redb 2.6.3 的 `Durability::Eventual` 对每个事务执行 macOS `F_BARRIERFSYNC`,而 TurboKV 和 fjall 的 Durable 模式在进程可恢复的操作系统缓存边界处停止。批量处理分摊了 redb 的固定屏障开销;因此其单键行在架构上是上下文不同的,而非类似耐久性的声明。跨引擎的稳定耗时未进行比较。

测试于 2026-08-28,使用 Apple M4 (`Mac16,1`)、32 GiB 内存、macOS 15.3.2 (24D81)、APFS 文件系统和 rustc 1.88.0。精确的原始重复次数、延迟百分位数、离散度、依赖版本、字节统计和放大信息位于 [JSON 工件](benchmarks/results/apple-m4-macos-15.3.2/durability-baseline-ingest-current.json) 及其 [文本报告](benchmarks/results/apple-m4-macos-15.3.2/durability-baseline-ingest-current.txt)。完整的方法和重运行命令位于 [`benchmarks/README.md`](benchmarks/README.md)。

相似文章

RyanCodrai/turbovec

GitHub Trending (daily)

turbovec 是一个基于 Rust 的向量索引,带有 Python 绑定,实现了谷歌的 TurboQuant 算法,提供高效的向量搜索,支持在线摄入,性能优于 FAISS,并具备过滤搜索能力。

开源 InfiniteKV:一种 KV 缓存,将旧 token 压缩为 104 字节的可搜索记录存储在内存或磁盘中,而非删除。Mistral-7B 从 token 76,747 处作答,超出其训练窗口 2.3 倍。附 Colab 演示

Reddit r/LocalLLaMA

InfiniteKV 是一种开源 KV 缓存技术,将旧 token 压缩为 104 字节的可搜索记录,存储在内存或磁盘中,使模型能够处理超出训练窗口的百万 token 上下文而无需丢弃数据。已验证可与 Mistral-7B 和 SmolLM2 配合使用。

@hasantoxr:向量数据库不再是云产品。它们正在变成 pip install。一个名为 turbovec 的新开源项目……

X AI KOLs Timeline

一个名为 turbovec 的开源项目在 GitHub 上获得了 1 万星标。它是一个基于 Rust、带有 Python 绑定的向量索引,使用谷歌研究的 TurboQuant 算法将嵌入压缩到接近理论香农极限,使得完全本地的 RAG(检索增强生成)成为可能——1000 万文档仅需 4 GB RAM,且搜索速度快于 FAISS。