一、问题的核心:FISCO BCOS长期运行后磁盘占满的本质
很多用FISCO BCOS做联盟链的团队,跑了几个月甚至一两年后,都会遇到一个头疼的问题:节点所在的服务器磁盘空间越用越少,最后甚至因为磁盘满了导致节点停服。有人会说“区块链不就是存交易数据吗?怎么会占这么大空间?”,其实核心问题不是交易多,而是FISCO BCOS的落盘数据结构、状态存储逻辑,没有针对长期运行做优化。
举个真实的例子:某联盟链项目上线18个月,共有12个节点,每个节点的磁盘占用达到了120G,其中区块数据占了85G,状态数据占了25G,日志占了10G。而实际的有效交易数据,换算成普通的数据库存储,可能连1G都不到。这中间的巨大差距,就是我们要解决的核心。
1.1 落盘数据的结构拆解
FISCO BCOS的节点数据主要存在data目录下,核心分为三类:
- 区块数据(
block目录):每个区块是一个单独的文件,里面包含区块头、交易列表、交易回执等信息。FISCO BCOS默认是“全量存储”,也就是从创世块到最新块的所有区块都会完整保留,哪怕有些区块的交易已经失效、或者对应的状态已经被更新过N次。 - 状态数据(
state目录):这是节点当前的全局状态快照,比如某个账户的余额、某个合约的存储值等。状态数据是动态更新的,每次出块都会修改,但默认不会自动清理历史状态。 - 日志数据(
logs目录):节点运行时的日志,包括交易处理、共识、网络交互等信息,默认不会自动轮转,会越攒越多。
很多人以为只有区块数据占空间,其实状态数据的历史版本才是“隐形大户”。比如一个合约的存储值更新了100次,默认会保留这100次的历史状态,每一次更新都会占用磁盘空间。
二、落盘数据格式的优化空间
FISCO BCOS的落盘格式本身有优化的空间,主要是针对“冗余存储”的部分做裁剪,同时不影响节点的正常运行和数据可追溯性。
2.1 区块数据的冗余优化
默认情况下,FISCO BCOS的每个区块文件都包含完整的交易和交易回执,而很多交易其实是重复的、或者无效的。比如:
- 同一个交易被重复广播,导致多个区块里有相同的交易;
- 交易失败的交易,其实不需要保留完整的回执,只需要保留交易ID和失败原因即可;
- 历史区块的交易,很多已经失去了业务意义,比如某个过期的投票、某个已经结算的订单。
优化的核心是“按需存储”,也就是只保留必要的信息,去掉冗余部分。
2.2 状态数据的冗余优化
FISCO BCOS的状态存储用的是Merkle Patricia Trie(MPT)结构,默认会保留所有历史状态的根哈希和对应的节点数据。比如你查询某个合约100天前的状态,节点可以通过历史根哈希找到对应的状态数据。但对于大部分联盟链来说,并不需要保留这么久的历史状态,通常只需要保留最近30天、90天的状态即可。
优化的核心是“定期清理历史状态”,只保留最近一段时间的状态数据,之前的历史状态可以删除。
三、状态裁剪策略的具体实现
状态裁剪是解决磁盘占满问题的核心,FISCO BCOS本身提供了状态裁剪的功能,但很多人不知道怎么用,或者用错了。
3.1 状态裁剪的原理
状态裁剪的原理很简单:节点在运行过程中,会定期生成一个新的状态快照,然后删除早于某个时间点的历史状态数据。比如你设置裁剪周期为30天,那么节点会保留最近30天的状态,30天之前的状态会被删除。
需要注意的是,状态裁剪不会影响当前的状态,也不会影响区块数据的完整性,只是删除了历史状态的索引和数据。
3.2 状态裁剪的配置示例
技术栈:FISCO BCOS 2.0+
首先,我们需要修改节点的配置文件config.ini,开启状态裁剪功能。
# 开启状态裁剪,1表示开启,0表示关闭
state_prune=1
# 裁剪周期,单位是天,这里设置为30天
state_prune_period=30
# 裁剪触发的区块间隔,单位是块,这里设置为每1000个块触发一次裁剪
state_prune_block_interval=1000
配置完成后,需要重启节点生效。重启后,节点会自动按照配置的参数进行状态裁剪。
我们可以通过命令查看状态裁剪的运行情况:
# 进入节点的控制台
bash console.sh
# 查看状态裁剪的配置和运行状态
getStatePruneStatus
输出结果示例:
State Prune Status:
Enabled: true
Period: 30 days
Last Prune Block: 123456
Last Prune Time: 2024-05-20 12:00:00
Next Prune Block: 124456
3.3 状态裁剪的注意事项
- 裁剪周期不能设置得太短:如果设置为1天,那么节点只能查询最近1天的历史状态,可能会影响业务的追溯需求。
- 裁剪周期不能设置得太长:如果设置为365天,那么状态数据还是会占用大量的磁盘空间,失去了裁剪的意义。
- 所有节点的裁剪周期要一致:如果部分节点裁剪周期短,部分节点裁剪周期长,可能会导致节点之间的数据不一致,影响共识。
四、存量数据的清理方案
如果节点已经运行了很长时间,磁盘已经占满了,这时候就需要对存量数据进行清理。存量清理分为两种情况:区块数据清理和状态数据清理。
4.1 状态数据的存量清理
状态数据的存量清理可以用FISCO BCOS提供的工具state_prune_tool来完成。
技术栈:FISCO BCOS 2.0+ 首先,我们需要停止节点的运行:
# 停止节点
bash stop.sh
然后,使用state_prune_tool工具清理早于指定时间的状态数据。比如我们要清理早于2024年1月1日的状态数据:
# 进入节点的bin目录
cd node/bin
# 执行状态清理命令
./state_prune_tool --data-dir ../data --prune-before "2024-01-01 00:00:00"
命令执行过程中会输出清理的进度:
Start pruning state data before 2024-01-01 00:00:00...
Total state nodes: 1234567
Pruned state nodes: 987654
Remaining state nodes: 246913
Pruning completed successfully.
清理完成后,重启节点即可:
bash start.sh
4.2 区块数据的存量清理
区块数据的存量清理相对复杂一些,因为区块数据是不可篡改的,清理后会影响数据的完整性。所以区块数据的清理需要满足一个前提:所有节点都已经同步到最新的区块,并且业务上不需要再追溯清理的区块数据。
区块数据的清理步骤如下:
- 停止所有节点的运行。
- 选择一个节点作为“源节点”,这个节点的区块数据是完整的。
- 对源节点的区块数据进行裁剪,只保留需要的区块范围。比如我们要保留最近6个月的区块,那么就删除6个月之前的区块文件。
- 其他节点从源节点同步裁剪后的区块数据。
需要注意的是,区块数据清理后,节点将无法查询到被清理的区块的交易信息,所以一定要谨慎操作。
五、应用场景、优缺点与注意事项
5.1 应用场景
- 联盟链长期运行后磁盘空间不足的场景:这是最常见的场景,通过状态裁剪和存量清理可以释放大量的磁盘空间。
- 对历史状态追溯需求不高的场景:比如一些业务场景只需要保留最近几个月的状态,不需要保留几年前的状态。
- 节点服务器磁盘资源有限的场景:比如一些小型联盟链的节点部署在云服务器上,磁盘空间有限,需要定期清理数据。
5.2 技术优缺点
优点:
- 可以大幅释放磁盘空间:比如某项目清理后,磁盘占用从120G降到了15G,释放了87.5%的空间。
- 不影响当前业务的正常运行:状态裁剪和存量清理都是在不影响当前状态和最新区块的前提下进行的。
- 配置简单,操作方便:FISCO BCOS提供了现成的配置和工具,不需要开发额外的程序。
缺点:
- 清理后无法追溯历史数据:状态清理后无法查询历史状态,区块清理后无法查询历史区块的交易信息。
- 操作不当可能导致节点异常:比如配置错误、清理过程中节点重启、所有节点裁剪周期不一致等,都可能导致节点出现问题。
- 清理过程会占用一定的系统资源:存量清理的时候,工具会扫描整个状态数据库,会占用CPU和磁盘IO资源,可能会影响其他业务的运行。
5.3 注意事项
- 清理前一定要备份数据:不管是状态裁剪还是存量清理,都有可能出现意外,所以一定要先备份
data目录下的所有数据。 - 清理前一定要确认业务需求:比如业务上是否需要追溯历史状态、是否需要保留所有的区块数据。
- 清理过程中不要操作节点:存量清理的时候,节点必须停止运行,不能中途启动节点。
- 清理后要验证节点的运行状态:清理完成后,要检查节点的日志、共识状态、交易处理是否正常。
六、文章总结
FISCO BCOS长期运行后磁盘占满的问题,本质上是落盘数据结构和状态存储逻辑没有针对长期运行做优化。解决这个问题的核心是“状态裁剪”和“存量清理”,状态裁剪是预防问题的长期策略,存量清理是解决已经出现的磁盘占满问题的应急方案。
在实际操作中,一定要根据业务需求合理配置裁剪周期,清理前备份数据、确认业务需求,清理后验证节点状态。只有这样,才能在不影响业务的前提下,有效解决磁盘占满的问题,保证联盟链的长期稳定运行。
评论
围绕“FISCO BCOS长期运行后区块数据占用的磁盘空间远超预期,落盘数据格式与状态裁剪策略的优化空间在哪里,如何做存量清理”参与讨论