1个稳定版本
1.0.0 | 2022年12月2日 |
---|
#103 在 数据库实现
1MB
16K SLoC
RainDB
基于LSM树实现的持久化键值存储库
动机和目标
本项目更像是一个玩具项目和学习数据库内部结构的工具。因此,代码将被尽可能注释。我们希望通过结构化的代码、避免“优雅”的技巧以及冗余的文档来实现高可读性。设计文档、生成的API文档以及代码中都会详细阐述概念和推理。无论您最擅长通过阅读代码还是阅读文档来学习,信息通常会在多个区域重复,以便于跟随。
RainDB 不追求高性能,也不追求与LevelDB的二进制兼容性。尽管如此,这些仍将是美好的副作用😀。尽管不追求二进制兼容性,但行为在很大程度上是相同的(即直接移植),并使用LevelDB的相同测试用例来检查这种一致性。
高级设计
RainDB的存储引擎是一个LSM树,并且具有/将具有以下传统组件:
- Memtable - 初始为跳表,但将来可以用其他数据结构进行交换
- 表文件
- 写入前日志
如上所述,RainDB的LSM树是一个双层系统,包含内存中的memtable和可持久化到单个存储介质的表文件。表文件存储可以在磁盘上,但也提供了内存存储(通常用于测试目的)。
有关架构和设计的更多信息,请参阅文档文件夹。
使用方法
use raindb::{DbOptions, ReadOptions, WriteOptions, DB};
// Use the default options to create a database that uses disk to store table files
let options = DbOptions {
create_if_missing: true,
..DbOptions::default()
};
// Optionally handle any errors that can occur when opening the database
let db = DB::open(options).unwrap();
db.put(
WriteOptions::default(),
"some_key".into(),
"some_value".into(),
).unwrap();
let read_result = db.get(ReadOptions::default(), "some_key".as_bytes()).unwrap();
使用内存存储
use raindb::{DbOptions, DB};
use raindb::fs::{FileSystem, InMemoryFileSystem};
let options = DbOptions {
create_if_missing: true,
..DbOptions::with_memory_env()
};
// OR
let mem_fs: Arc<dyn FileSystem> = Arc::new(InMemoryFileSystem::new());
let options = DbOptions {
filesystem_provider: Arc::clone(&mem_fs),
create_if_missing: true,
..DbOptions::default()
};
let db = DB::open(options).unwrap();
其他使用示例
示例文件夹包含了一些如何将RainDB嵌入应用程序中的示例。我通常不喜欢查阅源代码来获取示例,但db/db_test.rs
模块包含了许多公共API的使用示例。
血统
本项目大量借鉴了LevelDB的现有工作。对创造者将如此多的设计文档与代码本身一同提供的感谢。由于本项目在LevelDB的基础上进行了大量工作,一些选项和文档直接从中获取。
灵感也来源于LevelDB的Go端口和dermesser的Rust端口。
在文档注释中,如果做出了与LevelDB不同的策略性决策,将会有一个Legacy
标题。这可以包括名称更改,以便在RainDB和LevelDB之间仍可以找到对应关系。
许多测试也是从LevelDB中提取的,以确保行为的一致性。
其他艺术
未来想法
- 与Raft用于领导者选举的分布式存储一起使用
- 添加数据分片
参考文献
- 除了文档文件夹外,还可以在我的博客上找到各种组件的更多笔记和参考文献
- Alex Petrov的《数据库内部》
- Oren Eini - 博客系列 - 审查LevelDB
- MrCroxx - 博客系列
- 这是中文的,但谷歌翻译在这里做得很好。只是简要浏览了压缩文章,但它完成得非常好,似乎是理解LevelDB的极好学习资源。
免责声明
这不是一个官方支持的谷歌产品。
依赖关系
~4–13MB
~152K SLoC