
本文共 3130 字,大约阅读时间需要 10 分钟。
FISCO BCOS分布式存储设计解读
一丶数据容量现状及方案
1.区块链技术发展面临的挑战
- 数据容量
- 吞吐性能
- 数据孤岛(跨链)
- 隐私保护
2.数据容量困境
随着区块链的普及程序和应用程序不断提高,区块链存储的数据将越来越大,磁盘占用也越来越大
交易数增加 | 数据量增加 | 交易性能衰弱 同步数据耗时以太坊15年7月30号诞生 目前区块链数据已经膨胀5.4T ,同步时间需要花几个月时间,每天以3-4GB的数据在增长
比特币09年1月3号诞生 完完整整的区块链数据基本也就200多G,同步时间基本在24-36小时3.交易性能衰减分析
交易上链及出块生成数据,增加存储的KV个数——>本地RocksDB存储
sst的文件个数及层数增加——>(sst文件(数据文件、sst表):SST是Sorted Sequence Table (排序队列表),他们是排好序的数据文件)交易中读写KV的平均耗时增加——>单个交易处理时间增加,TPS降低4.海量数据备份与归档
目标
- 节点通过控制本地存储的整体数据量,稳定联机交易性能
- 外部通过导出节点数据,实现对历史数据的备份归档及容量的平行扩展 方案
- 通过对写入本地的链上数据按照一定维度进行划分,分别存于不同存储实例,从而**控制节点所访问的单个存储实例的大小(sst文件个数及层数),稳定链处理能力
- 归档:同时,对本地的存储实例,在实现数据迁移到外部存储进行备份归档后有选择地进行删除,从而控制本地存储的整体数据量,并在链外部实现存储的平行扩容
5.区块链基础架构
6.数据提交流程
FISCO BCOS中需要存储的数据可以分为两部分
- 一部分是经过共识的链上数据,包括交易,收据,区块和合约数据等
- 另一部分是各节点维持区块链运行所需的数据,包括当前块高,链上交易数和一些交易区块相关的索引信息
区块链上的新区块来自于同步模块和共识模块,以同步模块为例,当拿到新的区块之后,同步模块会调用BlockVerifier模块执行和验证区块,如果验证通过则调用BlockChain模块将区块和执行区块产生的数据提交给存储模块,由存储模块负责将数据序列化写入数据库。

7.存储模块概览
数据提交到存储模块之后,是一种抽象的表结构,存储模块首先将提交的数据加入缓存层,以提高查询性能
完成缓存的更新之后,需要提交的数据会加入提交队列,由缓存层负责异步提交到适配层,如果关闭了缓存设置,则同步到适配层适配层需要把提交的数据从FISCO BCOS的抽象表结构组织形式转化为后端对应存储的组织形式,对于MySQL这种关系型数据库,则直接将存储模块的表结构对应到数据库表结构即可,例如_sys_config_这个表在MySQL中如下图所示:

对于RocksDB或LevelDB这种KV的存储模式,将表名和插入时设置的主Key拼起来作为数据库的KEY,对应的数据则序列化为VALUE,对应于_sys_config_这个表,以及tx_count_limit这个主key的数据,其在KV数据库中的KEY为_sys_config_,_tx_count_limit,VALUE所对应的数据化后的字符串
8.数据库存储类型
关系型
非关系性9.什么选择RocksDB?
FISCO BCOS从1.0版本开始就是用LevelDB作为底层数据存储=引擎,在使用过程中我们也碰到一些小问题,例如内存占用高,文件描述符超限导致节点被干掉,节点被kill后可能导致DB损坏等
重构2.0版本时,为了更高的性能,需要一个更优秀的存储引擎,这个存储引擎应该满足下面这些条件:
- 开源且有持续的维护
- 读写性能要比LevelDB更高
- 嵌入式KV数据库,能够支持大数据量场景下的读写
- 与LevelDB类似的接口,降低迁移成本
RocksDB fork 自LevelDB,开源且由fackbook维护,相比于LevelDB有较明显的性能提升,保持了与LevelDB一致的接口,极低的迁移成本
10.RocksDB简介
一个可嵌入的,持久型的Key-value存储,为了更快速的存储环境而生
- 高性能
- 为快速存储而优化
- 可适配性
- 基于和高级的数据库交互
11.方案实施关键点
- 本地数据导出策略
- 本地数据管理及裁剪策略
- 外部数据组织策略
- 访问外部数据策略
12.区块链三级存储体系架构
- 分高速缓存,硬盘主存和辅助存储三级
- 调和性能容量两者矛盾
二丶本地数据导出策略
1.BinLog机制
- 数据先写BinLog在写缓存
- 启动阶段将比较BinLog块高及,判断是否重放BinLog,用于保证缓存机制可靠性
- 节点数据以BinLog为载体进行导出,供外部存储使用
2.BinLog优势
BinLog机制 vs 数据库快照
- 要求基于区块维度的数据快照
- 节点可选存储引擎包括RocksDB数据库和MySQL数据库,均可实现快照
- 减少适配两类存储引擎的开发工作量,服用已有BinLog机制
**TLV(Tag/Length/Value) vs protobuf


3.BinLog协议
4.数据导出完整性
节点数据的完整导出,是节点访问外部归档数据的前提
- 方案:BinLog机制为确认数据的完整导出,对每个区块进行RCR32校验
- CRC选择:CRC32适合不大于512MB数据量的校验,相比较CRC16合适不大于16KB数据量的校验
- 负收益:CRC32耗时在BinLog生成流程(TLV编码,计算CRC,写文件)耗时占比50%
三丶本地数据管理及裁剪策略
1.本地数据管理策略
本地数据分段存储
- 本地数据分数据库实例存储
- 分段规则:按该数据落盘时的块高进行分段,写入对应的数据库
ScalableStorage模式
- 本地多个RocksDB数据库实例+一个MySQL数据库实例用于访问外部存储
- 节点在各自表中记录“区块块高”到“RocksDB数据库实例”的映射关系
数据一致性
先写入区块数据信息,再写入状态数据信息更新区块高度2.ScalableStorage模式
3.本地数据裁剪策略
本地数据分类
- 大原则:区块数据与状态数据分开存储
- 结合分布式存储表的数据量大小进行“区块与状态”的分类
本地数据裁剪
- 结合分段存储规则,对整个段的数据库进行删除,而非部分删除
- 裁剪表_sys_hash_2_block_和_sys_block_2_nonces_
四丶外部数据组织策略
1.外部数据表分类
外部数据存储引擎
MySQL,适合扩容及大数据分析 当前状态表- 作用:支持节点访问本地已裁剪的数据
- 生成:基于从节点拉取的BinLog进行数据恢复
- 特点:表名及内容与节点一致 历史状态表
- 作用:支持节点获取特定区块高度的历史状态
- 生成:“当前作状态”的更新操作 记录
- 特点:外部存储特有,与“当前状态表”——对应,表名为“当前状态表”的表名加后缀“d”
2.外部数据表举例
当前状态表_sys_config_
历史状态表_sys_config_d_
3.历史状态查询
- 基于表的物理主键_id_和节点指定的_num_两字段进行查询
- 海量数据使用分页查询
4.数据库访问方式
数据代理amdb-proxy
- 解耦节点处理和外部数据表存储逻辑
- 实现准入控制
**zdb直连MySQL
配置简单,方便易用5.数据可信访问
已有方案
- 对外部数据采用“使用时再验证”的方式
- 快照状态拉取+重跑最近区块交易
优化方案
链上增加全节点类型,存储所有数据并制作checkpoint五丶方案收益
1.方案正负收益
- 提升缓存机制可靠性
- 本地数据量减少四分之三
- 同步区块耗时减少56%
- BinLog引入5%的性能损耗