#key-value-database #ios #mobile #android #clients #embedded-database #transaction

thetadb

适用于移动客户端(例如,iOS、Android)的轻量级嵌入式键值数据库

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数据库接口

MIT 许可证

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系统和SendSync特性来自动执行要求,而Swift则需要我们手动确保这些保证。

❗️ 只读事务和读写事务不能重叠,否则将发生死锁。

😺 因此,ThetaDB建议,如果您想使用事务,请使用带有闭包参数的API(例如,viewupdate)。

游标

我们可以使用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