3. 长安链数据管理

3.1. 数据库的选型与配置

3.1.1. 数据库选型

长安链数据库划分是按照存储数据的功能和特性分为以下几种数据库:

  • BlockDB 存储区块与交易

  • StateDB 存储最新的世界状态

  • HistoryDB 存储状态变更历史,账户交易历史和合约调用历史,是可选的

  • ResultDB 存储合约执行的读写集,是可选的

  • ContractEventDB 存储合约事件日志数据库,是可选的

在数据持久化存储上,长安链支持常用的数据库来存储账本数据,如LevelDB、BadgerDB、TikvDB、MySQL等数据库,业务可选择其中任意一种数据库来部署区块链,每种数据库的简介如下:

  • LevelDB,默认采用的数据库引擎,LevelDB作为一款嵌入式KV数据库,默认集成在长安链节点中,无需部署,性能也相对关系型数据要更好。

  • BadgerDB,作为另一种形式的KV单机数据库的实现,也是嵌入式KV数据库,性能在写入value比较大时比LevelDB更高,但是读性能可能差于LevelDB

  • TikvDB,作为KVDB的横向扩容版本,需要单独启动tikv服务,底层使用rocksdb,性能更高。tikv部署流程

  • MySQL, (provider为:chainmaker.yml -> storage -> blockdb_config/statedb_config/resultdb/… -> provider)

    • 采用关系型数据库的方式(provider: sql),支持schema和富查询,性能较KV数据库低,目前关系型数据库与区块链的状态数据并不能很好的结合,导致很少有区块链采用关系型数据库作为状态数据库。原因主要有两点:1.区块链需要对智能合约所读写的状态数据做严格的控制和校验,而SQL语句相对区块链来说过于灵活,难以控制;2.需要提前创建库表和索引,需要针对不同的智能合约创建不同的数据库表结构,不够灵活。目前长安链支持MySQL存储引擎,在系统数据如Block DB上支持区块元信息、交易信息的关系型语义,状态数据库支持kv的方式和智能合约编写SQL语句方式读写状态数据(world state)。

    • 采用模拟非关系型数据库的方式(provider: sqlkv),类似KV数据库的方式组织数据存储到mysql中,适合搭配BFDB存储模式使用(用户数据量偏大时可将mysql数据库替换成TDSQL等分布式存储数据库)(推荐使用)。

长安链的存储分为RawDB(非文件存储)和BFDB(文件存储)两种方式 (详见: 长安链数据存储

  • RawDB:RawDB是指将数据完全存储在关系或非关系型数据库中RawDB的优点是数据存储在数据库中

  • BFDB:BFDB是指将区块数据存放在”区块文件存储”中,然后将”区块文件存储”的数据索引信息存放在区块数据库、结果据库,其他业务数据库(状态数据库、历史数据库、事件数据库)与RawDB的数据一样

  • 存储方式选型:

节点数据存储类型 别称 配置方式 chainmaker-go支持版本 blockdb/resultdb/statedb/historydb等的provider 是否推荐
RawDB 非文件存储 disable_block_file_db:true >= v1.2.4 leveldb/badgerdb/tikvdb/sql
BFDB 文件存储 disable_block_file_db:false >= v2.2.0 leveldb/badgerdb/tikvdb/sqlkv
  • 注:

    • “长安链节点” BFDB归档方案不支持采用sql方式(provider: sql)存储的节点数据,用户可以使用sqlkv(provider: sqlkv)方式替代

    • 采用sql方式(provider: sql)存储的节点额外支持在链上部署支持sql的合约,但该sql合约性能偏低,不推荐使用

    • 表格中的provider为:chainmaker.yml -> storage -> blockdb_config/statedb_config/… -> provider

3.1.2. 数据库配置

长安链的数据库配置在chainmaker.yml中,具体详见: 长安链数据存储

3.1.3. 数据库选择建议

在数据库选择时,需要根据自身业务类型和数据量以及运营模式而定

  • 在区块链数据不到1T的情况下,建议使用LevelDB作为默认的数据库;(provider:leveldb)

  • 如果状态数据中存储了大量上KB级别的Value,可以将BadgerDB作为状态数据库的底层;(provider:badgerdb)

  • 如果预期数据量上T甚至更多且具备数据库自行运维能力,则建议采用TiKV作为数据库底层;(provider:tikvdb)

  • 如果预期数据量上T甚至更多但是不具备数据库自行运维能力,则建议采用TDSQL等常见的分布式云关系数据库作为数据库底层;(provider:sqlkv)

  • 如果支持的业务复杂,合约需要大量富查询和各个字段的索引查询,汇总等,建议采用MySQL作为状态数据库底层,并且使用SQL语句来编写智能合约。(但该合约性能偏低,不建议使用)(provider:sql)

在选择了不同的数据库作为provider后,其后续的子配置项就是这个数据库的参数配置,比如provider: leveldb,那么后续子配置项就是leveldb_config,其中store_path是最重要的配置参数,其他选型可以采用默认值即可,除非您非常明确这个选项对数据库的影响。

如果出于性能的考虑,可以关闭HistoryDB、ResultDB和ContractEventDB,这些数据库可以帮忙开发者查询更详细的记录,用于区块链浏览器或者数据分析的情况。

3.2. 数据归档&恢复功能

节点归档只在RawDB模式下,节点版本需要>= v1.2.4

cmc的归档功能是指将链上数据转移到独立存储上,归档后的数据具备可查询、可恢复到链上的特性。 为了保持数据一致性和防止误操作,cmc实现了分布式锁,同一时刻只允许一个cmc进程进行转储。 cmc支持增量转储和恢复、断点中继转储和恢复,中途退出不影响数据一致性。

注意:mysql需要设置好 sql_mode 以root用户执行 set global sql_mode = ‘ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION’;

主要参数说明如下:

  --sdk-conf-path:指定cmc使用sdk的配置文件路径
  --chain-id:指定链Id
  --type:指定链下独立存储类型,如 --type=mysql 默认mysql,目前只支持mysql
  --dest:指定链下独立存储目标地址,mysql类型的格式如 --dest=user:password:localhost:port
  --target:指定转储目标区块高度,在达到这个高度后停止转储(包括这个块) --target=100
  	也可指定转存目标日期,转储在此日期之前的所有区块 --target="2021-06-01 15:01:41"
  --blocks:指定本次要转储的块数量,注意:对于target和blocks这两个参数,cmc会就近原则采用先符合条件的参数
  --start-block-height:指定链数据恢复时的起始区块高度,如设置为100,则从已转储并且未恢复的最大区块开始降序恢复链数据至第100区块
  --secret-key:指定密码,用于链数据转储和链数据恢复时数据一致性校验,转储和恢复时密码需要一致
  • 根据时间转储,将链上数据转移到独立存储上,需要权限:sdk配置文件中设置与归档节点同组织的

    admin用户

    ./cmc archive dump --type=mysql \
    --dest=root:password:localhost:3306 \
    --target="2021-06-01 15:01:41" \
    --blocks=10000 \
    --chain-id=chain1 \
    --sdk-conf-path=./testdata/sdk_config_pwk.yml \
    --secret-key=mypassword
    
  • 根据区块高度转储,将链上数据转移到独立存储上,需要权限:sdk配置文件中设置与归档节点同组织的

    admin用户

    ./cmc archive dump --type=mysql \
    --dest=root:password:localhost:3306 \
    --target=100 \
    --blocks=10000 \
    --chain-id=chain1 \
    --sdk-conf-path=./testdata/sdk_config_pwk.yml \
    --secret-key=mypassword
    
  • 恢复,将链下的链数据恢复到链上,需要权限:sdk配置文件中设置与归档节点同组织的

    admin用户

    ./cmc archive restore --type=mysql \
    --dest=root:password:localhost:3306 \
    --start-block-height=0 \
    --chain-id=chain1 \
    --sdk-conf-path=./testdata/sdk_config_pwk.yml \
    --secret-key=mypassword
    
  • 根据区块高度查询链下已归档区块

    ./cmc archive query block-by-height [blockheight] \
    --chain-id=chain1 \
    --sdk-conf-path=./testdata/sdk_config_pwk.yml \
    --type=mysql \
    --dest=root:password:localhost:3306
    
  • 根据区块hash查询链下已归档区块

    ./cmc archive query block-by-hash [blockhash] \
    --chain-id=chain1 \
    --sdk-conf-path=./testdata/sdk_config_pwk.yml \
    --type=mysql \
    --dest=root:password:localhost:3306
    
  • 根据txid查询链下已归档区块

    ./cmc archive query block-by-txid [txid] \
    --chain-id=chain1 \
    --sdk-conf-path=./testdata/sdk_config_pwk.yml \
    --type=mysql \
    --dest=root:password:localhost:3306
    
  • 根据txid查询链下已归档tx

    ./cmc archive query tx [txid] \
    --chain-id=chain1 \
    --sdk-conf-path=./testdata/sdk_config_pwk.yml \
    --type=mysql \
    --dest=root:password:localhost:3306
    

3.3. 数据重建(rebuild_dbs)

当因为操作不当,恶意篡改等原因,导致状态数据库的数据与区块链历史数据或其他节点不一致,那么就意味着状态数据库损坏,需要利用账本数据进行数据重建。具体详见: 长安链数据存储