1. 概述

本文将介绍 SirixDB 是什么,以及它的核心设计目标。接着会带你了解 SirixDB 提供的基于游标的事务性低级 API。

SirixDB 是一个支持时间版本的 NoSQL 文档数据库,适用于处理数据演化场景。它不会覆盖磁盘上的任何数据,因此可以高效地恢复和查询资源的完整版本历史。此外,SirixDB 在存储开销上也做了优化。

如果你对构建轻量级文档数据库、版本控制、数据恢复等方面感兴趣,那么这篇文章非常适合你。

2. SirixDB 特性

SirixDB 是一个日志结构、支持时间版本的 NoSQL 文档数据库,内置了两种原生数据模型:二进制 XML 存储和 JSON 存储。

2.1. 设计目标

SirixDB 的核心设计目标包括以下几点:

并发性:使用极少锁机制,适用于多线程系统
异步 REST API:操作可独立进行,每个事务绑定特定版本,支持一个写事务与多个只读事务并发执行
版本控制/历史记录:最小存储开销下保存资源的完整历史版本,读写性能可调
数据完整性:类似 ZFS,父节点存储子节点页面的完整校验和,可检测数据损坏
写时复制(Copy-on-Write):数据不会被覆盖,修改时复制到新位置
按记录/按版本存储:不仅按页版本化,还支持按记录版本化,减少复制量
原子性保证(无 WAL):无须写前日志即可保证系统一致性,断电不会导致数据损坏
日志结构 & SSD 友好:批量写入,顺序提交,不覆盖已提交数据

SirixDB 的低级 API 支持 JSON 和 XML 资源的存储、遍历与比较。后续文章中我们还会介绍更高层次的 API,如 XQuery、RESTful API 等。

使用 SirixDB 至少需要 Java 11。

3. Maven 依赖配置

要使用 SirixDB,可以通过 Maven 引入 sirix-core 依赖:

<dependency>
    <groupId>io.sirix</groupId>
    <artifactId>sirix-core</artifactId>
    <version>0.9.3</version>
</dependency>

或者使用 Gradle:

dependencies {
    implementation 'io.sirix:sirix-core:0.9.3'
}

4. SirixDB 的树结构编码

SirixDB 中的每个节点通过以下方式引用其他节点:

firstChild/leftSibling/rightSibling/parentNodeKey/nodeKey

节点 ID 是由顺序生成器生成的唯一稳定 ID。

每个节点可以拥有第一个子节点、左兄弟、右兄弟和父节点。此外,SirixDB 还支持记录子节点数量、后代数量以及节点哈希值。

5. 创建一个包含单个资源的数据库

下面是一个创建数据库并导入 JSON 文件的示例:

var pathToJsonFile = Paths.get("jsonFile");
var databaseFile = Paths.get("database");

Databases.createJsonDatabase(new DatabaseConfiguration(databaseFile));

try (var database = Databases.openJsonDatabase(databaseFile)) {
    database.createResource(ResourceConfiguration.newBuilder("resource").build());

    try (var manager = database.openResourceManager("resource");
         var wtx = manager.beginNodeTrx()) {
        wtx.insertSubtreeAsFirstChild(JsonShredder.createFileReader(pathToJsonFile));
        wtx.commit();
    }
}

说明:

  • 首先创建数据库,然后创建一个资源
  • 使用 wtx 开启一个写事务并导入 JSON 文件
  • 使用 Java 的 try-with-resources 自动关闭资源

⚠️ 注意:XML 资源的创建方式与此类似,只是使用的 API 略有不同。

6. 打开数据库资源并进行遍历

6.1. JSON 资源的前序遍历

我们可以使用只读事务来遍历整个 JSON 资源:

try (var database = Databases.openJsonDatabase(databaseFile);
     var manager = database.openResourceManager("resource");
     var rtx = manager.beginNodeReadOnlyTrx()) {
    
    new DescendantAxis(rtx, IncludeSelf.YES).forEach((unused) -> {
        switch (rtx.getKind()) {
            case OBJECT:
            case ARRAY:
                LOG.info(rtx.getDescendantCount());
                LOG.info(rtx.getChildCount());
                LOG.info(rtx.getHash());
                break;
            case OBJECT_KEY:
                LOG.info(rtx.getName());
                break;
            case STRING_VALUE:
            case BOOLEAN_VALUE:
            case NUMBER_VALUE:
            case NULL_VALUE:
                LOG.info(rtx.getValue());
                break;
            default:
        }
    });
}
  • 使用 DescendantAxis 进行前序(深度优先)遍历
  • 节点哈希值默认是自底向上构建的
  • 数组和对象节点没有名称和值字段

SirixDB 提供了多种遍历轴,包括:

✅ XPath 所有轴
✅ LevelOrderAxis(层序遍历)
✅ PostOrderAxis(后序遍历)
✅ NestedAxis(链式轴)
✅ ConcurrentAxis(并发遍历)

接下来我们介绍 VisitorDescendantAxis,它可以根据节点类型执行不同的操作。

6.2. VisitorDescendantAxis 遍历

SirixDB 支持使用访问者模式定义节点行为。我们可以为 VisitorDescendantAxis 设置一个访问者:

public enum VisitResultType implements VisitResult {
    SKIPSIBLINGS,
    SKIPSUBTREE,
    CONTINUE,
    TERMINATE
}
  • SKIPSIBLINGS:跳过当前节点的兄弟节点
  • SKIPSUBTREE:跳过当前节点的子树
  • CONTINUE:继续遍历
  • TERMINATE:立即终止遍历

默认每个访问方法返回 CONTINUE,所以我们只需重写感兴趣的节点方法即可。

使用方式如下:

var axis = VisitorDescendantAxis.newBuilder(rtx)
  .includeSelf()
  .visitor(new MyVisitor())
  .build();

while (axis.hasNext()) axis.next();
  • MyVisitor 实现了 Visitor 接口
  • 每次遍历调用对应的 visit() 方法

6.3. 时间轴(Time Travel Axis)

SirixDB 支持“时间旅行”功能,可以遍历某个节点在不同版本中的状态。

使用 TimeAxis 可以遍历指定节点在不同修订版本中的快照:

try (var database = Databases.openJsonDatabase(databaseFile);
     var manager = database.openResourceManager("resource");
     var rtx = manager.beginNodeReadOnlyTrx()) {
    
    new TimeAxis(rtx).forEach((unused) -> {
        LOG.info("Revision: " + rtx.getRevision());
        LOG.info("Value: " + rtx.getValue());
    });
}
  • TimeAxis 遍历指定节点在所有修订版本中的状态
  • 可用于查看某个字段的历史变化

适用场景:审计、数据回滚、变更追踪等


以上内容为 SirixDB 的核心使用介绍,后续我们将继续介绍其高级 API 与应用场景。


原始标题:A Guide to SirixDB

« 上一篇: Java GSS API 指南
» 下一篇: Java Weekly, 第294期