Tonbo 嵌入式 OLAP 存储引擎

1次阅读

共计 3787 个字符,预计需要花费 10 分钟才能阅读完成。

Intoducing Tonbo

官网:https://tonbo.io/

我们很高兴地宣布,Tonbo 的预览版本现已开源!Tonbo 是一个用 Rust 编写的嵌入式持久数据库。它提供了基本的类似 KV 的方法:插入、过滤和范围扫描,使其成为数据密集型应用程序(包括其他类型的数据库)的基础。它还支持类型安全的结构化数据存储。对于 Rust 开发人员,我们提供了一个类似 ORM 的宏以方便使用:

#[tonbo_record]
pub struct User {#[primary_key]
    name: String,
    email: Option,
    age: u8,
}

Tonbo 能够快速、方便地查询类型安全的结构化数据。例如,集成一个查询引擎只需要几个小时的编码:datafusion example(对 DataFusion 的官方支持将包含在下一个版本中)。在我们的初步基准测试中,Tonbo 在数据扫描场景中的性能比 RocksDB 高出 2.2 倍,尽管它仍处于早期阶段。如果您对 Tonbo 感兴趣,请在 GitHub 上为该项目加注星标,在 Twitter 上关注我们,或加入 Discord 上的对话。

我们为什么建造 Tonbo?

Apache Arrow(例如 Apache Arrow DataFusion 生态系统)中的分析工具为数据处理提供了良好的基础。它们在可扩展性、开发效率和执行性能之间取得了很好的平衡,允许开发人员使用 DataFusion 在几天内构建高效的数据分析应用程序。

然而,大多数工具都关注数据库的读取路径。每个使用 DataFusion 的数据密集型应用程序最终都会花费大量时间来开发自己的写入路径实现。如果我们可以拥有像 DataFusion 一样敏捷且高性能的写入路径实现,会怎样?

这是 Tonbo 的主要设计目标:作为嵌入式数据库,为 Arrow 生态系统提供高度可扩展的数据存储引擎。此外,Tonbo 还有一个长远目标:提供离线优先的分布式数据存储能力,支持跨各种环境(从嵌入式 Linux、浏览器到服务器)快速灵活的数据存储,并与文件系统、OPFS 等多种存储系统集成和 S3。

		╔═tonbo═════════════════════════════════════════════════════╗
		║                                                           ║
		║    ┌─────────client memory─┐  ┌─────────client memory─┐   ║
		║    │ ┏━━━━━━━━━━━━┓        │  │ ┏━━━━━━━━━━━━┓        │   ║
		║    │ ┃  memtable  ┃        │  │ ┃  memtable  ┃        │   ║
		║    │ ┗━━━━┳━━━━━━━┛        │  │ ┗━━━━┳━━━━━━━┛        │   ║
		║    │ ┏━━━━▼━━━━━━━┓        │  │ ┏━━━━▼━━━━━━━┓        │   ║
		║    │ ┃  memtable  ┃        │  │ ┃  memtable  ┃        │   ║
		║    │ ┗━━━━┳━━━━━━━┛        │  │ ┗━━━━┳━━━━━━━┛        │   ║
		║    │ ┏━━━━▼━━━━━━━┓        │  │ ┏━━━━▼━━━━━━━┓        │   ║
		║    │ ┃  memtable  ┃        │  │ ┃  memtable  ┃        │   ║
		║    │ ┗━━━━┳━━━━━━━┛        │  │ ┗━━━━┳━━━━━━━┛        │   ║
		║    └──────╂────────────────┘  └──────╂────────────────┘   ║
		║    ┌──────╂─client storage─┐  ┌──────╂─client storage─┐   ║
		║    │ ┏━━━━▼━━━━┓           │  │ ┏━━━━▼━━━━┓           │   ║
		║    │ ┃ parquet ┃           │  │ ┃ parquet ┃           │   ║
		║    │ ┗━━━━┳━━━━┛           │  │ ┗━━━━┳━━━━┛           │   ║
		║    └──────╂────────────────┘  └──────╂────────────────┘   ║
		║           ┣━━━━━━━━━━━━━━━━━━━━━━━━━━┛                    ║
		║    ┌──────╂────────────────────────────────server ssd─┐   ║
		║    │      ┣━━━━━━━━━━━┓                               │   ║
		║    │ ┏━━━━▼━━━━┓ ┏━━━━▼━━━━┓                          │   ║
		║    │ ┃ parquet ┃ ┃ parquet ┃                          │   ║
		║    │ ┗━━━━┳━━━━┛ ┗━━━━┳━━━━┛                          │   ║
		║    └──────╂───────────╂───────────────────────────────┘   ║
		║    ┌──────╂───────────╂────────object storage service─┐   ║
		║    │      ┣━━━━━━━━━━━╋━━━━━━━━━━━┳━━━━━━━━━━━┓       │   ║
		║    │ ┏━━━━▼━━━━┓ ┏━━━━▼━━━━┓ ┏━━━━▼━━━━┓ ┏━━━━▼━━━━┓  │   ║
		║    │ ┃ parquet ┃ ┃ parquet ┃ ┃ parquet ┃ ┃ parquet ┃  │   ║
		║    │ ┗━━━━━━━━━┛ ┗━━━━━━━━━┛ ┗━━━━━━━━━┛ ┗━━━━━━━━━┛  │   ║
		║    └──────────────────────────────────────────────────┘   ║
		║                                                           ║
		╚═══════════════════════════════════════════════════════════╝

Tonbo 是如何设计的?

Tonbo 建立在 LSM Tree 架构之上。通常,数据库使用日志结构合并 (LSM) 树或 B+ 树。LSM 树的主要优点是所有前台写入都在内存中执行,而后台写入是顺序的,从而产生非常高的写入吞吐量。

LSM 树对文件系统的要求也很低:它们只需要一个支持仅追加操作的文件系统即可实现高性能的数据插入和更新。这使得 Tonbo 适用于从闪存到 SSD 的各种设备。通过将数据清理和压缩等任务卸载到后台,LSM 树简化了读 / 写过程并减少了前端的负载。

类型安全的结构化存储

如前所述,Tonbo 超越了传统的 KV 数据库,支持结构化数据的读写。此功能是通过将用户定义的数据结构忠实地转换为 Arrow 模式来实现的。Tonbo 利用 Arrow/Parquet 提供的功能来支持下推操作,例如限制、项目和过滤器:

let lower = "Alice".into();
let upper = "Bob".into();
let mut scan = txn
  .scan((Bound::Included(&name), Bound::Excluded(&upper)))
  .await
  // just project first column, skip others when reading from db files
  .projection(vec![1])
  // only need one row
  .limit(1)
  .take()
  .await.unwrap();

这意味着 Tonbo 可以精确扫描用户指定的数据,并跳过不相关的数据,如果使用得当,查询效率可能会提高十倍以上。此外,根据编译时类型提示,Tonbo 可以免费将从文件读取的字节转换为用户定义的数据类型。在许多数据密集型应用程序中,序列化开销可能占总负载的 30% 到 50%。

Asynchronous

Tonbo 完全支持异步方法:

db.insert(User {name: "Alice".into(),
  email: Some("[email protected]".into()),
  age: 22,
})
.await.unwrap();

let name = "Alice".into();
let user = txn
  .get(
    &name,
    Projection::All,
  )
.await.unwrap();

使用异步接口不仅可以提高并发操作的效率,还可以让 Tonbo 在资源受限的设备上提供并发访问,例如浏览器、移动应用程序和嵌入式 Linux 系统。如前几节所述,LSM Tree 架构需要后台任务来进行垃圾收集和压缩新写入的数据,这在浏览器中实现起来可能具有挑战性,因为它们无法轻松启动后台任务。通过利用 WASM 和异步 Rust 接口,我们可以构建从 OPFS 到 JavaScript 调度程序的完整异步任务链。在我们即将发布的博客文章中,我们将详细描述我们如何实现这一目标——见证吧!

					╔═Web APP══════════════════════╗
					║                              ║
					║ ┏━JS microtasks━━━━━━━━━━━━┓ ║
					║ ┃                          ┃ ║
					║ ┃ ┏╍WASM binding╍╍╍╍╍╍╍╍╍┓ ┃ ║
					║ ┃ ┇                      ┇ ┃ ║
					║ ┃ ┇ ┏━Tonbo async API━━┓ ┇ ┃ ║
					║ ┃ ┇ ┃                  ┃ ┇ ┃ ║
					║ ┃ ┇ ┃ ┏╍WASM binding╍┓ ┃ ┇ ┃ ║
					║ ┃ ┇ ┃ ┇              ┇ ┃ ┇ ┃ ║
					║ ┃ ┇ ┃ ┇   ┏━━━━━━┓   ┇ ┃ ┇ ┃ ║
					║ ┃ ┇ ┃ ┇   ┃ OPFS ┃   ┇ ┃ ┇ ┃ ║
					║ ┃ ┇ ┃ ┇   ┗━━━━━━┛   ┇ ┃ ┇ ┃ ║
					║ ┃ ┇ ┃ ┗╍╍╍╍╍╍╍╍╍╍╍╍╍╍┛ ┃ ┇ ┃ ║
					║ ┃ ┇ ┗━━━━━━━━━━━━━━━━━━┛ ┇ ┃ ║
					║ ┃ ┗╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┅┛ ┃ ║
					║ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ║
					╚══════════════════════════════╝

Futures and Promises

在准备下一个版本时,我们计划引入几个新功能:

  • 运行时架构声明:对 JavaScript (WASM) 和 Python 的基本支持。
  • S3 集成:基于此集成实现分层 LSM 树。

我们很快将为自托管和云环境提供统一的解决方案,从而能够在任何远程存储平台上创建离线优先的存储和备份服务。设计和实现只有在实际使用和反馈的情况下才有价值。如果您认为我们的工作可以使您受益,请直接联系。我们很高兴能够合作并提供力所能及的帮助。

正文完
 0