fabric 的数据库原理

数据库的必要性:

  • fabric 除了使用账本(区块)存储交易数据以外,还会使用有几个 nosql 数据库,用来共同维护 fabric的交易数据。我们知道 orderer 节点将 client 来发的交易进行排序切块后通过 grpc 协议,发送给 peer 的 leader 节点, leader 节点再将接收到的区块通过 gossip 协议分发给组织内其他的 peer 节点。这样每个 peer 节点都会拥有同样的区块文件。虽然这个区块文件记录了所有了的交易数据,但是有个致命的缺陷 —— 很难检索某个交易对象的历史交易记录,且很难查某个交易对象的当前状态,甚至有时候还可能需要存放一些私有数据。
    因此,fabric 使用多个 nosql 来记录交易对象的索引、交易区块的索引、交易对象的当前值以及特定的私有数据,用于提高 fabric 的交易效率和 fabric 本身账本的维护。

账本的分类:

历史数据库(history DB):

功能:用于存储所有 有效交易 的关键信息的数据库,通过历史状态数据能查询到每一笔有效交易是在哪个区块中,以及对应的交易ID是多少

  • 历史状态数据库使用 levelDB 存储,并且只存储有效记录的 key(这里的key指的是交易中的key),而不存储交易的 value

  • 使用组合键的方式进行存储到数据库中,构成组合键的源码为:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // CompositeKeySep is a nil byte used as a separator between different components of a composite key
    var CompositeKeySep = []byte{0x00}

    //ConstructCompositeHistoryKey builds the History Key of namespace~key~blocknum~trannum
    // using an order preserving encoding so that history query results are ordered by height
    func ConstructCompositeHistoryKey(ns string, key string, blocknum uint64, trannum uint64) []byte {
    var compositeKey []byte
    compositeKey = append(compositeKey, []byte(ns)...)
    compositeKey = append(compositeKey, CompositeKeySep...)
    compositeKey = append(compositeKey, []byte(key)...)
    compositeKey = append(compositeKey, CompositeKeySep...)
    compositeKey = append(compositeKey, util.EncodeOrderPreservingVarUint64(blocknum)...)
    compositeKey = append(compositeKey, util.EncodeOrderPreservingVarUint64(trannum)...)
    return compositeKey
    }
  • 组合键示意图:

    • namespace : 命名空间,命名空间是读写集的属性,用于通道之间的数据隔离的。
    • compositeKeySep: 仅仅用于组合键中键之间连接(fabric 使用 0x00 作为连接符)。
    • Key: 将 KV 键值对写入到账本中的 Key
    • block: 交易所在的区块号
    • ID: 交易的 ID 编号(fabric 会为每一个交易生成一个交易 ID (txid))

世界状态数据库(world state DB):

功能:世界状态数据库用于记录交易对象最终状态的数据库,如果把历史状态数据库当做你的交易流水,那么世界状态数据库就类似于银行卡的余额。

  • 支持 levelDB 和 couchDB

私有数据库(private DB):

功能:私用数据库用于保存隐私交易数据

  • levelDB
  • 隐私的交易数据不会直接存放到账本(ledger)中,但是隐私交易数据的 keyvalue 的 hash 会存到账本中。
  • 隐私交易数据背书通过后,会通过 gossip 协议将隐私交易数据发送给被授权的其他 peer 节点。

区块索引数据库(Index DB):

功能:区块索引数据库用于存储具体的某个区块某个交易区块数据文件中的位置

  • 每当区块数据文件中存储一个区块,Index DB 都会为这个区块建立一批索引。
  • fabric 网络中只有一个 Index DB(即多通道共用一个 Index DB)
  • 数据库默认位置:/var/hyperledger/production/ledgersData/chains/index

idstore 数据库:

功能:idstore 数据库用于存储不同账本的 ledgerID 的键值对及其创世区块

  • 默认使用路径:/var/hyperledger/production/ledgersData/ledgerProvider/
  • ledgerID 的值其实就是 channelID
  • 其中有一对关键的键值对用来存储账本ID,其 key 为 underConstructionLedgerKey, value 为 ledgerID,这个是用于当 peer 加入通道的时候或者账本数据恢复的时候的数据恢复标志位。

补充:

区块数据文件:

功能:区块数据文件用于存储区块长度、区块头、每天交易的长度以及交易数据

  • 默认存储路径:/var/hyperledger/production/ledgersData/chains/chains/mychannel
  • 图解: