1 个不稳定版本
0.2.0 | 2019 年 9 月 15 日 |
---|
#166 在 数据库实现
61KB
1.5K SLoC
Binstore
Binstore 是用 Rust 编写的简单键值存储。这意味着 binstore 不处理序列化/反序列化。它所做的只是以缓存友好和紧凑的文件格式存储键值元素。目前,这个项目主要是为了娱乐,但希望将来能够演变成可以使用的工具。
文件格式
头部
字段名 | 描述 | 类型 |
---|---|---|
magic | 魔数 | u32 |
version | 版本号 | u8 |
timestamp | 创建时间戳 | i64 |
si_base_offset | 稀疏索引开始的位置 | u64 |
di_base_offset | 密集索引开始的位置 | u64 |
data_base_offset | 压缩数据开始的位置 | u64 |
num_entries | 文件中条目数 | u64 |
稀疏索引
键 | DI 偏移 |
---|---|
h_0000 | di_off_1 |
h_1000 | di_off_2 |
h_2000 | di_off_3 |
... | ... |
h_xxxx | di_off_x |
密集索引
DI 偏移 | 键 | 数据偏移 |
---|---|---|
di_off_1 | h_0000 | data_off_1 |
h_0001 | data_off_2 | |
h_0013 | data_off_3 | |
... | ... | ... |
h_0988 | data_off_4 | |
di_off_2 | h_1000 | data_off_5 |
h_1003 | data_off_6 | |
... | ... | ... |
di_off_x | h_xxxx | data_off_x |
数据
数据偏移 | 数据 |
---|---|
data_off_1 | LZ4_1 |
data_off_2 | LZ4_2 |
data_off_3 | LZ4_3 |
... | ... |
data_off_x | LZ4_x |
说明
binstore 文件分为四个部分
- 头部。头部帮助我们识别 binstore 文件(通过魔数),它们允许我们知道当前 binstore 引擎是否可以读取给定的 binstore 文件(版本号),以及 binstore 文件是在何时创建的。我们还存储其他部分的偏移量和存储在此数据文件中的条目数。
- 稀疏索引。不超过 1-2 MB 的索引;稀疏索引用于在密集索引中跳转到我们正在查找的键大致所在的位置。稀疏索引对于避免全扫描至关重要。
- 密集索引。在密集索引中,存在一个从键(以下将解释键)到存储与该键相关联的
Value
集合的文件偏移量的映射。密集索引条目大小固定且按其键排序;这允许二分查找。 - 数据。这是实际存储
Value
的地方。为了节省空间,我们使用LZ4压缩算法。
示例
查询
use std::iter::FromIterator;
use std::collections::{BTreeMap, BTreeSet};
use tempfile::NamedTempFile;
use binstore::bucket::*;
fn main() {
let mut bmap = BTreeMap::new();
for key in 0 .. 100 {
bmap.insert(key as u64, BTreeSet::from_iter(0 .. (key as u128)));
}
let tmp = NamedTempFile::new().unwrap();
create(tmp.path(), &bmap).expect("create");
{
let bucket = Bucket::open(tmp.path()).expect("open");
let mut bucket = bucket.check_headers().expect("check_headers");
let si = bucket.read_sparse_index().expect("sparse index");
for (key, actual_values) in &bmap {
let (offset_1, offset_2) = si.try_get(*key).expect("try_get");
let values = bucket.try_get(*key, offset_1, offset_2)
.expect("try_get (1)")
.expect("try_get (2)");
assert_eq!(actual_values, &values);
}
}
}
合并
use std::iter::FromIterator;
use std::collections::{BTreeMap, BTreeSet};
use tempfile::NamedTempFile;
use binstore::bucket::*;
fn main() {
let mut bmap1 = BTreeMap::new();
for key in 0 .. 100 {
bmap1.insert(key as u64, BTreeSet::from_iter(0 .. (key as u128)));
}
let mut bmap2 = BTreeMap::new();
for key in 0 .. 200 {
bmap2.insert(key as u64, BTreeSet::from_iter(0 .. (key as u128)));
}
let tmp1 = NamedTempFile::new().unwrap();
let tmp2 = NamedTempFile::new().unwrap();
let merged_file = NamedTempFile::new().unwrap();
create(tmp1.path(), &bmap1).unwrap();
create(tmp2.path(), &bmap2).unwrap();
merge(tmp1.path(), tmp2.path(), merged_file.path()).unwrap();
}
将来将在examples/
中添加更多示例。
依赖项
~6–16MB
~193K SLoC