文章目录
项目介绍
类型 | 语言 | star | 简介 |
---|---|---|---|
sled | rust | 4.6k | 嵌入式数据库,全新设计,beta尚未稳定 |
LevelDB | c++ | 23.3k | google开源,快速键值存储库,LSM树(牺牲了部分读性能,用来大幅提高写性能。) |
skade/leveldb | rust | 118 | leveldb的rust封装,即通过rust调用leveldb c++ api,key仅支持i32
|
leveldb-rs | rust | 22 | 用rust重写leveldb,完全兼容 |
RocksDB | c++ | 19.4k | facebook开源,在LevelDB之上做了改进 |
rust-rocksdb | rust | 900 | rocksdb的rust封装,即通过rust调用rocksdb c++ api |
LMDB | c | 1.7k | 即Lightning Memory-Mapped Database Manager 闪电内存映射数据库管理器。是一个基于B+ tree的数据库管理库,使用mmap访问存储 |
lmdb-rs | rust | 91 | LMDB的rust封装,即通过rust调用lmdb c api |
TiKV | rust | 9k | 一个分布式 Key-Value store,采用 Raft 一致性协议保证数据的强一致性,以及稳定性,同时通过 Raft 的 Configuration Change 机制实现了系统的可扩展性,存储引擎使用了RocksDB |
skade/leveldb局限性太大,key仅支持i32。TiKV功能强大,其分布式特性我们这里暂时用不到。由于要在rust中使用,这里主要考虑sled、rust-rocksdb、leveldb-rs、lmdb-rs这四种实现。
sled
已知问题
- 如果
可靠性
是您的主要制约因素,请使用SQLite
。sled是beta。 - 如果
存储价格性能
是您的主要限制因素,请使用RocksDB
。sled有时会占用太多空间。 - 如果您有很少写入的多进程工作负载,请使用
LMDB
。底座设计用于长时间运行的高并发工作负载,例如有状态服务或更高级别的数据库。 - 还很年轻,暂时应该被认为是不稳定的。
- 磁盘格式将发生变化,在1.0.0发行版之前进行手动迁移!
如何使用
use sled::{Result,Config, Mode};
fn main() -> Result<()> {
//LowSpace在这种模式下,数据库将做出有利于使用更少空间而不是支持尽可能高的写入吞吐量的决策。此模式还将努力减少碎片,因此会更频繁地重写数据
//HighThroughput在这种模式下,数据库将尝试最大程度地提高写入吞吐量,同时潜在地使用更多的磁盘空间。
let db = Config::new()
.mode(Mode::HighThroughput)
.path("mydir")
.open()?;
let k = b"k".to_vec();
let v1 = b"v1".to_vec();
let v2 = b"v2".to_vec();
// set and get
db.insert(k.clone(), v1.clone())?;
assert_eq!(db.get(&k).unwrap().unwrap(), (v1));
// compare and swap
match db.compare_and_swap(k.clone(), Some(&v1), Some(v2.clone()))? {
Ok(()) => println!("it worked!"),
Err(sled::CompareAndSwapError { current: cur, proposed: _ }) => {
println!("the actual current value is {:?}", cur)
}
}
// scan forward
let mut iter = db.range(k.as_slice()..);
let (k1, v1) = iter.next().unwrap().unwrap();
assert_eq!(v1, v2);
assert_eq!(k1, k);
assert_eq!(iter.next(), None);
// deletion
db.remove(&k)?;
Ok(())
}
数据目录如下
mydir
├── DO_NOT_USE_THIS_DIRECTORY_FOR_ANYTHING
├── conf
├── db
├── heap
└── snap.0000000000000204
leveldb-rs
如何使用
use rusty_leveldb::{LdbIterator, Options, DB};
use std::env::args;
use std::io::{self, Write};
use std::iter::FromIterator;
fn get(db: &mut DB, k: &str) {
match db.get(k.as_bytes()) {
Some(v) => {
if let Ok(s) = String::from_utf8(v.clone()) {
println!("{} => {}", k, s);
} else {
println!("{} => {:?}", k, v);
}
}
None => println!("{} => <not found>", k),
}
}
fn put(db: &mut DB, k: &str, v: &str) {
db.put(k.as_bytes(), v.as_bytes()).unwrap();
db.flush().unwrap();
}
fn delete(db: &mut DB, k: &str) {
db.delete(k.as_bytes()).unwrap();
db.flush().unwrap();
}
fn iter(db: &mut DB) {
let mut it = db.new_iter().unwrap();
let (mut k, mut v) = (vec![], vec![]);
let mut out = io::BufWriter::new(io::stdout());
while it.advance() {
it.current(&mut k, &mut v);
out.write_all(&k).unwrap();
out.write_all(b" => ").unwrap();
out.write_all(&v).unwrap();
out.write_all(b"\n").unwrap();
}
}
fn compact(db: &mut DB, from: &str, to: &str) {
db.compact_range(from.as_bytes(), to.as_bytes()).unwrap();
}
fn main() {
let args = Vec::from_iter(args());
if args.len() < 2 {
panic!(
"Usage: {} [get|put/set|delete|iter|compact] [key|from] [val|to]",
args[0]
);
}
let mut opt = Options::default();
opt.reuse_logs = false;
opt.reuse_manifest = false;
opt.compression_type = rusty_leveldb::CompressionType::CompressionNone;
let mut db = DB::open("tooldb", opt).unwrap();
match args[1].as_str() {
"get" => {
if args.len() < 3 {
panic!("Usage: {} get key", args[0]);
}
get(&mut db, &args[2]);
}
"put" | "set" => {
if args.len() < 4 {
panic!("Usage: {} put key val", args[0]);
}
put(&mut db, &args[2], &args[3]);
}
"delete" => {
if args.len() < 3 {
panic!("Usage: {} delete key", args[0]);
}
delete(&mut db, &args[2]);
}
"iter" => iter(&mut db),
"compact" => {
if args.len() < 4 {
panic!("Usage: {} compact from to", args[0]);
}
compact(&mut db, &args[2], &args[3]);
}
_ => unimplemented!(),
}
}
rust-rocksdb
如何使用
use std::{mem, sync::Arc, thread, time::Duration};
use pretty_assertions::assert_eq;
use rocksdb::{
perf::get_memory_usage_stats, BlockBasedOptions, BottommostLevelCompaction, Cache,
CompactOptions, DBCompactionStyle, Env, Error, FifoCompactOptions, IteratorMode, Options,
PerfContext, PerfMetric, ReadOptions, SliceTransform, Snapshot, UniversalCompactOptions,
UniversalCompactionStopStyle, WriteBatch, DB,
};
use util::DBPath;
#[test]
fn external() {
let path = DBPath::new("_rust_rocksdb_externaltest");
{
let db = DB::open_default(&path).unwrap();
assert!(db.put(b"k1", b"v1111").is_ok());
let r: Result<Option<Vec<u8>>, Error> = db.get(b"k1");
assert_eq!(r.unwrap().unwrap(), b"v1111");
assert!(db.delete(b"k1").is_ok());
assert!(db.get(b"k1").unwrap().is_none());
}
}
lmdb-rs
如何使用
use lmdb::{EnvBuilder, DbFlags};
fn main() {
let env = EnvBuilder::new().open("test-lmdb", 0o777).unwrap();
let db_handle = env.get_default_db(DbFlags::empty()).unwrap();
let txn = env.new_transaction().unwrap();
{
let db = txn.bind(&db_handle); // get a database bound to this transaction
let pairs = vec![("Albert", "Einstein",),
("Joe", "Smith",),
("Jack", "Daniels")];
for &(name, surname) in pairs.iter() {
db.set(&surname, &name).unwrap();
}
}
// Note: `commit` is choosen to be explicit as
// in case of failure it is responsibility of
// the client to handle the error
match txn.commit() {
Err(_) => panic!("failed to commit!"),
Ok(_) => ()
}
let reader = env.get_reader().unwrap();
let db = reader.bind(&db_handle);
let name = db.get::<&str>(&"Smith").unwrap();
println!("It's {} Smith", name);
}
功能对比
类别 | sled | leveldb-rs | rust-rocksdb | lmdb-rs |
---|---|---|---|---|
事务(批量写) | 支持 | 支持 | 支持 | 支持 |
压缩算法 | zstd | snappy | snappy、lz4、zstd、zlib、bzip2 | \ |
数据结构 | lsm写入,b+tree读取 | lsm | lsm | B+ tree |
rust异步/多线程 | 支持 | 不支持 | 支持 | 支持 |
性能测试对比
测试数据仅供参考
macbook pro( Core i7 /16G),使用rust性能测试框架criterion,时间取中位值
插入不同的长度的key/value
类型(单位bytes) | sled | dermesser/leveldb-rs | rust-rocksdb | lmdb-rs |
---|---|---|---|---|
key/value 10/128 | 6.3429 us | 7.4070 us | 25.551 us | 1.2887 us |
key/value 10/1024 | 12.612 us | 21.162 us | 35.959 us | 12.389 us |
key/value 10/4096 | 7.7579 ms | 67.716 us | 83.034 us | 30.920 us |
key/value 10/8192 | 7.6942 ms | 110.58 us | 126.85 us | 31.935 us |
key/value 10/262144 | 36.024 ms | 2.8490 ms | 2.6729 ms | 659.74 us |
key/value 128/128 | 6.4928 us | 10.405 us | 25.168 us | 1.9674 us |
key/value 128/1024 | 14.181 us | 23.435 us | 36.920 us | 9.4915 us |
key/value 128/4096 | 7.6899 ms | 71.338 us | 90.285 us | 20.711 us |
key/value 128/8192 | 7.8685 ms | 122.77 us | 125.80 us | 32.952 us |
key/value 128/262144 | 36.234 ms | 2.8922 ms | 2.4643 ms | 761.65 us |
key/value 256/128 | 6.7232 us | 12.599 us | 28.777 us | 2.4083 us |
key/value 256/1024 | 13.955 us | 25.972 us | 39.344 us | 7.7123 us |
key/value 256/4096 | 7.6926 ms | 74.074 us | 88.930 us | 29.819 us |
key/value 256/8192 | 7.8424 ms | 116.25 us | 83.962 us | 31.714 us |
key/value 256/262144 | 36.820 ms | 2.8694 ms | 1.4018 ms | 650.39 us |
key/value 512/128 | 7.9187 us | 15.932 us | 16.031 us | 2.8804 us |
key/value 512/1024 | 2.6200 ms | 31.438 us | 20.967 us | 17.665 us |
key/value 512/4096 | 7.6336 ms | 73.891 us | 38.338 us | 35.919 us |
key/value 512/8192 | 7.7780 ms | 128.78 us | 53.688 us | 37.614 us |
key/value 512/262144 | 36.271 ms | 2.9039 ms | 1.1011 ms | 635.37 us |
lmdb MDB_MAXKEYSIZE值默认最大值为511,这里为了测试修改了源码默认值
monotonic insert/get/remove
key值固定,value为空
类型(单位条) | sled | dermesser/leveldb-rs | rust-rocksdb | lmdb-rs |
---|---|---|---|---|
inserts 1 | 661.11 ns | 2.0548 us | 6.0926 us | 840.68 ns |
get 1 | 630.05 ns | 4.0336 us | 1.3458 us | 899.93 ns |
remove 1 | 578.18 ns | 1.8852 us | 6.4212 us | 867.66 ns |
inserts 100w | 1.5521 s | 1.8899 s | 4.9668 s | 666.38 ms |
get 100w | 1.5316 s | 5.3495 s | 1.5867 s | 663.98 ms |
remove 100w | 1.1838 s | 1.8561 s | 4.8582 s | 255.59 ms |
inserts 1000w | 16.365 s | 19.965 s | 44.013 s | 6.9606 s |
random insert/get/remove
key值大小随机,1~65536bytes,value为空
类型 | sled | dermesser/leveldb-rs | rust-rocksdb | lmdb-rs |
---|---|---|---|---|
inserts | 1.5588 us | 3.0858 us | 7.2210 us | 464.96 ns |
get | 1.4756 us | 15.385 us | 2.1107 us | 369.62 ns |
remove | 1.1267 us | 2.7740 us | 7.1509 us | 23.588 ns |
其他性能测试报告参考
- http://www.lmdb.tech/bench/inmem/
- https://www.influxdata.com/blog/benchmarking-leveldb-vs-rocksdb-vs-hyperleveldb-vs-lmdb-performance-for-influxdb/