一个轻量级的 Bitcask 键值存储引擎,使用 Rust 实现。
- 核心操作:
put、get、delete,O(1) 磁盘寻址 - 原子批量写入:
WriteBatch支持事务操作,保证 ACID 特性 - 迭代器支持: 前缀过滤、反向迭代、fold 操作
- 数据压缩: Merge 操作回收已删除/更新键的磁盘空间
- 多种索引类型:
- BTree(默认,内存索引)
- SkipList(内存索引,并发友好)
- B+Tree(持久化索引,基于 jammdb)
- 灵活的 I/O 方式:
- 标准文件 I/O
- 内存映射 I/O(mmap),加速启动
- 数据完整性: CRC32 校验确保日志记录完整
- 并发安全: 细粒度锁实现线程安全
- 进程安全: 文件锁防止多实例同时访问
┌─────────────────────────────────────────────────────────┐
│ Engine │
├─────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Index │ │ Active File │ │ Older Files │ │
│ │ (BTree/ │ │ (写入) │ │ (只读) │ │
│ │ SkipList/ │ │ │ │ │ │
│ │ B+Tree) │ │ │ │ │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │ │
│ └────────────────>│<─────────────────┘ │
│ Log Records │
│ (key, value, type, crc) │
└─────────────────────────────────────────────────────────┘
在 Cargo.toml 中添加依赖:
[dependencies]
bitcask = { path = "path/to/mini-bitcask" }use bitcask::{db::Engine, options::Options};
use bytes::Bytes;
fn main() {
// 使用默认配置打开存储引擎
let opts = Options::default();
let engine = Engine::open(opts).expect("failed to open engine");
// 写入数据
engine.put(Bytes::from("name"), Bytes::from("bitcask")).unwrap();
// 读取数据
let value = engine.get(Bytes::from("name")).unwrap();
println!("value: {:?}", String::from_utf8(value.to_vec()));
// 删除数据
engine.delete(Bytes::from("name")).unwrap();
}use bitcask::{db::Engine, options::{Options, WriteBatchOptions}};
use bytes::Bytes;
fn main() {
let engine = Engine::open(Options::default()).unwrap();
// 创建批量写入
let wb = engine.new_write_batch(WriteBatchOptions::default()).unwrap();
// 批量写入操作
wb.put(Bytes::from("key1"), Bytes::from("value1")).unwrap();
wb.put(Bytes::from("key2"), Bytes::from("value2")).unwrap();
wb.delete(Bytes::from("old_key")).unwrap();
// 原子提交
wb.commit().unwrap();
}use bitcask::{db::Engine, options::{Options, IteratorOptions}};
fn main() {
let engine = Engine::open(Options::default()).unwrap();
// 使用前缀过滤迭代
let iter_opts = IteratorOptions {
prefix: b"user:".to_vec(),
reverse: false,
};
let iter = engine.iter(iter_opts);
while let Some((key, value)) = iter.next() {
println!("{:?} -> {:?}", key, value);
}
// fold 操作
engine.fold(|key, value| {
println!("{:?}: {:?}", key, value);
true // 继续迭代
}).unwrap();
}use bitcask::options::{Options, IndexType, IOType};
use std::path::PathBuf;
let opts = Options {
dir_path: PathBuf::from("/data/bitcask"), // 数据库目录
data_file_size: 256 * 1024 * 1024, // 单个数据文件大小,256MB
sync_writes: false, // 每次写入是否持久化
bytes_per_sync: 0, // 累计写入多少字节后持久化(0 表示禁用)
index_type: IndexType::BTree, // 索引类型
mmap_at_startup: true, // 启动时使用 mmap 加速加载
data_file_merge_ratio: 0.5, // 可回收空间达到 50% 时触发 merge
};| 类型 | 存储位置 | 适用场景 |
|---|---|---|
BTree |
内存 | 通用场景,性能均衡 |
SkipList |
内存 | 高并发写入场景 |
BPlusTree |
磁盘 | 数据量大、内存有限场景 |
基于 Actix-web 的 HTTP API 服务。
cd bitcask-http
cargo runAPI 接口:
POST /bitcask/put- 存储键值对(JSON 请求体)GET /bitcask/get/{key}- 根据 key 获取 valueDELETE /bitcask/delete/{key}- 删除 keyGET /bitcask/listkeys- 列出所有 keyGET /bitcask/stat- 获取数据库统计信息
兼容 Redis 协议的服务,支持常用数据结构。
cd bitcask-redis
cargo run支持的命令:
- String:
SET、GET - Hash:
HSET - Set:
SADD - List:
LPUSH - Sorted Set:
ZADD
使用任意 Redis 客户端连接:
redis-cli -p 6380
> SET name bitcask
OK
> GET name
"bitcask"运行基准测试:
cargo benchmini-bitcask/
├── src/
│ ├── lib.rs # 库入口
│ ├── db.rs # 核心 Engine 实现
│ ├── options.rs # 配置选项
│ ├── batch.rs # WriteBatch 原子批量写入
│ ├── iterator.rs # 迭代器实现
│ ├── merge.rs # 数据压缩
│ ├── errors.rs # 错误类型
│ ├── data/ # 数据文件处理
│ │ ├── data_file.rs # 数据文件操作
│ │ └── log_record.rs# 日志记录编解码
│ ├── fio/ # 文件 I/O 抽象
│ │ ├── file_io.rs # 标准文件 I/O
│ │ └── mmap.rs # 内存映射 I/O
│ └── index/ # 索引实现
│ ├── btree.rs # BTree 索引
│ ├── skiplist.rs # SkipList 索引
│ └── bptree.rs # B+Tree 索引
├── bitcask-http/ # HTTP API 服务
├── bitcask-redis/ # Redis 兼容服务
├── examples/ # 使用示例
└── benches/ # 性能测试
感谢 RoseDB Labs 组织的开源项目和技术分享,本项目的实现参考了其设计思路。