美文网首页
长安链源码学习--账本 (七)

长安链源码学习--账本 (七)

作者: 明神特烦恼 | 来源:发表于2021-06-15 15:40 被阅读0次

    作者:明神特烦恼
    公众号:明神特烦恼

    在共识完成后进行区块提交阶段,对调用账本模块来对区块以及交易等进行持久化,本章节将一起分析一下账本模块实现过程。

    带着问题读代码:
    1)存储方式有哪些?
    2)账本存储哪些内容,有哪些索引?
    3)在写如多张数据库表,如果发生意外掉电,如何保证原子性?如何回滚?

    第一个问题:存储方式有哪些?

       长安链支持nosql、sql两种形式的存储,其中nosql支持rocksdb、leveldb,sql支持mysql、sqlite。多种存储形式满足不同应用场景及业务要求。
       无论何种存储方式,上面已经高层封装,屏蔽掉底层存储逻辑,化简其他模块的操作复杂度,后面的分析以nosql为例。

    const (
        UnknownDb EngineType = 0
        LevelDb   EngineType = 1
        RocksDb   EngineType = 2
        MySQL     EngineType = 3
        Sqlite    EngineType = 4
    )
    

    第二个问题:账本存储哪些内容,有哪些索引?(以nosql为例)

    1.写块流程:

    1)记录最新写入的块号lastBlockNumKeyStr

    2)记录BlockHeightKeyBlockInfo关系:

    • BlockHeightKey = blockNumIdxKeyPrefix + blocknum
    • BlockInfo = 序列化(Header + Dag + TxIds + AdditionalData)

    3)记录BlockHashKeyBlockHeight关系

    • BlockHashKey = blockHashIdxKeyPrefix + blockhash
    • BlockHeightKey

    4)记录TxidKeyTxInfo 关系

    • TxidKey = txIDIdxKeyPrefix + txid
    • TxInfo = 序列化(Transaction)

    5)记录BlockTxidKeyBlockHeightKey 关系

    • BlockTxidKey = blockTxIDIdxKeyPrefix + txid
    • BlockHeightKey

    6)记录LastConfigKeyBlockHeightKey 关系(只有配置块记录该关系)

    • LastConfigKey = lastConfigBlockNumKey
    • BlockHeightKey

       账本模块通过上述记录的内容提供服务有:获取最后一个区块块号;通过块号获取块内容;通过块Hash获取块号;通过txid获取交易详情;通过txid获取其所在块号;获取最新配置块号。

    2.写世界状态流程:

    1)记录当前世界状态对应的最新块号stateDBSavepointKey

    2)记录写入数据的KV值 Or 删除Key

    • Key:合约Name + WriteKey
    • Value:世界状态值
    • OP: 插入

    Or

    • Key:合约Name + WriteKey
    • OP: 删除
    3.写入历史数据库流程:

    1)记录当前历史数据库对应的最新块号historyDBSavepointKey

    2)记录操作的Key与块号、Txid关系

    • Key:合约Name + WriteKey + BlockHeight + Txid

    3)记录操作账户与块号、Txid关系

    • Key:账号ID + BlockHeight + Txid

    4)记录合约与块号、Txid关系

    • Key:合约名称 + BlockHeight + Txid
    4. 写入合约结果流程:

    1)记录当前合约结果数据库对应的最新块号:resultDBSavepointKey
    2)记录RWSetsTxid与读写集之间的关系

    • RWSetsTxid:txRWSetIdxKeyPrefix + txid
    • 读写集:序列化(TxRWSet)

    第三个问题:在写如多张数据库表,如果发生意外掉电,如何保证原子性?如何回滚?

       上面提到了多个流程,每个流程是原子操作,保证了事务一致性。但多个流程之间是非原子的,也就是世界状态写入完成,而写块流程还未完成,如果此时发生掉电(进程kill等),那么节点自身的数据将不完整,这是不被允许的。
       为解决掉电问题,长安链处理办法如下:
        - 同步写binlog,binlog非随机写入,机械硬盘执行速度较快。
        - 并发完成写块流程、写世界状态流程、历史数据库流程等。
        - 写入完成后,异步清理binlog。

       当进行crash后重启检测及恢复流程:
        - 创建存储Handler时判断binlog是否有内容,如果有下一步。
        - 获取binlog中记录最后一个log的块高度logSavepoint
        - 获取写块流程的最新块号lastBlockNumKeyStr,如果logSavepoint > lastBlockNumKeyStr,表示有区块数据未执行写块流程,调用CommitBlock进行写块。
        - 其他几个流程同理。

    注意:
    1) 这里要保证每个流程自身内部是原子的。
    2) 恢复流程中并未删除binlog,这是因为binlog的处理是每100个块删除一次,并不需要实时删除,也不会产生存储泄露。
    3)恢复流程没有回滚,只有继续写入。

    相关文章

      网友评论

          本文标题:长安链源码学习--账本 (七)

          本文链接:https://www.haomeiwen.com/subject/gxezeltx.html