Hyperledger Fabric数据存储结构
概述
Hyperledger Fabric支持多链。每个链对应一套账本。所以区块链每个peer节点会维护多套账本。每个超级账本包含以下元素:
- 账本编号:快速查询存在哪些账本
- 账本数据:实际的区块数据存储
- 区块索引:快速查询区块/交易
- 状态数据:最新的世界状态数据
- 历史数据:跟踪键的历史
每个peer节点会维护4个DB,它们分别是:
- idStore,存储chainID。用户快速查询节点存在哪些账本
- stateDB,存储world state(默认为LevelDB,可替换为CouchDB)
- historyDB,存储stateDB的key的版本变化
- blockIndex,存储区块文件索引
如下图:(因只展示了单链的数据存储情况,故idStore未在图中展示)
其中,世界状态和账本数据是两个账本最重要的组成部分。
账本编号(LedgerId)
账本编号的数据存储在LevelDB数据库中,只是记录了有哪些账本,创建新的账本会检查是否有相同的账本编号存在,保证了全局唯一性。账本编号库并不存储与区块相关的数据。
账本数据(Ledger)
账本数据是以二进制文件的形式存储的,每个账本数据存储在不同的目录下。账本数据的所有操作都是通过区块文件管理器(blockfileMgr)实现的。区块文件管理器创建的文件以“blockfile_”为前缀,6位数字为后缀,后缀必须是从小到大连续的数字,中间不能有缺失。默认的区块文件大小上限是64M(目前这个大小是硬编码在代码中的),一个账本能保存最大数据量大概是61TB。
下图是区块账本数据的结构:
- 信息描述:B代表区块链账本数据。其中B0为创世区块,其中包含了区块头H0,区块数据D0(因为是创世区块,区块数据里不包含交易数据),区块元数据M0。区块B1区块头H1,其中包含了前一个区块B0的加密hash和本身区块的加密hash。
区块(Block)
每个区块包含三个部分。
区块头(Block Header)
区块数(Block Number):一个从0(创世区块)开始的整数,并且对于附加到区块链的每个新块增加1。
当前区块hash(Current Block Hash):当前区块中包含的所有交易的hash。
前一区块hash(Previous Block Hash):前一个区块的hash。
区块数据(Block Data)
区块数据包含了一组交易,交易在区块创建时写入。
区块元数据(Block Matadata)
元数据包含了区块创建时间,写入程序的证书,公钥和签名。
交易(transaction)
交易头(Header)
交易头中包含了一些重要的交易元数据,例如链码的名称、版本。
签名(Signature)
包含由application创建的加密签名。它是由application的私钥生成的。用于检查交易有没有被篡改。
交易提案(Proposal)
包含了由application生成的交易请求参数。
交易提案返回(Response)
包含了由背书节点返回的模拟执行结果(读写集RWset)。这是链码的输出,如果交易验证通过,这些数据会被用于更新世界状态。
背书(Endorsements)
包含了交易的背书。一个返回对应多个背书。
区块索引(Block Index)
Hyperledger Fabric提供了多种区块索引的方式,以便能快速找到区块。索引的内容是文件位置指针(File Location Pointer)。文件位置指针由三个部分组成:所在文件的编号(fileSuffixNum)、文件内的偏移量(offset)、区块占用的字节数(bytesLength)。
状态数据(State Database)
状态数据(state database)记录的是交易执行的结果,最新的状态代表了通道(channel)上所有键的最新值,所以又称为“世界状态”。状态数据库目前支持LevelDB和CouchDB。
- LevelDB(默认的KV数据库):支持键的查询,组合键的查询、范围键的查询。
- CouchDB(可选的KV数据库):支持键的查询、组合件的查询,还有复杂的查询。
不同账本的状态数据库存放在不同的目录下,不同链码的数据是按链码编号(chaincodeID)作为命名空间来划分数据。
状态数据库的基本操作是基于键值对的管理。读取状态数据的时候不用指定版本,读取到的状态数据是某个时刻最新的版本,返回的数据包含版本和数据两个部分。
- 信息描述:状态数据库W包含两条数据。第一条,key=CAR1,value=Audi,版本=0.第二条,key=CAR2,value为一条复杂json表达式,版本也是0.
上述图片描述了世界状态数据库的基本存储结构。当发生交易后,改变数据内容,并变更版本号。
历史数据(History Database)
历史数据记录了每个状态数据的历史信息,历史信息是保存在LevelDB数据库中的。