4 个版本
0.0.4 | 2024年3月12日 |
---|---|
0.0.3 | 2024年3月7日 |
0.0.2 | 2023年9月23日 |
0.0.1 | 2023年9月16日 |
#1241 在 数据库接口
145KB
4K SLoC
适用于移动客户端(例如,iOS、Android)的轻量级嵌入式键值数据库,使用Rust编写。
ThetaDB
适用于对移动客户端具有“高读、低写”需求的场景,它使用B+树作为索引管理的基础层。
受到Go的BoltDB的启发,ThetaDB使用mmap
,依靠操作系统来同步内存和数据库文件。ThetaDB还实现了shadow paging
,以保证事务的原子性和持久性,防止数据丢失或损坏数据库的内部结构。
打开数据库
使用以下方式在指定路径打开数据库。如果数据库文件不存在,ThetaDB将自动创建和初始化它。
use thetadb::{Options, ThetaDB, Result};
let path = "target/db.theta";
// The simplest way to open with default `Options`:
let db = ThetaDB::open(path)?;
// Open with `Options`:
let db = Options::new()
.force_sync(true)
.mempool_capacity(8)
.open(path)?;
ThetaDB将在数据库实例被销毁时自动关闭。
获取、插入、更新、删除
// Insert a new key-value pair into database.
db.put(b"foo", b"foo")?;
// Check if the database contains a given key.
assert!(db.contains(b"foo")?);
assert!(!db.contains(b"unknown")?);
// Get the value associated with a given key.
assert_eq!(
db.get(b"foo")?,
Some(b"foo".to_vec())
);
assert_eq!(
db.get(b"unknown")?,
None
);
// Update an existing value associated with a given key.
db.put(b"foo", b"bar")?;
assert_eq!(
db.get(b"foo")?,
Some(b"bar".to_vec())
);
// Delete an existing key-value pair from database.
db.delete(b"foo")?;
assert!(!db.contains(b"foo")?);
assert_eq!(
db.get(b"foo")?,
None
);
事务
ThetaDB有两种事务:读-只事务
和读-写事务
。读-只事务允许只读访问,而读-写事务允许修改。
ThetaDB允许同时进行多个读-只事务,但一次最多允许一个读-写事务。当读-写事务提交时,它对数据库拥有独占访问权,直到提交完成,此时其他尝试访问数据库的事务将被阻塞。你可以将这种情况视为读写锁的共享访问
和独占访问
。
读-只事务
// Start a read-only transaction.
let tx = db.begin_tx()?;
// Then perform read-only access.
_ = tx.contains(b"foo")?;
_ = tx.get(b"foo")?;
// Or you can perform a read-only transaction using closure,
// with `view` method:
db.view(|tx| {
_ = tx.contains(b"foo")?;
_ = tx.get(b"foo")?;
Ok(())
})?;
读-写事务
ThetaDB的读-写事务被设计为自动回滚,因此除非你显式调用commit
方法,否则对事务所做的任何更改都将被丢弃。
或者,你可以使用闭包执行读-写事务,如果没有任何错误发生,那么在闭包调用后,事务将自动提交。
// Start a read-write transaction.
let mut tx = db.begin_tx_mut()?;
// Then perform read-write access.
tx.put(b"hello", b"world")?;
_ = tx.get(b"hello")?;
// Finally, commit the transaction.
tx.commit()?;
// Or you can perform a read-write transaction using closure,
// with `update` method:
db.update(|tx| {
tx.put(b"hello", b"world")?;
_ = tx.get(b"hello")?;
Ok(())
})?;
注意
❗️ 事务实例不可发送,这意味着将它们发送到另一个线程是不安全的。Rust利用Ownership
系统和Send
和Sync
特性来自动执行要求,而Swift则需要我们手动确保这些保证。
❗️ 只读事务和读写事务不能重叠,否则将发生死锁。
😺 因此,ThetaDB建议,如果您想使用事务,请使用带有闭包参数的API(例如,view
,update
)。
游标
我们可以使用Cursor
自由遍历ThetaDB中的数据。
例如,我们可以像这样遍历ThetaDB中的所有键值对
// Forward traversal.
let mut cursor = db.first_cursor()?;
while let Some((key, value)) = cursor.key_value()? {
println!("{:?} => {:?}", key, value);
cursor.next()?;
}
// Backward traversal.
let mut cursor = db.last_cursor()?;
while let Some((key, value)) = cursor.key_value()? {
println!("{:?} => {:?}", key, value);
cursor.prev()?;
}
或者我们可以这样对ThetaDB进行范围查询
let mut cursor = db.cursor_from_key(b"C")?;
while let Some((key, value)) = cursor.key_value()? {
if key == b"G" {
break;
}
println!("{:?} => {:?}", key, value);
cursor.next()?;
}
注意
❗️ 游标也是一个事务(可以理解为只读事务),因此它也遵循上述提到的交易考虑事项。
依赖关系
~0.3–0.8MB
~19K SLoC