IAVL 树与四种裁剪策略:Cosmos 系节点怎么留版本、删历史 图 1
IAVL 树与四种裁剪策略:Cosmos 系节点怎么留版本、删历史 · 图 1

状态树为什么必须版本化

在 CometBFT 加 Cosmos SDK 这条技术栈上,共识层对这一区块执行完世界长什么样只认一个值:应用哈希(AppHash)。它写进区块头、由验证者签名背书,来自应用状态数据结构的默克尔根。要让任何人在任何高度独立重放执行并得到同一个根,状态存储必须同时做到两件事:每个高度留下确定的根,历史版本可按需读取、也按策略清理。IAVL 树就是承担这件事的组件,Cosmos 系节点磁盘上最占地方的账本往往就是它。

一棵带版本号的平衡二叉树

机制示意(图片由 Agnes 生成,非产品界面或数据图)

官方仓库 cosmos/iavl 对它的定义是可版本化、可快照的不可变 AVL+ 树。键值对按字节序挂在叶子上,中间节点保存左右子树的哈希,任何一次增删改都沿路径重算到根,因此同样的操作序列永远得到同样的根;树高通过旋转保持平衡,单次读写代价是对数级。每执行完一个区块,树提交一个新版本号并产出新根,旧版本并不立刻消失,而是与新的根共存于底层键值库,供按高度查询历史状态。版本即区块高度,这条对应关系也是链上按高度做证明的基础。

四种裁剪策略

全部版本永久保留意味着磁盘无上限增长,SDK 因此在 app.toml 中提供裁剪策略:default 保留最近约三十六万两千八百八十条历史状态——官方文档换算约为三个半星期的量——并每十个区块执行一次清理;everything 只保留最近两个状态,同样按固定间隔动手;nothing 什么都不删,对应归档节点的角色;custom 则通过 pruning-keep-recentpruning-interval 两个参数自定义保留条数和清理频率。策略只决定删哪些高度,具体怎么删交给底层存储实现执行。各链发行方可以改写默认值,以所在链的当期文档为准。

裁剪删不掉什么

需要划清界限:裁剪针对的是历史版本的状态快照,区块数据、状态同步用的快照导出属于另一套机制,不在这把刀下。但被裁掉的高度上,按历史余额查询这类请求会直接失败;轻客户端或跨链验证若指向过旧高度,也拿不到对应见证。这正是节点运维里归档节点贵、普通节点会忘事这一张力的来源:省钱与可回溯之间没有免费午餐。

快节点、导出与证明:树的三项配套能力

为了让按键值直接读取不必每次走整棵树,IAVL 维护一套叫快节点的旁路缓存,直接映射到当前值,查询热的最新状态时不必从根逐层下降;它只是加速层,根哈希仍由带版本的主树计算,删掉缓存不影响共识正确性。树还支持导出与导入:给定一个高度,可以把该版本的整棵树以流的形式导出,供另一台节点在其上重建同构的树——状态同步快照正是靠这类能力让新节点不必从零回放历史。链上证明也挂在同一棵树上:存在性证明与非存在性证明都由树生成,跨链场景里证明对端余额或某个键值时,最终核验的就是这棵树在某个版本的根。

核对路径

想确认一台节点还留着多少历史,可以把配置里的裁剪字段与一次按旧高度的链上查询交叉验证:配置说保留三百天,就抽一个三百天前的区块高度查同一笔余额。不同 Cosmos 系链的默认参数可能不同,动手改配置前先备份数据库,删除历史版本不可逆。对普通用户而言更实用的提醒是:如果你依赖某个网页浏览器或索引服务查自己几个月前的持仓变化,它背后多半是裁剪策略宽松得多的归档节点;同一笔查询打到一台 everything 策略的公共节点上,返回版本不存在的报错并不是数据丢失,只是那台节点按自己的配置把这页记忆清掉了。

本文仅解释存储机制,不构成任何投资建议。