Rust分布式数据库插件holochain_sqlite的使用:支持SQLite存储的Holochain数据持久化与高效查询

Rust分布式数据库插件holochain_sqlite的使用:支持SQLite存储的Holochain数据持久化与高效查询

holochain_sqlite是一个用于Holochain持久化状态的构建模块,当前版本为0.0.1。

历史背景

最初这个crate是为LMDB设计的,在稳定后我们完全重构了它,改用SQLite作为键值存储。这是为将来更智能和全面地使用SQLite做准备,通过精心选择的索引和查询来优化性能。但目前,这个crate的结构只能在这个重大重构的背景下理解。

后端:SQLite

持久化实现专门针对SQLite后端。未来如果需要更换后端或支持IndexedDb等,我们将适当泛化接口以兼顾两者。

缓冲存储(Buffered Stores)

持久化Holochain状态的基本单元是BufferedStore。这个接口将三部分组合在一起:

  1. SQLite数据库引用
  2. 只读事务引用(与其他存储共享)
  3. “暂存空间” - 用于暂存写操作的HashMap(缓冲区)

暂存空间的目的是避免需要打开读写事务(同一时间只能有一个)。通过暂存空间缓冲区,存储引用可以存在更长时间,累积更改,然后在短暂的读写事务中一次性刷新缓冲区。

BufferedStore包含对只读事务的引用,这意味着存储作为构造时持久数据的快照。底层持久化的更改不会反映在这个BufferedStore中。

强类型

所有BufferedStore都是强类型的。所有键和值必须可序列化/反序列化,因此在将项目放入存储时自动进行序列化/反序列化。因此,源链CAS被分成两个独立的数据库:一个用于Entries,另一个用于Actions。

工作区(Workspaces)

Holochain代码不直接处理单个数据存储。BufferedStores总是分组到Workspace中,Workspace是为特定目的组合的存储集合。工作区可以选择提供对底层存储的开放访问,或者将它们保护在专门构建的接口后面。

Workspace中的存储都提供共同的只读事务,因此它们的快照在工作区构建时彼此一致。工作区提供自己的接口与存储交互。一旦更改累积在BufferedStores中,工作区本身可以被提交,这使用新的读写事务从每个存储刷新更改并提交到磁盘。提交会消耗工作区。

构建模块

holochain_sqlite crate提供三种缓冲KV存储抽象以及一个简单的CAS抽象:

  1. KvBuf:普通KV存储
  2. KvIntBuf:键必须为整数的KV存储(使用LMDB时有意义,但现在不重要)
  3. KvvBuf:每个键有多个值的KV存储,支持按键迭代
  4. CasBuf:强制键必须是值"地址"(内容)的KvBuf

holochain crate将这些构建模块组合在一起构建更特定目的的BufferedStore实现。

示例代码

use holochain_sqlite::prelude::*;
use rusqlite::Connection;

// 创建SQLite连接
let conn = Connection::open_in_memory().unwrap();

// 创建缓冲KV存储
let mut kv_store = KvBuf::new(&conn);

// 插入数据
kv_store.put("key1".to_string(), "value1".to_string()).unwrap();
kv_store.put("key2".to_string(), "value2".to_string()).unwrap();

// 获取数据
let value = kv_store.get(&"key1".to_string()).unwrap();
assert_eq(value, Some("value1".to_string()));

// 创建整数键KV存储
let mut int_store = KvIntBuf::new(&conn);
int_store.put(1, "int_value1".to_string()).unwrap();
int_store.put(2, "int_value2".to_string()).unwrap();

// 创建CAS存储
let mut cas_store = CasBuf::new(&conn);
cas_store.put("address1", "content1".to_string()).unwrap();

完整示例

use holochain_sqlite::{kv::KvBuf, sqlite::rusqlite::Connection};
use serde::{Serialize, Deserialize};

#[derive(Debug, Clone, Serialize, Deserialize)]
struct Person {
    name: String,
    age: u32,
}

fn main() -> anyhow::Result<()> {
    // 创建内存数据库
    let conn = Connection::open_in_memory()?;
    
    // 初始化KV存储
    let mut kv_store = KvBuf::new(&conn);
    
    // 存储自定义结构
    let person = Person {
        name: "Alice".to_string(),
        age: 30,
    };
    kv_store.put("person1".to_string(), person.clone())?;
    
    // 获取数据
    let retrieved: Option<Person> = kv_store.get(&"person1".to_string())?;
    println!("Retrieved person: {:?}", retrieved);
    
    // 创建事务并提交
    let tx = conn.transaction()?;
    kv_store.flush(&tx)?;
    tx.commit()?;
    
    Ok(())
}

贡献

Holochain是一个开源项目。我们欢迎各种形式的参与,并积极扩大接受参与的范围。

许可证

CAL-1.0许可证

版权所有©2019-2024,Holochain基金会

本程序是自由软件:您可以根据许可证条款重新分发和修改它。


1 回复

Rust分布式数据库插件holochain_sqlite使用指南

概述

holochain_sqlite是一个为Holochain应用提供SQLite存储支持的Rust插件,它实现了高效的数据持久化和查询功能,特别适合需要本地数据库支持的分布式应用开发。

主要特性

  • 为Holochain提供SQLite后端存储
  • 支持高效的数据持久化
  • 提供灵活的查询能力
  • 与Holochain DNA无缝集成
  • 轻量级且高性能

安装方法

在Cargo.toml中添加依赖:

[dependencies]
holochain_sqlite = "0.1.0"

完整示例代码

use holochain_sqlite::prelude::*;
use holochain_types::prelude::*;

#[tokio::main]
async fn main() -> SqliteDbResult<()> {
    // 1. 初始化SQLite存储
    let db_path = "holochain_data.db";
    let sqlite = SqliteStorage::new(db_path).await?;
    sqlite.initialize().await?;
    
    // 2. 存储示例数据
    let entry = Entry::try_from(SerializedBytes::try_from(
        r#"{"name":"test","value":42}"#,
    )?)?;
    let header_hash = HeaderHash::from_raw_32(vec![0; 32]);
    let entry_hash = EntryHash::from_raw_32(vec![1; 32]);
    sqlite.store_entry(&header_hash, &entry_hash, &entry).await?;
    
    // 3. 查询数据
    let retrieved_entry = sqlite.get_entry(&entry_hash).await?;
    println!("Retrieved entry: {:?}", retrieved_entry);
    
    // 4. 高级查询
    let high_value_entries = advanced_query(&sqlite).await?;
    println!("Entries with value > 40: {}", high_value_entries.len());
    
    // 5. 性能优化
    optimize_database(&sqlite).await?;
    
    Ok(())
}

async fn advanced_query(sqlite: &SqliteStorage) -> SqliteDbResult<Vec<Entry>> {
    use rusqlite::params;
    
    let mut conn = sqlite.connection().await?;
    let mut stmt = conn.prepare(
        "SELECT entry_blob FROM entries WHERE json_extract(entry_blob, '$.value') > ?"
    )?;
    
    let entries = stmt
        .query_map(params![40], |row| {
            let blob: Vec<u8> = row.get(0)?;
            Entry::try_from(SerializedBytes::from_unchecked(blob.into()))
        })?
        .collect::<Result<Vec<_>, _>>()?;
    
    Ok(entries)
}

async fn optimize_database(sqlite: &SqliteStorage) -> SqliteDbResult<()> {
    // 创建索引
    sqlite.execute(
        "CREATE INDEX IF NOT EXISTS idx_entry_value ON entries(json_extract(entry_blob, '$.value'))"
    ).await?;
    
    // 设置PRAGMA优化参数
    sqlite.execute("PRAGMA journal_mode = WAL").await?;
    sqlite.execute("PRAGMA synchronous = NORMAL").await?;
    sqlite.execute("PRAGMA cache_size = -10000").await?;
    
    Ok(())
}

集成到Holochain DNA

在DNA配置文件中添加SQLite存储配置:

---
manifest_version: "1"
name: my_dna
integrity:
  network_seed: "my_network_seed"
  properties: ~
  origin_time: "2023-01-01T00:00:00.000000Z"
  zomes:
    - name: my_zome
      bundled: target/wasm32-unknown-unknown/release/my_zome.wasm
      dependencies: ~
coordinator:
  zomes: ~
  storage:
    type: sqlite
    path: "./dna_data.db"

注意事项

  1. 在多线程环境中使用时,确保正确处理连接池
  2. SQLite文件路径需要有写入权限
  3. 大型数据集应考虑分片策略
  4. 定期备份重要数据
回到顶部