> ## Documentation Index
> Fetch the complete documentation index at: https://docs.monad.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# MonadDb

> 用于存储区块链数据的高性能数据库。

## 概要

MonadDb 是 Monad 在保持与以太坊完全兼容的同时实现高性能的关键组件。它是一个专为存储，经过验证的区块链数据而设计的定制键值数据库。具体而言,MonadDb 针对在磁盘上高效存储默克尔帕特里夏树 (Merkle Patricia Trie) 节点进行了优化。

## 默克尔帕特里夏树结构化数据库

大多数以太坊客户端使用通用的键值数据库,这些数据库要么以 B 树(例如 [LMDB](https://www.symas.com/lmdb))形式实现,要么以 LSM 树(例如 [LevelDB,](https://github.com/google/leveldb) [RocksDB](https://rocksdb.org/))形式实现。然而,以太坊使用[默克尔帕特里夏树](https://ethereum.org/en/developers/docs/data-structures-and-encoding/patricia-merkle-trie/) (MPT) 数据结构来存储状态和其他经过认证的字段,如收据和交易。这导致了一个次优的解决方案,即一个数据结构被嵌入到另一个数据结构中。MonadDb 原生实现了[帕特里夏树](https://en.wikipedia.org/wiki/Radix_tree)(基数树的一种特定变体)数据结构,无论在磁盘上还是在内存中。尽管有明确的设计倾向,MonadDb 是一个灵活的键值存储,能够存储任何类型的数据。例如,MonadDb 也用于存储 Monad 的区块头和负载。

## 基于页的存储状态

在 [MIP-8](https://mips.monad.xyz/MIPs/MIP-8) 中,存储 trie 承诺的是页而不是单个 slot。一页是由 128 个连续存储 slot 组成的连续区域(4096 字节),每个存储叶节点保存对某一页内容的 32 字节承诺,因此 trie 存储的是 `{page_index, page_commitment}` 对。trie 的其他部分保持不变:key 使用 keccak 哈希,分支、扩展和叶节点遵循标准 MPT 规则。读取单个 32 字节 slot 时,本来就会从 SSD 拉取整页,所以承诺现在覆盖的正是一次磁盘读取所对应的单位。

页承诺是一棵仅覆盖该页已占用 slot 的 BLAKE3 Merkle 根。计算它的开销以及针对它的证明大小,与页内实际占用的多少成正比,而不与 4096 字节的页大小成正比。相应的 gas 计价请参见[存储页](/zh/developer-essentials/opcode-pricing#storage-pages),现有数据库如何迁移请参见 [MIP-8 激活与存储页迁移](/zh/node-ops/upgrade-instructions/page-storage-mip-8-migration)。

## 异步 IO

Monad [并行](/zh/monad-arch/execution/parallel-execution)执行多个交易。为了实现这一点,读操作不应阻塞后续操作,而这个目标推动了数据库的[异步 I/O](/zh/monad-arch/concepts/asynchronous-io) (async I/O)。上面提到的键值数据库缺乏适当的异步 I/O 支持(尽管在这方面有一些改进的努力)。MonadDb 充分利用了最新的内核对异步 I/O 的支持(在 Linux 上是 [io\_uring](https://unixism.net/loti/index.html))。这避免了产生大量内核线程来处理待处理的 I/O 请求以尝试异步执行工作。

## 文件系统绕过

现代文件系统为应用程序提供了便利的抽象,但在构建高吞吐量 I/O 软件时会引入开销。这些通常隐藏的成本包括块分配、碎片化、读/写放大和元数据管理。文件和一组系统调用的抽象允许应用程序像文件数据是连续存储的那样与之交互。管理确切物理磁盘位置的复杂性对应用程序(及其开发者)是抽象隐藏的。然而,磁盘上的实际内容可能被分割成多个不连续的部分。访问或写入这样的文件通常涉及不止一个简单的 I/O 操作。

为了最小化开销,MonadDb 为运营者提供了绕过文件系统的选项。MonadDb 基于帕特里夏树数据结构实现了自己的索引系统,消除了对文件系统的依赖。用户可以灵活地在常规文件或块设备上运行 MonadDb。为了获得最佳性能,建议直接在块设备上运行 MonadDb。这种方法避免了所有与文件系统相关的开销,让 MonadDb 能够完全释放 SSD 的性能。

## 并发控制

Monad 区块链由多个客户端组成,每个客户端作为读者或写者与数据库交互。为了支持这一功能,MonadDb 必须高效地在单个写者(执行)和多个读者(共识和 RPC)之间同步。

MonadDb 实现了一个持久(或不可变)的帕特里夏树。当树中的分支被更新时,会创建该分支上节点的新版本,并保留树的先前版本。这种方法便于数据库内的版本管理,并大大简化了读者和写者之间的同步。它确保所有读操作都准确一致,同时保证从读者的角度来看,写操作是完整且原子的。

## 现代 SSD 上的写性能

持久数据结构的使用还允许我们执行顺序写入,这在现代 SSD 上比随机写入提供更好的性能。现代 SSD 垃圾回收发生在块级别。在顺序写入时,一个完整的块在下一个块之前被填满,这大大简化了垃圾回收。随机写入的垃圾回收成本要高得多。顺序写入还更有效地分布数据,从而减少写放大并延长 SSD 寿命。

## 压缩

随着历史版本的累积,写入磁盘的数据量将增长。鉴于其运行的有限磁盘容量,不可能保留完整的历史记录。MonadDb 存储区块链数据和状态的最近版本,并根据可用磁盘空间动态调整历史长度。随着较新版本的存储和较旧版本的修剪,底层存储空间变得碎片化。为了解决这个问题,MonadDb 与更新同步执行压缩,整合活动数据并释放未使用的存储以供回收。这在保持性能和数据完整性的同时减少了碎片化。
