本文档概述了 Go-Ethereum (Geth) 中的存储系统,涵盖了区块链数据的存储、访问和管理方式。存储系统负责持久化所有区块链数据,包括区块、交易、收据和以太坊状态。
有关状态管理的具体信息,请参阅状态管理。
Geth 采用两层存储架构,旨在平衡性能和磁盘使用量。
这种架构使 Geth 能够高效地处理频繁访问的近期数据和不常访问的历史数据。
来源:ethdb/database.go72-83 core/rawdb/database.go40-67
Geth 在 ethdb 包中为其存储需求定义了清晰的接口,将不同存储操作的关注点分开。
键值数据库用于存储
Geth 支持多种键值数据库后端
这两种数据库都是持久化的键值存储,针对快速读取和批量写入进行了优化。
来源:cmd/utils/flags.go103-108 core/rawdb/database.go37-83
历史数据存储,也称为 Freezer,是一个仅追加的数据库,专为很少发生变化的历史数据而设计。它按类型将数据组织到单独的文件中
每种文件类型由一个“freezer table”管理,该表负责索引和高效存储。
来源:core/rawdb/freezer.go1-190 core/rawdb/freezer_table.go1-400
存储系统根据预定义的模式组织区块链数据,使用特定的键格式来存储不同类型的数据。
不同类型的数据使用不同的键前缀和格式
| 数据类型 | 键格式 | 描述 |
|---|---|---|
| 区块头 | headerPrefix + num + hash | 按编号和哈希查找的区块头 |
| 区块体 | bodyPrefix + num + hash | 按编号和哈希查找的区块体 |
| 区块收据 | blockReceiptsPrefix + num + hash | 按区块编号和哈希查找的收据 |
| 交易 | txLookupPrefix + hash | 按哈希查找的交易数据 |
| 规范哈希 | headerPrefix + num + canonicalSuffix | 指定编号下规范区块的哈希 |
| 头部区块 | headBlockKey | 当前头部区块的哈希 |
| 状态 Merkle Trie 节点 | 将流量定向到响应时间最快的服务器 | 按哈希查找的状态 Merkle Trie 节点 |
来源:core/rawdb/schema.go1-100 core/rawdb/accessors_chain.go1-200
以太坊的状态使用修改过的 Merkle Patricia Trie 结构来存储。这种数据结构提供了
来源:trie/trie.go32-60 core/state/statedb.go68-157
Geth 提供多个命令行选项来配置存储系统
| 标志 | 描述 | 默认 |
|---|---|---|
--datadir | 数据库的数据目录 | ~/.ethereum |
--datadir.ancient | 历史数据目录 | datadir/chaindata 内部 |
--db.engine | 数据库后端 | pebble |
--cache | 用于缓存的内存分配 | 4096 MB (主网) |
--cache.database | 数据库缓存的百分比 | 50% |
--nocompaction | 禁用数据库压缩 | false |
| 标志 | 描述 | 默认 |
|---|---|---|
--gcmode | 垃圾回收模式 | full |
--state.scheme | 状态存储方案 | hash |
--cache.gc | GC 的百分比 | 25% |
--snapshot | 启用快照数据库模式 | true |
--history.transactions | 交易历史保留 | 约 1 年 |
--history.state | 状态历史保留 | 90,000 个区块 |
来源:cmd/utils/flags.go91-114 cmd/utils/flags.go251-301 cmd/geth/config.go1-120
Geth 支持不同的存储模型以适应各种用例
完整节点存储所有近期状态和区块,同时修剪旧状态。这是默认的存储模式,在磁盘使用量和功能之间取得了平衡。
存档节点存储所有区块的完整状态历史,需要显著更多的磁盘空间,但支持完整的历史查询。
--gcmode=archive 启用来源:eth/backend.go378-380 cmd/utils/flags.go251-290
Geth 支持两种不同的状态存储方案
路径方案在某些操作上更有效,并为状态历史提供更好的支持。
来源:cmd/utils/flags.go262-267 core/rawdb/database.go135-144
当区块链数据超过特定阈值时,Geth 会自动将旧数据从键值数据库迁移到历史存储。这个过程
此迁移是自动进行的,对用户透明。
来源:core/rawdb/freezer.go380-520
Geth 实现了多种修剪机制来控制磁盘使用量
状态修剪器会移除不再需要的历史状态数据
--gcmode=archive 禁用修剪--history.state 进行配置可以修剪交易查找以减少磁盘使用量
--history.transactions 进行配置事件日志也可以修剪以节省空间
--history.logs 进行配置--history.logs.disable 禁用来源: cmd/utils/flags.go262-301 eth/backend.go140-142
Geth 启动时,会按此顺序初始化数据库系统
如果检测到损坏数据,Geth 可能会尝试进行恢复操作。
来源: eth/backend.go129-147 core/genesis.go290-320
Geth 包含多种数据库恢复机制
如果数据库损坏,可以通过再次从网络同步来重建数据库。
Geth 中的存储系统是一个复杂的多层架构,旨在高效处理以太坊节点多样化的数据存储需求。通过将活动数据与历史数据分开,并采用各种修剪策略,Geth 在性能、功能和资源使用之间取得了平衡。
具有键值存储和历史数据库的两层架构,以及可配置的修剪选项,使得 Geth 能够部署在各种环境中,从资源受限的设备到专用的归档节点。
来源: eth/backend.go1-527 core/rawdb/database.go1-200 ethdb/database.go1-186