6个版本
0.0.14 | 2024年7月23日 |
---|---|
0.0.13 | 2024年7月22日 |
#43 in 数据库实现
642 每月下载量
465KB
7.5K SLoC
VelarixDB是一个基于LSM的存储引擎,旨在显著减少IO放大,从而提高存储设备的性能和耐用性。
简介
VelarixDB:设计用于减少IO放大
VelarixDB是一个持续进行中的项目(非生产就绪),旨在优化加载时间和压缩过程中的数据移动。受到WiscKey论文的启发,WiscKey:在SSD意识存储中将键与值分开,velarixdb旨在在传统键值存储中显著提高性能。
问题
在LevelDB或RocksDB的压缩过程中,在最坏的情况下,需要读取、排序和重写多达10个SSTable文件,因为不允许在所有从第1级开始的sstables中键重叠。假设在合并一个级别的SSTables之后,下一个级别超过了其阈值,压缩可以从第0级级联到第6级,这意味着整体写放大可以达到50(忽略第一个压缩级别)。[参考资料 -> 官方LevelDB压缩过程文档]。这种重复的数据移动会导致SSD的磨损加剧,由于写周期数高而缩短其寿命。目标是尽量减少压缩过程中移动的数据量,从而减少重写的数据量,并延长设备的寿命。
解决方案
为了解决这个问题,我们关注一个键是否已被删除或更新。在压缩过程中包含值(通常比键大)是不必要的,这会放大读取和写入的数据量。因此,我们将键和值分开存储。具体来说,我们使用32位整数将值偏移量映射到键上。
这种方法减少了压缩过程中读取、写入和移动的数据量,从而提高了性能并减少了存储设备(尤其是SSD)的磨损。通过最小化数据移动,我们不仅提高了数据库的效率,还显著延长了底层存储硬件的寿命。
性能优势
根据WiscKey论文中提供的基准测试,实现可以比LevelDB和RocksDB
- 2.5倍到111倍提高数据库加载性能
- 1.6倍到14倍提高随机查找性能
设计用于异步运行时
基于操作系统的内核级别异步IO的介绍和效率,例如Linux内核的io_uring
,VelarixDB被设计为异步运行时。在这种情况下,是Tokio运行时。Tokio允许进行高效和可扩展的异步操作,充分利用了现代的多核处理器。坦白说,大多数操作系统文件系统目前还没有提供异步API,但Tokio使用线程池来卸载阻塞的文件系统操作。这意味着尽管文件系统操作本身在操作系统级别是阻塞的,但Tokio可以处理它们而不阻塞主异步任务执行器。
未来Tokio可能会采用io_uring
免责声明
请注意,velarixdb仍在开发中,尚未准备好投入生产。
基本功能
- 原子操作:Put、Get、Delete和Update
- 100%安全稳定的Rust
- 将键与值分离,减少压缩过程中移动的数据量(即,减少IO放大)
- 垃圾回收器
- 使用Crossbeam SkipMap的无锁memtable(无
Mutex
) - Tokio运行时,用于高效线程管理
- 布隆过滤器,用于快速内存中键搜索
- 使用Value Log进行崩溃恢复
- 索引,用于改进排序字符串表(SSTs)上的搜索
- 键范围,用于存储SST中的最大和最小键
- 尺寸分层压缩策略(STCS)
待办事项
- 快照隔离
- 块缓存
- 批量写入
- 范围查询
- Snappy压缩
- 值缓冲区,以保持值在内存中,并仅批量刷新以减少IO(正在研究)
- 校验和,用于检测数据损坏
- 分层压缩(LCS)、时间窗口压缩(TCS)和统一压缩(UCS)
- 监控模块,用于持续监控和生成报告
不是
- 独立的服务器
- 关系数据库
- 宽列数据库:它没有列的概念
约束
- 键限制为65,536字节,值限制为2^32字节。更大的键和值会对性能产生更大的影响。
- 类似于任何典型的键值存储,键按字典顺序存储。如果您存储整数键(例如,时间序列数据),请使用大端形式以保持局部性。
基本用法
cargo add velarixdb
use velarixdb::db::DataStore;
# use tempfile::tempdir;
#[tokio::main]
async fn main() {
let root = tempdir().unwrap();
let path = root.path().join("velarix");
let mut store = DataStore::open("big_tech", path).await.unwrap(); // handle IO error
store.put("apple", "tim cook").await;
store.put("google", "sundar pichai").await;
store.put("nvidia", "jensen huang").await;
store.put("microsoft", "satya nadella").await;
store.put("meta", "mark zuckerberg").await;
store.put("openai", "sam altman").await;
let entry1 = store.get("apple").await.unwrap(); // Handle error
let entry2 = store.get("google").await.unwrap();
let entry3 = store.get("nvidia").await.unwrap();
let entry4 = store.get("microsoft").await.unwrap();
let entry5 = store.get("meta").await.unwrap();
let entry6 = store.get("openai").await.unwrap();
let entry7 = store.get("***not_found_key**").await.unwrap();
assert_eq!(std::str::from_utf8(&entry1.unwrap().val).unwrap(), "tim cook");
assert_eq!(std::str::from_utf8(&entry2.unwrap().val).unwrap(), "sundar pichai");
assert_eq!(std::str::from_utf8(&entry3.unwrap().val).unwrap(), "jensen huang");
assert_eq!(std::str::from_utf8(&entry4.unwrap().val).unwrap(), "satya nadella");
assert_eq!(std::str::from_utf8(&entry5.unwrap().val).unwrap(), "mark zuckerberg");
assert_eq!(std::str::from_utf8(&entry6.unwrap().val).unwrap(), "sam altman");
assert!(entry7.is_none());
// Remove an entry
store.delete("apple").await.unwrap();
// Update an entry
let success = store.update("microsoft", "elon musk").await;
assert!(success.is_ok());
}
存储JSON
use serde::{Deserialize, Serialize};
use serde_json;
use velarixdb::db::DataStore;
# use tempfile::tempdir;
#[tokio::main]
async fn main() {
let root = tempdir().unwrap();
let path = root.path().join("velarix");
let mut store = DataStore::open("big_tech", path).await.unwrap(); // handle IO error
#[derive(Serialize, Deserialize)]
struct BigTech {
name: String,
rank: i32,
}
let new_entry = BigTech {
name: String::from("Google"),
rank: 50,
};
let json_string = serde_json::to_string(&new_entry).unwrap();
let res = store.put("google", json_string).await;
assert!(res.is_ok());
let entry = store.get("google").await.unwrap().unwrap();
let entry_string = std::str::from_utf8(&entry.val).unwrap();
let big_tech: BigTech = serde_json::from_str(&entry_string).unwrap();
assert_eq!(big_tech.name, new_entry.name);
assert_eq!(big_tech.rank, new_entry.rank);
}
示例
有关实际示例,请参阅此处
依赖项
~12–25MB
~368K SLoC