数据库的必要性:
- 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)中,但是隐私交易数据的
key
和value 的 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
- 图解: