具体实施方式
这里将详细地对示例性实施例进行说明,其示例表示在附图中。下面的描述涉及附图时,除非另有表示,不同附图中的相同数字表示相同或相似的要素。以下示例性实施例中所描述的实施方式并不代表与本说明书一个或多个实施例相一致的所有实施方式。相反,它们仅是与如所附权利要求书中所详述的、本说明书一个或多个实施例的一些方面相一致的装置和方法的例子。
需要说明的是:在其他实施例中并不一定按照本说明书示出和描述的顺序来执行相应方法的步骤。在一些其他实施例中,其方法所包括的步骤可以比本说明书所描述的更多或更少。此外,本说明书中所描述的单个步骤,在其他实施例中可能被分解为多个步骤进行描述;而本说明书中所描述的多个步骤,在其他实施例中也可能被合并为单个步骤进行描述。
区块链一般被划分为三种类型:公有链(Public Blockchain),私有链(PrivateBlockchain)和联盟链(Consortium Blockchain)。此外,还可以有上述多种类型的结合,比如私有链+联盟链、联盟链+公有链等。
其中,去中心化程度最高的是公有链。加入公有链的参与者(也可称为区块链中的节点)可以读取链上的数据记录、参与交易、以及竞争新区块的记账权等。而且,各节点可自由加入或者退出网络,并进行相关操作。
私有链则相反,该网络的写入权限由某个组织或者机构控制,数据读取权限受组织规定。简单来说,私有链可以为一个弱中心化系统,其对节点具有严格限制且节点数量较少。这种类型的区块链更适合于特定机构内部使用。
联盟链则是介于公有链以及私有链之间的区块链,可实现“部分去中心化”。联盟链中各个节点通常有与之相对应的实体机构或者组织;节点通过授权加入网络并组成利益相关联盟,共同维护区块链运行。
基于区块链的基本特性,区块链通常是由若干个区块构成。在这些区块中分别记录有与该区块的创建时刻对应的时间戳,所有的区块严格按照区块中记录的时间戳,构成一条在时间上有序的数据链条。
对于链外产生的数据,可以将其构建成区块链所支持的标准的交易(transaction)格式,然后发布至区块链,由区块链中的节点设备对该交易进行共识,并在达成共识后,由区块链中作为记账节点的节点设备,将这笔交易打包进区块,在区块链中进行持久化存证。
在区块链领域,有一个重要的概念就是账户(Account);在实际应用中,通常可以将账户划分为外部账户和合约账户两类;外部账户就是由用户直接控制的账户,也称之为用户账户;而合约账户则是由用户通过外部账户创建的,包含合约代码的账户(即智能合约)。
对于区块链中的账户而言,通常会通过一个结构体,来维护账户的账户状态。当区块中的交易被执行后,区块链中与该交易相关的账户的状态通常也会发生变化。
在一个例子中,账户的结构体通常包括Balance,Nonce,Code和Storage等字段。其中:
Balance字段,用于维护账户目前的账户余额;
Nonce字段,用于维护该账户的交易次数;它是用于保障每笔交易能且只能被处理一次的计数器,有效避免重放攻击;
Code字段,用于维护该账户的合约代码;在实际应用中,Code字段中通常仅维护合约代码的hash值;因而,Code字段通常也称之为Codehash字段。
Storage字段,用于维护该账户的存储内容;对于合约账户而言,通常会分配一个独立的持久化的存储空间,用以存储该合约账户对应的存储空间中存储的合约数据;该独立的存储空间通常称之为该合约账户的账户存储。合约账户的存储内容通常会以key-value键值对的形式被构建成MPT(Merkle Patricia Trie)树的数据结构存储在上述独立的存储空间之中。MPT树是区块链领域用于存储和维护区块链数据的一种逻辑的树形结构,在这种树形结构中通常包括根节点、中间节点、叶子节点。
其中,基于合约账户的存储内容构建成的MPT树,通常也称之为Storage树。而Storage字段通常仅维护该Storage树的根节点的hash值;因此,Storage字段通常也称之为Storage Root hash字段。其中,对于外部账户而言,以上示出的Code字段和Storage字段的字段值均为空值。
对于大多数区块链模型,通常都会采用Merkle树;或者,基于Merkle树的数据结构的Merkle树变种等逻辑的树形结构,来存储和维护数据。例如,MPT树,就是一种用来存储和维护区块链数据的,融合了Trie字典树的树形结构的Merkle树变种。
以下以使用MPT树,来存储区块链数据为例进行说明;
在一个例子中,区块链中需要存储和维护的区块链数据,通常包括账户状态(state)数据、交易数据和收据数据;因此,在实际应用中,可以分别将上述账户状态数据、交易数据和收据数据以key-value键值对的形式,组织成MPT状态树(即world state)、MPT交易树和MPT收据树等三棵MPT树,分别进行存储和维护。
其中,除了以上三棵MPT树以外,合约账户对应的存储空间中存储的合约数据,通常也会被构建成为一棵MTP Storage树(以下简称为Storage树)。该 Storage树的根节点的hash值,会被添加到与该Storage树对应的合约账户的上述结构体中的Storage字段。
MPT状态树,是由区块链中所有账户(包括外部账户和合约账户)的账户状态(state)数据,以key-value键值对的形式组织成的MPT树;MPT交易树,是由区块链中的交易(transaction)数据,以key-value键值对的形式组织成的MPT树;MPT收据树,是区块中的交易在执行完毕后生成的与每笔交易对应的交易(receipt)收据,以key-value键值对的形式组织成的MPT树。
以上示出的MPT状态树、MPT交易树和MPT收据树的根节点的hash值,最终都会被添加至对应区块的区块头中。
其中,MPT交易树和MPT收据树均与区块相对应,即每一个区块都有自己的MPT交易树和MPT收据树。而MPT状态树是一个全局的MPT树,并不与某一个特定的区块相对应,而是涵盖了区块链中所有账户的账户状态数据。区块链每产生一个最新区块,则在该最新区块中的交易被执行之后,区块链中这些被执行交易的相关账户(可以是外部账户也可以是合约账户)的账户状态,通常也会随之发生变化。
例如,当区块中的一笔“转账交易”执行完毕后,与该“转账交易”相关的转出方账户和转入方账户的余额(即这些账户的Balance字段的字段值),通常也会随之发生变化。节点设备在区块链产生的最新区块中的交易执行完毕后,由于当前区块链中的账户状态发生了变化,因此节点设备需要根据区块链中所有账户当前的账户状态数据,来构建MPT状态树,用于维护区块链中所有账户的最新状态。
也即,每当区块链中产生一个最新区块,并且该最新区块中的交易执行完毕后,导致区块链中的部分账户的账户状态发生了变化,节点设备需要基于区块链中所有账户最新的账户状态数据,重新构建一棵MPT状态树。换句话说,区块链中每一个区块,都有一个与之对应的MPT状态树;该MPT状态树,维护了在该区块中的交易在执行完毕后,区块链中所有账户最新的账户状态。
请参见图1,图1为本说明书示出的一种将区块链中的各个区块链账户的账户状态数据以key-value键值对的形式组织成MPT状态树的示意图。
MPT树,是一种较为传统的经过改良的Merkle树变种,其融合了Merkle树和Trie字典树(也称之为前缀树)两种树形结构的优点。
在MPT树中通常包括三种节点,分别为叶子节点(leaf node),扩展节点(extension node)和分支节点(branch node)。其中,MPT树的根节点通常可以是扩展节点;MPT树的中间节点通常可以是分支节点或者其它的扩展节点。
其中,扩展节点和分支节点可以统称为字符节点,用于存储账户状态数据的key(即账户地址)对应的字符串的字符前缀部分;其中,对于MPT树而言,上述字符前缀部分通常是指共享字符前缀;所述共享字符前缀,是指所有账户状态数据的key(即区块链账户地址)所具有的相同的一个或者多个字符组成的前缀。而上述叶子节点,用于存储区块链数据的key对应的字符串的字符后缀部分和Value(即具体的账户状态数据)。
扩展节点,用于存储账户地址的共享字符前缀中一个或者多个字符(即图1示出的shared nibble),和该扩展节点链接的下一层的节点的hash值(即图1示出的Next node)。
分支节点,包含17个槽位,前16个槽位对应着key中的16个可能的十六进制字符,一个字符对应一个nibble(半字节),前16个槽位中的每一个槽位,分别表示一个账户地址的共享字符前缀中的一个字符,这些槽位用于填充该分支节点链接的下一层的节点的hash值。最后一个槽位为value槽位,一般为空值。
叶子节点,用于存储账户地址的字符后缀(即图1示出的key-end),和账户状态数据的value(即以上描述的账户的结构体);其中,账户地址的字符后缀和账户地址的共享字符前缀共同组成了一个完整的账户地址;所述字符后缀,是指除了账户地址的共享字符前缀以外的最后一个或者多个字符组成的后缀。
假设需要组织成MTP状态树的账户状态数据如下表1所示:
其中,需要说明的是,在表1中,前三行的账户地址对应的区块链账户为外部账户,Codehash和Storage root字段为空值。第4行的账户地址对应的区块链账户为合约账户,Codehash字段维护了该合约账户对应的合约代码的hash值;Storage root字段维护了该合约账户的存储内容构成的Storage树的根节点的hash值。
最终按照表1中的账户状态数据组织成的MPT状态树,如图1所示;该MPT状态树是由4个叶子节点,2个分支节点,和2个扩展节点(其中一个扩展节点作为根节点)构成。
在图1中,prefix字段为扩展节点和叶子节点共同具有的前缀字段。该prefix字段的不同字段值可以用于表示不同的节点类型。
例如,prefix字段的取值为0,表示包含偶数个nibbles的扩展节点;如前所述,nibble表示半字节,由4位二进制组成,一个nibble可以对应一个组成账户地址的字符。prefix字段的取值为1,表示包含奇数个nibble(s)的扩展节点;prefix字段的取值为2,表示包含偶数个nibbles的叶子节点;prefix字段的取值为3,表示包含奇数个nibble(s)的叶子节点。
而分支节点,由于其是并列单nibble的字符节点,因此分支节点不具有上述prefix字段。
扩展节点中的Shared nibble字段,对应该扩展节点所包含的键值对的key值,表示账户地址之间的共同字符前缀;比如,上表中的所有账户地址均具有共同的字符前缀a7。Next Node字段中填充下一个节点的hash值(hash指针)。
分支节点中的16进制字符0~f字段,对应该分支节点所包含的键值对的key值;如果该分支节点为账户地址在MPT树上的搜索路径上的中间节点,则该分支节点的Value字段可以为空值。0~f字段中用于填充下一层节点的hash值。
叶子节点中的Key-end,对应该叶子节点所包含的键值对的key值,表示账户地址的最后几个字符(账户地址的字符后缀)。从根节点搜索到叶子节点的搜索路径上的各个节点的key值,构成了一个完整的账户地址。该叶子节点的Value字段填充账户地址对应的账户状态数据;例如,可以对上述Balance,Nonce,Code和storage等字段构成的结构体进行编码后,填充至叶子节点的Value字段。
进一步的,如图1所示的MPT状态树上的node,最终也是以Key-Value键值对的形式存储在数据库中;
其中,当MPT状态树上的node在数据库中进行存储时,MPT状态树上的node的键值对中的key,可以为node所包含的数据内容的hash值;MPT状态树上的node的键值对中的Value,为node所包含的数据内容。
在将MPT状态树上的node存储至数据库时,可以计算该node所包含的数据内容的hash值(即对node整体进行hash计算),并将计算出的hash值作为key,将该node所包含的数据内容作为value,生成Key-Value键值对;然后,将生成的Key-Value键值对存储至数据库中。
由于MPT状态树上的node,以Key-value键值对的形式进行存储;其中,Key可以是node所包含的数据内容的hash值,Value可以是node所包含的数据内容;因此,在需要查询MPT状态树上的node时,通常可以基于node所包含的数据内容的hash值作为key来进行内容寻址。
请参见图2,图2为本说明书示出的一种将合约账户对应的存储空间中存储的合约数据组织成MPT storage树的示意图。
请继续参见表1,表1中示出的账户地址为“a77d397”的账户为合约账户,因此该合约账户对应的存储空间中存储的合约数据会被组织成一颗storage树;其中,该storage树的根节点的hash值S1,会被添加到图1示出的MTP状态树中与该合约账户对应的叶子节点中的storage root字段里。
假设该合约账户的存储空间中存储的合约数据如下表2所示:
需要说明的是,合约账户的存储空间中存储的合约数据,通常可以是状态变量的形式;在进行存储时,可以将状态变量名以key-value键值对的形式组织成如图2所示的storage树进行存储。
例如,在一个例子中,可以将合约账户的账户地址和状态变量在合约账户的账户存储中的存储位置的hash值作为key,将状态变量对应的变量取值作为value。
其中,图2示出的storage树的基本结构与图1示出的MTP状态树相似,在本说明书中不再赘述。通过上述图1和图2的描述可知,基于MPT树的树形结构设计,分支节点可以存储所有账户地址的共享字符前缀中的其中一个字符;而扩展节点则可以存储所有账户地址的共享字符前缀中的一个或者多个字符。
在实际应用中,上述MPT状态树上的叶子节点通常采用的是bucket数据桶的结构,可以存储若干个账户地址的字符后缀和对应的账户状态数据。而bucket数据桶中所包含的数据,通常在逻辑上是一个整体;因此,上述MPT树中的叶子节点所包含的所有的账户地址的字符后缀和对应的账户状态数据,将会作为数据库中的一个存取单位进行整体存取;而将叶子节点所包含的所有数据作为一个整体进行存取,显然不够灵活;
例如,在实际应用中,假设bucket数据桶中存储了N个账户地址的字符后缀和对应的账户状态数据,则通常可以将bucket数据桶中存储的这N个账户地址的字符后缀和对应的账户状态数据按照顺序进行拼接,再基于拼接后的数据整体进行hash计算,并将计算得到的hash值作为该叶子节点的hash填充到该叶子节点在MPT树上链接的上一层的节点中(比如图1示出的Branch Node)。
假设需要读取叶子节点中包含的某一个特定的账户地址的字符后缀和对应的账户状态数据,则需要从数据库中将该叶子节点中所包含的所有数据内容,整体读取到内存中,再在内存中进一步查找需要读取的账户地址的字符后缀和对应的账户状态数据;
相应的,如果需要向叶子节点写入一个新的账户地址的字符后缀和对应的账户状态数据,或者对叶子节点中包含的一个特定的账户地址对应的账户状态数据进行更新,则也需要从数据库中将该叶子节点中所包含的所有数据内容,整体读取到内存中,再将新的账户地址的字符后缀和对应的账户状态数据写入;或者,写入特定的账户地址对应的更新后的账户状态数据,对内存中该特征的账户地址对应的原有的账户状态数据进行更新。
可见,将叶子节点所包含的所有数据作为一个整体进行存取,会存在存取大量无关数据的问题,不仅会使针对该数据库的IO次数增多,而且会浪费IO读写带宽;比如,本来只想存取叶子节点所包含的所有数据中的某一条数据,则不得不需要从数据库中将叶子节点所包含的所有数据都读取到内存中。
有鉴于此,本说明书提出一种新的逻辑的树形结构设计方案。
在实现时,上述逻辑的树形结构仍然可以包括根节点、中间节点和叶子节点;其中,上述根节点、中间节点用于存储区块链数据的key中的字符;上述叶子节点用于存储区块链数据的value。所述树形结构上的任一节点通过其hash值与上一层的节点链接;
与上述MPT树不同的是,上述叶子节点包含若干条在逻辑上分离的数据记录;所述数据记录中存储的数据内容包括所述区块链数据的value;
区块链中的节点设备在存储区块链数据时,可以获取待存储的区块链数据的key-value键值对,再将该待存储的区块链数据转换成上述逻辑的树形结构上的根节点、中间节点和叶子节点;然后,将上述根节点、中间节点和叶子节点的key-value键值对再进一步存储在数据库中;上述叶子节点、所述中间节点和所述根节点的key-value键值对中,value可以为节点的存储内容,key可以为节点的存储内容的hash值。
其中,将上述叶子节点的key-value键值对存储在数据库包括:
将上述叶子节点包含的若干条数据记录中的至少部分数据记录,聚合成为与承载所述数据库的物理存储介质的单位存储单元的存储容量适配的若干数据块,在所述物理存储介质中存储。
在以上技术方案中,一方面,由于逻辑的树形结构中的叶子节点中所包含的若干条数据记录,在逻辑上不再是一个整体,而是在逻辑上分离的若干条数据记录;因此,上述逻辑的树形结构中的叶子节点所包含的数据,将不再作为数据库中的一个整体的存取单位进行整体存取,上述叶子节点所包含的每一条数据记录将作为数据库中的一个独立的存取单位,从而使得针对上述数据库的数据存取将更加灵活;
另一方面,由于叶子节点包含的若干条数据记录中的至少部分数据记录,聚合成为与承载所述数据库的物理存储介质的单位存储单元的存储容量适配的若干数据块,在所述物理存储介质中存储;单位存储单元单位存储单元;而上述存储介质的单次IO读写的容量通常取决于该物理存储介质支持的单位存储单元的存储容量;因此,通过这种方式,可以使该数据块的存储容量,与上述存储介质的单次IO读写的容量更加适配,从而可以充分利用上述物理存储介质自身的IO读写能力,降低IO读写所需的带宽,提升数据读写效率。
请参见图3,图3是一示例性实施例提供的一种区块链数据存储方法的流程图。所述方法应用于区块链节点设备所述方法包括以下步骤:
步骤302,获取待存储的区块链数据的key-value键值对;
步骤304,将所述待存储的区块链数据的key-value键值对转换成逻辑的树形结构上的根节点、中间节点和叶子节点;所述根节点、中间节点用于存放所述区块链数据的key中的字符;所述树形结构上的任一节点通过其hash值与上一层的节点链接;所述叶子节点包含若干条在逻辑上分离的数据记录;所述数据记录中存储的数据内容包括所述区块链数据的value;
步骤306,将所述根节点、中间节点和叶子节点的key-value键值对存储在数据库中;其中,所述叶子节点、所述中间节点和所述根节点的key-value键值对中,value为节点的存储内容,key为节点的存储内容的hash值;将所述叶子节点的key-value键值对存储在数据库包括:将所述叶子节点包含的若干条数据记录中的至少部分数据记录,聚合成为与承载所述数据库的物理存储介质的单位存储单元的存储容量适配的若干数据块,在所述物理存储介质中存储。
在本说明书中,为了避免节点频繁发生分裂,提出一种前N层的用于存储区块链数据的key中的字符节点的层数固定的逻辑的树状结构。
其中,所谓逻辑的树形结构,是指在数据库的底层物理存储中,并不存在与树形结构完全对应的物理存储结构,而仅在数据库中存储上述树形结构上的各个节点的物理数据以及各个节点之间的链接关系数据,从而可以基于数据库中存储的各个节点的物理数据和链接关系数据,在逻辑层面上还原出上述树形结构。
上述逻辑的树状结构,具体可以包括根节点、中间节点和叶子节点;其中,上述逻辑的树状结构上的节点,可以通过其自身的hash值与上一层的节点进行链接。上述根节点和中间节点,具体用于存储区块链数据的key-value键值对中的key中的至少一个字符;上述叶子节点具体用于存储区块链数据的value(即区块链数据的具体内容)。中间节点的层数可以为一层也可以为多层,在本说明书中不进行特别限定。
例如,在一个例子中,上述区块链数据的key,仍然可以包括字符前缀部分(Sharednibble)和字符后缀部分(key-end);在这种情况下,根节点和中间节点可以用于存储上述字符前缀中的字符;而上述叶子节点则可以用于存储上述字符后缀和区块链数据的value。
一方面,由于上述逻辑的树形结构中的根节点和中间节点,可以存放区块链数据的key中的字符;因此,上述上述逻辑的树形结构具有Trie字典树的特点;另一方面,上述逻辑的树状结构上的节点,可以通过其自身的hash值与上一层的节点进行链接;因此,上述上述逻辑的树形结构也具有Merkle树的特点。综上,本说明书描述的逻辑的树形结构,实际上是一种类似于MPT树的融合了Trie字典树的树形结构的Merkle树变种。
请参见图4,图4为本说明书示出的一种FDMT(Fixed Depth Merkle Tree)树的树形结构图。上述FDMT树是一种融合了Trie字典树的树形结构的Merkle树变种。
如图4所示,在本说明书示出的FDMT树的树形结构中,包含前N层(图4示出的为3层,仅为示意性的)的Tree node,和最后一层的Leaf node(即叶子节点)。其中,在前N层的Tree node中,第一层为作为根节点的Tree node,其它层为作为中间节点的Tree node。
其中,与以上描述的MPT树不同的是,上述FDMT树前N层的各个Tree node(即根节点和中间节点),将采用统一的数据结构,从而使得上述FDMT树前N层的各个Tree node,可以不再因为新写入了数据导致存储的字符的长度发生变化而发生节点分裂。
如图4所示,上述FDMT树上的前N层的Tree node,均可以包括分别代表不同字符的多个block;上述block为用于存放区块链数据的key中的字符的“位置”。而每一个block可以进一步包括多个分别代表不同字符的槽位;上述槽位也用于存放区块链数据的key中的字符。
例如,图4示出的为每一个Tree node包括N个block;每个block进一步包括N个slot。其中,上述FDMT树上各层的节点之间,仍然可以采用在上一层的节点中填充下一层的节点的hash值(hash指针)的方式,来进行节点间的链接。也即,上述FDMT树中的节点,通过其自身的hash值,链接至上一层的节点。相应的,上述槽位具体可以用于填充当前的Treenode所链接的下一层节点的hash值。Tree node的下一层节点,具体仍然可以是Tree node,也可以是Leaf node。
其中,在实际应用中,当上述FDMT树上的Tree node中的任一block中的任一槽位填充的hash值发生更新后,通常需要重新计算该Tree node的hash值,并根据计算出的该hash值与上一层的节点重新进行链接。
而在计算该Tree node的hash值时,通常需要将Tree node中填充的所有数据都作为计算参数进行hash计算;因此,在Tree node采用了如图4所示的数据结构的情况下,如果需要计算该Tree node的hash值,则需要先计算出该Tree node包含的各个block的hash,再将各block的hash拼接起来,再针对拼接起来的hash执行二次的hash计算。
通过这种方式,当上述FDMT树上的Tree node中的任一block中的任一槽位填充的hash值发生更新,在重新计算该Tree node的hash值时,即便该Tree node中其它的各个block未发生数据更新,也仍然需要作为计算参数参与hash计算,显然存在hash计算的计算量较大的问题。
基于此,在本说明书中,为了降低在计算Tree node的hash时的计算量,上述Treenode具体可以采用主block(即主位置)和多个子block(即子位置)的数据结构。
请参见图5,图5为本说明书示出的另一种FDMT树的树形结构图。
如图5所示,在本说明书示出的FDMT树的树形结构中,仍然包含前N层的Treenode,和最后一层的叶子节点。
其中,与图4示出的上述FDMT树的结构不同的是,如图5中示出的上述FDMT树上的前N层的Tree node,均可以包括主block(即图5中示出的root block)和分别代表不同字符的多个子block;而每一个block还可以进一步包括多个槽位;例如,图5示出的为每一个Tree node均包括一个主block和N个子block;而每个子block又进一步包括N个slot。
其中,上述主block和子block包含的槽位的功能,具体可以有所不同;如图5所示,对于主block而言,可以包含分别与各子block对应的多个槽位,每一个槽位具体可以用于填充对应的子block中存储的数据内容的hash;
例如,在计算任一子block的hash时,具体可以将该子block中各槽位填充的hash值拼接起来,再针对拼接起来的hash执行二次的hash计算,得到该子block的hash。
而对于子block而言,可以包含分别代表不同的字符的多个槽位,每一个槽位具体可以用于填充该Tree node链接的下一层节点的hash值。
在本说明书中,按照图5示出的Tree node的结构,则可以用该Tree node中的主block的hash值来表示该Tree node的hash值;从而,在这种情况下,当上述FDMT树上的Treenode中的任一子block中的槽位填充的hash值发生更新后,则在重新计算该Tree node的hash值时,只需要将该发生数据更新的该子block中各个槽位中填充的hash值进行拼接,并将拼接出的hash值作为计算参数重新进行hash计算,再将计算出的hash值填充至该Treenode中的主block中与该子block对应的槽位,然后再对主block中的各槽位填充的hash进行拼接,再将拼接出的hash值作为计算参数重新进行hash计算即可得到该Tree node的hash。
在整个过程中,对于未发生数据更新的其它子block中各个槽位填充的hash值,将不再需要进行拼接后作为计算参数参与hash计算,从而从而可以降低在重新计算该Treenode的hash值时的hash计算量和计算时长,提升hash计算的计算效率。
需要解释的是,图4和图5中示出的上述FDMT树上各层的节点之间的链接关系仅为示意性的,并不是指对上述FDMT树上各层的节点之间的链接关系的一种特殊限定。
请继续参见图4和图5,图4和图5示出的上述FDMT树上的各Tree node,均可以用于存储上述区块链数据的key中的至少部分字符。
对于图4示出的上述FDMT树上的各Tree node,实际存储的字符,具体可以是该Tree node中的block(即至少有一个槽位填充了hash值的非空block)所代表的字符,与该block中填充了hash值的槽位(即非空槽位)所代表的字符,进行拼接生成的字符串。
而对于图5示出的上述FDMT树上的各Tree node,实际存储的字符,具体可以是该Tree node中的子block所代表的字符,与该block中填充了hash值的槽位所代表的字符,进行拼接生成的字符串。
其中,需要说明的是,在实际应用中,Tree node中的每一个block,可以仅代表一位字符;也即,基于图4和5示出的Tree node的存储格式,每一个Tree node实际存储的上述区块链数据的key的字符前缀中的部分字符,为长度是2位字符的字符串。
例如,请参见图6,图6为本说明书示出的一种Tree node的结构图;
如图6所示,该Tree node包含16个代表不同的16进制字符的子block;每一个子block进一步包括16个分别代表不同的16进制字符的slot(图6中只示出了block6包含的16个槽位);假设该Tree node中的子block6(代表了16进制字符6)为非空block,该子block中的slot4(代表了16进制字符4)、slot6(代表了16进制字符6)和slot9(代表了16进制字符9)为填充了该Tree node链接的下一层节点的hash值的非空slot;则该Tree node存储的上述区块链数据的key的字符前缀中的部分字符,分别为16进制字符串“64”、“66”和“69”。
其中,上述Tree node中所包含的子block的数量,以及每一个子block所包含的槽位的数量,在本说明书中不进行特别限定;在实际应用中,可以基于上述区块链数据的key对应的字符串所包含的字符元素的类型数,来确定上述Tree node所包含的子block的数量;以及,子block所包含的槽位的数量;
例如,假设上述区块链数据对应的key为16进制字符串,此时上述区块链数据的key对应的字符串所包含的字符元素的类型数为16;则上述Tree node中所包含的子block的数量,以及每一个子block所包含的槽位的数量都可以为16。
需要说明的是,在实际应用中,上述Tree node所包含的子block的数量,和上述子block所包含的槽位的数量,可以保持相同;
例如,在一个例子中,以上述区块链数据的key对应的字符串为16进制字符串为例,在这种情况下,上述前N层的Tree node可以均包括16个分别代表不同的16进制字符的子block;而每一个子block可以进一步包括16个分别代表不同的16进制字符的槽位。
通过这种方式,则上述FDMT树树前N层每一层中的单个Tree node,可以具有16*16=256个槽位,显然图4示出的FDMT树上的Tree node相对于图1示出的MPT树上用于存储字符前缀的节点而言,将具有更大的存储容量。
当前,在实际应用中,上述Tree node所包含的子block的数量,和上述子block所包含的槽位的数量,具体也可以不相同;
例如,在实际应用中,上述区块链数据的key对应的字符串,具体也可以是由两种不同进制字符构成的字符串;比如,上述区块链数据的key对应的字符串具体可以是由16进制的字符和10进制的字符混合构成的字符串,在这种情况下,上述前N层的Tree node可以均包括16个分别代表不同的16进制字符的子block;而每一个子block可以进一步包括10个分别代表不同的10进制字符的槽位;或者,上述前N层的Tree node可以均包括10个分别代表不同的10进制字符的子block;而每一个子block可以进一步包括16个分别代表不同的16进制字符的槽位。
在示出的一种实施方式中,上述FDMT树包含的Tree node的层数,具体可以是一个固定值;在实际应用中,上述N的取值具体可以是一个大于或者等于1的整数;也即,上述FDMT树具体可以是一棵包含至少一层Tree node,并且包含的Tree node的层数相对固定的Merkle树。
例如,在一个例子中,以上述区块链数据的key为区块链账户地址为例,假设区块链系统支持的区块链账户地址被设计为,前6位地址字符可以相同,那么在这种情况下,可以将区块链账户地址前6位的地址字符,作为区块链账户地址的字符前缀;而且,由于Treenode存储的区块链账户地址的字符前缀中的字符的长度为2位字符;因此,上述FDMT树可以被设计成包含三层Tree node的树状结构。
基于图4和图5示出的FDMT树的树形结构设计,一方面,由于FDMT树中的每一个Tree node,均包括分别代表不同字符的多个block;并且,各block又进一步包括多个分别代表不同字符的槽位;因此,通过这种设计,使得每一个Tree node将具有更大的数据存储容量和数据承载能力,从而在将上述FDMT树中的Tree node写入数据库进行存储时;或者,在访问数据库中存储的上述FDMT树中的Tree node时,可以使得Tree node的数据存储容量,与承载上述数据库的存储介质的单次IO读写的容量更加适配,从而可以充分利用承载上述数据库的存储介质自身的IO读写能力,提升数据读写效率;而且,上述FDMT树上单个Tree node的数据存储容量和数据承载能力的提升,势必也会导致上述FDMT树整体的数据存储容量和数据承载能力的提升,使得上述FDMT树上可以存储更多的区块链数据;
例如,以承载上述数据库的存储介质为单个物理扇区为4KB大小的磁盘为例,假设该磁盘单次IO读写的容量为4kb(一个扇区),对于图1描述的MPT树上的1个Branch node而言,假设Branch node包含的16个字段均填充了32byte的hash值,则该Branch Node的数据存储容量是32字节*16=512byte左右;显然针对Branch node的一次IO读取的容量最大只能是512byte左右,远小于该磁盘一次IO读取最大可以读取到4Kb的读取能力,这显然无法充分利用磁盘的IO读取能力,存在严重的性能浪费。
但是,如果采用如图4和图5所示出的FDMT树的树形结构设计,假设上述前N层的Tree node可以均包括16个分别代表不同的16进制字符的block;每一个block可以进一步包括16个分别代表不同的16进制字符的槽位,则上述FDMT前N层每一层中的单个Treenode,可以具有16*16=256个槽位;假设每一个槽位填充的是32byte的hash值,则在所有槽位满负荷的情况下,一个Tree node的最大存储容量则是256*32byte=8192byte=8kb,刚好是两个扇区的容量。显而易见的,图4和图5示出的FDMT的树形结构中的每一个Tree node的数据存储容量,与上述磁盘的单次IO读写的容量更加适配,可以充分利用该磁盘自身的IO读写能力,提升数据读写效率。
而且,上述FDMT上单个Tree node的数据存储容量和数据承载能力的提升,势必也会导致上述FDMT整体的数据存储容量和数据承载能力的提升,使得上述FDMT上可以存储更多的区块链数据;
例如,假设图4和图5示出的FDMT的树形结构包含3层Tree node,每一层的Treenode可以均包括16个分别代表不同的16进制字符的子block;每一个block可以进一步包括16个分别代表不同的16进制字符的槽位;那么,每一层中的单个Tree node,可以具有16*16=256个槽位;则三层Tree node共计可以承载256*256*256=16.77M个字符组合,共计可以链接16.77M个bucket 数据块;假设用户定义每一个bucket 数据块可以承载16条数据记录,则整棵FDMT树最多可以承载16.77M*16条区块链数据;显而易见的,相较于图1示出的MPT树,图4和图5示出的上述FDMT可以存储更多的区块链数据,具有更大的数据承载能力。
第二方面,由于FDMT中的每一个Tree node均采用统一的数据结构;对于上述FDMT中各层的Tree node来说,其实际存储的区块链数据的key的字符前缀的字符长度也将保持固定;因此,通过这种设计,可以避免由于各层Tree node其实际存储的字符长度不固定,而导致的节点的频繁分裂,从而可以确保FDMT的树形结构中所包含的Tree node的层数,始终处于一个相对稳定的状态。
第三方面,由于通过这种设计,每一个Tree node将具有更大的数据存储容量和数据承载能力;并且,上述FDMT的树形结构中包含的Tree node的层数,将处于一个相对稳定的状态;因此,Tree node存储容量的提升和Tree node的层数相对稳定,在某种程度上可以确保上述FDMT的Tree node将具有更少的层数;从而,在Tree node具有更大的数据存储容量和数据承载能力,以及上述FDMT的Tree node具有更少的层数的基础之上,系统在进行冷启动,需要从承载上述数据库的存储介质中,将上述FDMT的前N层的Tree node作为需要频繁读取的数据加载到内存中时,则可以显著降低将上述存储介质存储的上述前N层的Treenode读取到内存中时的IO读取次数,以及将FDMT的前N层的Tree node加载到内存时的整体读取时长,进而从根本上缩短系统冷启动时的启动时延。
例如,以图4和图5示出的FDMT为3层Tree node固定,且每一层的Tree node包括16个分别代表不同的16进制字符的block;每一个block进一步包括16个分别代表不同的16进制字符的槽位为例,在所有槽位满负荷的情况下,一个Tree node的最大存储容量则是256*32byte=8192byte=8kb;对于图1示出的MPT树而言,由于其需要进行频繁的节点分裂,MPT树的层数不固定;而且,由于其单个Branch Node节点的存储容量512byte,远小于图4和图5示出的FDMT上的Tree node;势必造成MPT树具有更大的层数(比如MPT树最大可以达到64层,远大于3层)。而系统在冷启动,将FDMT树前N层读取到内存时,通常是一层一层读取的;因此,基于图1示出的MPT树,显然需要更多的读取次数。
而且,由于MPT树上的单个Branch Node节点的存储容量512byte,仅为承载上述数据库的单个物理扇区为4KB的八分之一,读取效率很低;因此,即便按照图1的MPT树和图4和图5示出的FDMT树存储同样的数据,系统在冷启动针对MPT树的IO读取次数,也可能是针对图4和图5示出的FDMT树的IO读取次数的至少8倍。
显而易见的,系统在冷启动时,针对图4和图5示出的上述FDMT树的IO读取次数,将远小于针对图1示出的MPT树的IO读取次数;因此,图4和图5示出的上述FDMT树的树形结构设计对系统冷启动将更加友好。
在示出的一种实施方式中,上述区块链数据的key对应的字符串仍然可以包括字符前缀和字符后缀;在这种情况下,上述Tree node可以用于存储所述区块链数据的key的字符前缀中的字符;上述叶子节点可以用于存储所述区块链数据的key的字符后缀和上述区块链数据的Value。
其中,需要说明的是,由于上述叶子节点实际存储的数据,相对于上述Tree node来说,通常具有更大的数据容量;比如,上述叶子节点实际存储的上述区块链数据的value,通常为上述区块链数据的原始内容,上述区块链数据的原始内容相对于上述区块链数据的字符前缀而言,其所占用的存储空间也更大;因此,在本说明书中,为了确保上述叶子节点能够具有更大的数据容量,上述叶子节点具体可以采用大数据块的形式来存储数据。
其中,上述数据块的具体形式以及存储结构,在本说明书中不进行特别限定;
在示出的一种实施方式中,上述叶子节点具体可以是bucket数据桶的形式;其中,上述bucket数据桶具体可以是用于存储数据的容器或者存储空间。
请参见图7,图7为本说明书示出的一种bucket数据桶的结构图;
如图7所示,在上述bucket数据桶(即图7中示出的bucker node)中,可以包括若干条数据记录;其中,需要说明的是,上述bucket数据桶中包含的上述若干条数据记录,在逻辑上可以不是一个整体,而是在逻辑上分离的若干条不同的数据记录。每一条数据记录都分别对应一条区块链数据,用于存储上述区块链数据的value;也即,一条数据记录,是指一条存储的数据内容包括所述区块链数据的value的存储记录。
需要说明的是,上述bucket数据桶中包含的上述若干条数据记录,在逻辑上不是一个整体,是指各条数据记录都可以分别对应一个独立的查询键值(key),从而可以基于各条数据记录的查询键值,来针对上述bucket数据桶中的各条数据记录分别进行精确查询,而并不要对上述bucket数据桶中存储的所有的数据记录进行整体的读取。
其中,各条数据记录分别对应的查询键值的具体形式和具体内容,在本说明书中不进行特别限定,可以是任意形式的能够作为各条数据记录的查询索引的字符串。
在示出的一种实施方式中,各条数据记录对应的查询键值具体可以是各条数据记录包含的数据内容的hash值;上述bucket数据桶中包含的上述数据记录,则具体可以包括由上述区块链数据的value的hash值,和上述区块链数据的value对应的数据内容构成的key-value键值对。
当然,在实际应用中,各条数据记录对应的查询键值也可以是hash值以外的其它形式的能够作为各条数据记录的查询索引的字符串,在本说明中不进行特别的限定;例如,在一个例子中,各条数据记录对应的查询键值具体也可以是由节点设备为各条数据记录分别设置的一个唯一的标识(比如编号)。
在示出的一种实施方式中,如果上述Tree node用于存储上述区块链数据的key的字符前缀中的字符;则上述叶子节点用于存储上述区块链数据的key的字符后缀和上述区块链数据的Value;
相应的,上述bucket数据桶中包含的上述数据记录,具体可以是将上述区块链数据的key中的字符后缀和上述区块链数据的value对应的数据内容整体进行hash计算得到的hash值,和上述区块链数据的key的字符后缀和上述区块链数据的value对应的数据内容构成的key-value键值对。
需要说明的是,在实际应用中,上述数据记录具体也可以是key-value键值对以外的其它类型的数据形式,在本说明书中不再进行一一列举。
通过以上实施方式,由于FDMT树中的叶子节点中所包含的若干条数据记录,在逻辑上不再是一个整体,而是在逻辑上分离的若干条key-value键值对;因此,上述FDMT树中的叶子节点所包含的数据,将不再作为数据库中的一个整体的存取单位进行整体存取,上述叶子节点所包含的每一条key-value键值对将作为数据库中的一个独立的存取单位,从而使得针对上述数据库的数据存取将更加灵活;
例如,以上述区块链数据的value为区块链中的区块链账户对应的最新账户状态数据,上述区块链数据的key为区块链账户地址为例,在这种情况下,上述bucket数据桶中包含的上述数据记录对应的key-value键值对的key,可以是区块链账户地址的字符后缀和对应的账户状态数据两部分数据内容的hash值;该key-value键值对的value,可以是区块链账户地址的字符后缀和对应的账户状态数据两部分的数据内容。
假设需要读取上述bucket数据桶中包含的某一个特定的账户地址的字符后缀和对应的账户状态数据,由于上述bucket数据桶中的各条key-value键值对都是一个独立的存取单位;因此,只需要基于该特定的账户地址的字符后缀和对应的账户状态数据这两部分数据内容的hash值,在数据库进行内容寻址即可,而不再需要从数据库中将该叶子节点中所包含的所有数据内容,整体读取到内存中,再在内存中进一步查找需要读取的账户地址的字符后缀和对应的账户状态数据;
相应的,如果需要向上述bucket数据桶写入一个新的账户地址的字符后缀和对应的账户状态数据,或者对上述bucket数据桶中包含的一个特定的账户地址对应的账户状态数据进行更新,则可以直接根据新的账户地址的字符后缀和对应的账户状态数据构建key-value键值对,并将该key-value键值对写入上述bucket数据桶即可;或者,基于该特定的账户地址的字符后缀和对应的账户状态数据这两部分数据内容的hash值,在数据库进行内容寻址,查找到对应的key-value键值对,然后写入该特定的账户地址对应的更新后的账户状态数据,对该key-value键值对原有的value进行更新即可。
在示出的一种实施方式中,上述bucket数据桶中包含的若干条key-value键值对,除了可以是在逻辑上分离的数据以外,对于每一条key-value键值对的key和value而言,也可以是在逻辑上分离的数据;
也即,在实际应用中,还可以进一步对上述bucket数据桶所包含的各条key-value键值对中的key和value,进行进一步的逻辑分离。
其中,对上述bucket数据桶所包含的各条key-value键值对中的key和value,进行进一步的逻辑分离的具体方式,在本说明书中不仅特别限定;
例如,在示出的一种实施方式中,可以将上述bucket数据桶的存储结构划分为在逻辑上隔离的数据头部分和数据体部分,在上述数据头部分集中存储上述bucket数据桶包含的各条key-value键值对中的key,在上述数据体部分集中存储上述bucket数据桶包含的各条key-value键值对中的value。
请参见图8,图8为本说明书示出的另一种bucket数据桶的结构图;
在图8示出的bucket数据桶的结构中,具体可以包括数据头部分(即图8示出的Header部分)和数据体部分(即图8示出的Body部分);上述数据头部分和上述数据体部分为在逻辑上完全分离的两部分。
其中,上述数据头部分,用于集中存储该bucket数据桶所包含的若干条key-value键值对中的key;也即,将该bucket数据桶所包含的若干条key-value键值对中的key统一存储在该bucket数据桶中的数据头部分。
相应的,上述数据体部分,用于集中存储该bucket数据桶所包含的若干条key-value键值对中的value;也即,将该bucket数据桶所包含的若干条key-value键值对中的value统一存储在该bucket数据桶中的数据体部分。
在实际应用中,上述数据头部分和上述数据体部分,可以分别包含若干条在逻辑上分离的数据记录;上述数据头部分包含的数据记录,用于存储该bucket数据桶所包含的若干条key-value键值对中的key;相应的,上述数据体部分包含的数据记录,用于存储该bucket数据桶所包含的若干条key-value键值对中的value。其中,所述数据头部分包含的若干数据记录与所述数据体部分包含的若干条数据记录可以是一一对应的关系;
例如,如图8所示,数据头部分包含的各条数据记录,可以按照顺序与数据体部分包含的各条数据记录一一对应;比如,数据头部分包含的第N条数据记录,与数据体部分包含的第N条数据记录对应。
在示出的一种实施方式中,当上述FDMT树上的叶子节点采用了如图7示出的bucket数据桶的结构时,则可以将上述FDMT树中的叶子节点中的数据头部分所包含的数据内容的hash值,作为该叶子节点的hash值;此时,上述FDMT树上的叶子节点,可以通过自身的数据头部分所包含的数据内容的hash值,链接至其上层的字符节点。
在这种情况下,在计算上述bucket数据桶的hash值时,可以不再需要针对该bucket数据桶包含的所有数据内容进行hash计算,而是只针对该bucket数据桶的数据头部分包含的数据内容进行hash值计算;由于上述bucket数据桶的数据头部分包含的数据内容,通常要远小于上述bucket数据桶的数据体部分包含的数据内容;因此,通过这种方式,可以显著的降低hash计算的计算量,进而减少hash计算所需要的时长,提升hash计算的计算效率;
例如,请继续参见图8,假设上述bucket数据桶的数据头部分和数据体部分,分别包含16条数据记录;对于数据头部分的一条数据记录,其本身实际上是一个hash值,通常大小为32bytes;而对于数据体部分的一条数据记录,其对应的是区块链数据的value,通常大小为1KB左右;因此,上述数据头部分包含的数据内容的总大小为32byte*16=512bytes=0.5KB;而上述数据体部分包含的数据内容的总大小为16*1KB=16KB;显而易见的,上述数据头部分包含的数据内容的总大小,远小于上述数据体部分包含的数据内容的总大小。
请继续参见图8,假设图8示出的bucket node1中的value2更新为value
;则只需
要将该bucket node1的Header部分的第二条数据记录所包含的“key-end2+ value2”更新
为“key-end2+ value
”,然后重新计算“key-end2+ value
”的hash值;其中,该hash值
可以表示成hash(key-end2+ value
);再将该bucket node1的Body部分的第二条数据记
录所包含的key2更新为hash(key-end2+ value
);而bucket node1中除了Header部分和
Header部分的第二条数据记录以外,其它条的数据记录则完全不需要进行更新。
当针对bucket node1的以上更新完成后,此时可以进一步将bucket node1的Header部分包含的各个key1-keyN拼接起来,再重新计算一个hash值,然后将计算得到的hash值,填充到该bucket node1的上一层字符节点相应的槽位中(比如图7示出的block1的第2个槽位)。由于上述bucket node1的Header部分包含的数据内容,要远小于上述bucketnode1的Header部分包含的数据内容;因此,通过这种方式,可以显著的降低hash计算的计算量,进而减少hash计算所需要的时长,提升hash计算的计算效率。
其中,需要说明的是,上述bucket数据桶中所包含的数据记录的条数,在本说明书中不进行特别限定;在示出的一种实现方式中,上述bucket数据桶中包含的数据记录的条数,具体可以由用户进行自定义配置。
例如,以上述区块链数据为区块链中的区块链账户对应的最新账户状态数据,上述区块链数据的key为区块链账户地址为例,在这种情况下, 上述bucket数据桶中的每一条数据记录,分别与一个区块链账户的账户状态相对应;上述bucket数据桶中的数据记录的条数,实际上代表着该bucket数据桶可以容纳区块链账户的账户承载能力;因此,用户通过对上述bucket数据桶能够容纳的数据记录的条数进行自定义,可以实现对上述bucket数据桶的账户承载能力进行灵活的定制;比如,在一个例子中,上述bucket数据桶中包含的数据记录的条数可以由用户配置为16条或者64条,从而使得单个bucket数据桶可以承载16或者64个区块链账户的状态数据。
其中,还需要说明的是,上述bucket数据桶中所存储的数据记录的总存储容量,在本说明书中也不进行特别限定;在示出的一种实现方式中,上述bucket数据桶中所存储的数据记录的总存储容量,具体可以由用户进行自定义配置。
例如,在实现时,以承载上述数据库的存储介质为单个物理扇区为4KB大小的磁盘为例,在这种情况下,用户可以将上述bucket数据桶中所存储的数据记录的最大存储容量,设置为4KB;或者,4KB的整数倍,以使上述bucket数据桶的最大存储容量,能够与上述存储介质的单个物理扇区的存储容量相适配。
在本说明书中,待存储的区块链数据,具体可以包括至少以下四类数据:
区块中收录的交易;在区块中的交易执行完毕后,与所述区块中收录的交易对应的交易收据;在区块中的交易执行完毕后,与所述区块链中的区块链账户对应的最新账户状态数据;智能合约账户的存储内容;
相应的,上述FDMT树具体也可以包括:
用于存储区块中收录的交易的交易树;用于存储与区块中收录的交易对应的交易收据的收据树;用于存储与所述区块链中的区块链账户对应的最新账户状态数据的状态树;用于存储智能合约账户的存储内容的存储树。
当然,在实际应用中,也可以只利用上述FDMT树的树形结构来存储上述四种数据中的部分类型的数据;比如,只利用上述FDMT树来存储上述区块链账户对应的最新账户状态数据,其他类型的数据可以利用其他形式的二叉树来存储(比如MPT或者其它形式的二叉树)。
其中,上述交易树、收据树和状态树的根节点的hash值,可以存储在区块头中;上述存储树的根节点的hash值,可以存储在与该存储树对应的合约账户的结构体中的Storage字段中。
上述区块链数据的key,具体可以是指区块链数据在数据库中对应的查找键值;相应的,上述区块链数据的Value,具体可以是上述区块链数据的原始内容;
其中,在实际应用中,上述查找键值具体可以是一个与区块链数据对应的字符串;当上述区块链数据为不同类型的数据时,其对应的key也会存在一定的差异;
例如,当上述区块链数据为区块中收录的交易;或者,在区块中的交易执行完毕后,与该区块中收录的交易对应的交易收据时,此时与该区块链数据对应的key,具体可以是交易在区块中的序号;或者,其它形式的交易标识。
当上述区块链数据为在区块中的交易执行完毕后,与区块链中的区块链账户对应的最新账户状态数据时,此时与该区块链数据对应的key,具体可以是区块链账户的账户地址。
当上述区块链数据为智能合约账户的存储内容时,此时与该区块链数据对应的key,具体可以是合约账户的账户地址和上述存储内容在合约账户的账户存储中的存储位置的hash值;比如,在一个例子中,上述存储内容通常可以是状态变量,则可以将合约账户的账户地址和状态变量在合约账户的账户存储中的存储位置的hash值作为key。
在本说明书中,当区块链中的节点设备在存储区块链数据时,可以先获取待存储的区块链数据的key-value键值对;例如,在一个例子中,节点设备可以在获取到待存储的区块链数据时,将该区块链数据处理成key-value键值对;
在获取到待存储的区块链数据的key-value键值对之后,可以将该待存储的区块链数据的key-value键值对转换成上述逻辑的树形结构上的根节点、中间节点和叶子节点;
例如,在一个例子中,上述节点设备可以搭载一个与上述FDMT树对应的存储接口或者存储服务,该存储接口或者存储服务具体可以用于将待存储的区块链数据的key-value键值对,转换成为FDMT树上的节点;则该节点设备在获取到待存储的区块链数据的key-value键值对之后,可以通过调用该存储接口或者存储服务,按照图4或者图5示出的FDMT树的树形结构,将待存储的区块链数据的key-value键值对,转换成为图4或者图5上的根节点、中间节点和叶子节点。
在将存储的区块链数据的key-value键值对转换成上述逻辑的树形结构上的根节点、中间节点和叶子节点之后,可以将这些根节点、中间节点和叶子节点以key-value键值对的形式存储在数据库中;
例如,上述数据库通常是存储在上述节点设备上搭载的持久化存储介质(比如存储磁盘)中的;上述存储介质为与上述数据库对应的物理存储;在将上述FDMT树在数据库中进行存储时,具体可以通过执行commit命令,将上述FDMT树上的节点,以Key-Value键值对的形式,从节点设备的内存中,进一步写入承载上述数据库的存储介质。
其中,上述数据库的具体类型,在本说明书中不进行特别限定,本领域技术人员可以基于实际的需求进行灵活的选择;
在一种实现方式中,上述数据库,具体可以是Key-Value型数据库;例如,在一个例子中,上述数据库可以为采用多层存储结构的LevelDB数据库;或者,基于LevelDB架构的数据库;比如,Rocksdb数据库就是一种典型的基于LevelDB数据库架构的数据库。
需要说明的是,上述FDMT树上的节点,在以Key-Value键值对的形式在上述数据库中进行存储时,上述Key-Value键值对的key,具体可以是上述FDMT树上的节点的节点ID;
其中,上述节点ID具体可以包括能够唯一标识上述FDMT树上的节点的标识信息;
例如,在一种实现方式中,上述节点ID具体可以是上述FDMT树上的节点包含的数据内容的hash值;在这种情况下,在需要查询上述FDMT树上的节点时,可以基于节点所包含的数据内容的hash值作为key来进行内容寻址。
在另一种实现方式中,上述节点ID具体也可以包括上述FDMT树上的节点在上述FDMT树中的路径信息;也即,将上述FDMT树上的节点在上述FDMT树中的路径信息作为该节点的节点ID;其中,上述路径信息具体可以包括,能够描述节点与其它节点之间的链接关系,以及节点在上述FDMT树上的位置的任意形式的信息;
例如,请参见图9,图9为本说明书示出的一种为FDMT树上的节点设置节点ID的示意图;对于如图9所示包含三层Tree node的FDMT树,假设作为根节点的tree node的节点ID用0x00表示,那么图9示出的bucket node的节点ID,可以表示成0x00123456。
其中,0x00为根节点的节点ID;123456是指上述bucket node在FDMT树上从根节点到该bucket node的路径信息;12表示第一层tree node的第一个block的第2个槽位;34表示第二层tree node的第3个block的第4个槽位;56表示第三层tree node的第5个block的第6个槽位。
基于该节点ID,可以明确该bucket node与其它node之间的链接关系,以及该bucket node在上述FDMT树上的具体位置;比如,基于该节点ID,可以明确该bucket node链接在第三层tree node的第5个block的第6个槽位;而第三层的tree node又链接在第二层tree node的第3个block的第4个槽位;第二层的tree node又进一步链接在第一层作为根节点的tree node的第1个block的第2个槽位。
通过这种方式,可以在使用节点ID来检索FDMT树上存储的节点时,可以精确定位到该节点在FDMT树上所在的具体槽位。
在另一种实现方式中,上述节点ID具体也可以包括上述FDMT树上的节点在上述FDMT树中的相对位置,和该节点所包含的数据内容的hash值(此时该节点ID也可以作为该节点的hash标识);也即,将上述FDMT树上的节点在上述FDMT树中的相对位置,和该节点所包含的数据内容的hash值作为该节点的节点ID。
例如,在实现时,可以将节点在上述FDMT树中的相对位置,和该节点所包含的数据内容的hash值拼接生成的字符串,作为该节点的节点ID;当然,在实际应用中,在进行拼接的过程中,也可以进一步引入上述相对位置和上述hash值以外的其它类型的信息来生成节点ID;在本说明书中不再一一列举。
通过这种方式,除了可以基于节点所包含的数据内容的hash值作为key来进行内容寻址以外,还可以在使用节点ID来检索FDMT树上存储的节点时,可以精确定位到该节点在FDMT树上所在的具体槽位。
其中,需要说明的是,上述节点设备上用于承载上述数据库的存储介质,具体可以是持久性存储介质;例如,可以是能够对数据进行持久性存储的磁盘、内存或者其它形式的存储介质,在本说明书中不再进行一一列举。
在本说明书中,在将上述bucket数据桶中包含的若干条数据记录,存储至上述数据库时,还可以将上述bucket数据桶中包含的若干条数据记录中的至少部分数据记录,进一步聚合成为若干小数据块,以小数据块的形式存储在承载该数据库的物理存储介质中;例如,可以存储在不同的单位存储单元中,以上描述的逻辑分离的基础上进一步实现物理隔离。
其中,上述小数据块的存储容量,具体可以与上述物理存储介质支持的单位存储单元的存储容量进行适配;在实际应用中,上述小数据块的存储容量,具体可以不大于上述物理存储介质支持的单位存储单元的存储容量即可(即上述小数据块的存储容量,尽可能的接近上述单位存储单元的存储容量)。
例如,以上述单位存储单元为上述物理存储介质的扇区为例,假设上述物理存储介质支持的扇区大小为4KB,则可以将上述bucket数据桶中包含的若干条数据记录中的至少部分数据记录,进一步聚合成为存储容量不大于4KB的若干小数据块,在上述存储介质中不同的扇区进行存储,实现物理隔离。
在示出的一种实施方式中,在上述bucket数据桶中包含的上述数据记录,为用于存储上述区块链数据的key中的字符后缀和上述区块链数据的value的key-value键值对的情况下,除了每一条key-value键值对的key和value而言,可以是在逻辑上分离的数据以外,上述bucket数据桶包含的若干条key-value键值对中的至少部分key-value键值对中的key和value,也可以分别被聚合成为若干小数据块,存储在承载上述数据库的物理存储介质中不同的单位存储单元。
也即,可以将如图6所示的上述bucket数据桶的数据头部分包含的至少部分key,聚合成为若干小数据块,存储在承载上述数据库的物理存储介质中;还可以将如图6所示的上述bucket数据桶的数据体部分包含的至少部分value,也聚合成为若干小数据块,存储在承载上述数据库的物理存储介质中。
其中,上述小数据块的存储容量,具体也可以不大于上述物理存储介质支持的单位存储单元的存储容量即可。
例如,以上述单位存储单元为上述物理存储介质的扇区为例,假设上述物理存储介质支持的扇区大小为4KB,则可以将上述bucket数据桶的数据头部分包含的至少部分key,进一步聚合成为存储容量不大于4KB的若干小数据块,在上述存储介质中不同的扇区进行存储;
相应的,还可以将上述bucket数据桶的数据体部分包含的至少部分value,也进一步聚合成为存储容量不大于4KB的若干小数据块,在上述存储介质中不同的扇区进行存储。
通过这种方式,由于上述存储介质的单次IO读写的容量通常取决于该物理存储介质支持的单位存储单元的存储容量;比如,以单位存储单元为扇区为例,上述存储介质的单次IO读写的容量通常是扇区的存储容量的整数倍;而上述小数据块的存储容量又与上述单位存储单元的存储容量相适配;因此,通过将上述bucket数据桶包含的若干条数据记录中的至少部分数据记录,聚合成为若干小数据块,存储在承载数据库的物理存储介质中不同的单位存储单元之中,可以充分利用上述物理存储介质自身的IO读写能力,降低IO读写所需的带宽,提升数据读写效率。
以下以上述区块链数据为区块链账户对应的最新账户状态数据,上述区块链数据的key为区块链账户地址为例,对将上述区块链数据以key-value键值对的形式,写入采用如图4和图5所示的FDMT树的具体过程,进行详细描述。
在实现时,接入区块链的用户客户端,可以将交易数据打包成区块链所支持的标准的交易格式,然后发布至区块链;而区块链中的节点设备,可以基于搭载的共识算法与其它节点设备一起,对用户客户端发布至区块链的这些交易进行共识,以此来为区块链产生最新区块;其中,具体的共识过程不再赘述。
区块链中的节点设备在执行了目标区块中的交易之后,区块链中与这些被执行的交易相关的目标账户的账户状态,通常也会随之发生变化;因此,节点设备在目标区块中的交易执行完毕后,可以获取上述目标账户发生账户更新后的最新账户状态数据(即目标区块中的交易执行后的账户状态),并将获取到的上述目标账户的最新账户状态数据处理成key-value键值对;其中,该key-value键值对的key为该目标账户的账户地址;该key-value键值对的value为该目标账户的最新账户状态。
当将获取到的上述目标账户的最新账户状态数据处理成key-value键值对之后,可以将该key-value键值对转换成为上述FDMT树上的Tree node、Extend node和Leafnode,并将上述Tree node、Extend node和Leaf node的key-value键值对在数据库中进行存储。
请参见图10,图10为本说明书示出的一种将账户状态数据写入FDMT状态树的示意图;
在图10示出的上述FDMT状态树中,前三层为Tree node,最后一层为叶子节点;其中,上述叶子节点可以采用以上描述的bucket数据桶的存储结构。各层的Tree node均包括主block和16个分别代表不同的16进制字符1~f的子block;并且,各子block进一步包括16个分别代表不同的16进制字符1~f的槽位。
假设上述目标账户的账户地址为“a71135125”,最新的账户状态数据为“state1”;此时,该账户地址的字符前缀(Shared nibble)为“a71135”;字符后缀(key-end)为“125”。
在将该账户地址“a71135125”的账户状态数据“state1”以key-value键值对的形式写入上述FDMT树时,首先可以在数据库中定位该FDMT树上作为根节点的Tree node(比如根据区块头中填充的根节点的hash来定位根节点);如果是首次数据写入,根节点尚未创建,此时可以创建根节点;再从第一层的根节点Tree node1开始,依次在三层Tree node中确定与该账户地址的字符前缀“a71135”对应的字符槽位。如图10所示,与该账户地址的字符前缀“a71135”对应的字符槽位,具体包括:Tree node1的第11个子block(代表字符a)中的第8个槽位(代表字符7);Tree node2的第2个子block(代表字符1)中的第2个槽位(代表字符1);Tree node3的第4个子block(代表字符3)中的第6个槽位(代表字符5)。
在确定以上槽位后,可以在Tree node3的第4个子block的第6个槽位链接的bucket node中,写入由字符后缀“125”和状态数据“state1”构成的数据记录;当然,如果bucket node中已经存在与字符后缀“125”对应的数据记录,表明FMDT树上写入过上述账户地址“a71135125”的历史账户状态数据对应的value;此时可以将该数据记录中存储的Value更新为“state1”即可。
数据写入完成后,可以重新计算该bucket node包含的数据内容的hash值,将该hash值填充到Tree node3的第4个block的第6个槽位中,对该槽位原有的hash值进行更新。
进一步的,在将Tree node3的第4个block的第6个槽位中原有的hash值进行更新之后,再重新计算Tree node3的主block包含的数据内容的hash值,将该hash值填充到Treenode2的第2个block的第2个槽位,对该槽位原有的hash值进行更新。
然后,在将Tree node2的第2个block的第2个槽位原有的hash值进行更新之后,再重新计算Tree node2的主block包含的数据内容的hash值,将该hash值继续填充到Treenode1(即根节点)的第11个block的第8个槽位,对该槽位原有的hash值进行更新。
当根节点Tree node1的第11个block的第8个槽位原有的hash值也更新完成后,此时再重新计算根节点Tree node1的主block包含的数据内容的hash值,并基于该hash值对区块头里存储的FDMT状态树的根节点的hash值进行更新。当区块头里存储的该FDMT状态树的根节点的hash值更新完成后,此时针对该FDMT状态树的更新完成,上述账户地址“a71135512”和对应的账户状态数据“state1”构成的key-value键值对,成功写入到FDMT状态树。
需要强调的是,以上述区块链数据为区块链账户对应的最新账户状态数据为例,仅为示例性的;在实际应用中,当上述区块链数据为以上描述的4种区块链数据的其它类型的区块链数据时,将其写入相应的FDMT树的具体过程,与以上述描述的实现过程类似,在本说明书中不再进行一一赘述。
与上述方法实施例相对应,本申请还提供了装置的实施例。
与上述方法实施例相对应,本说明书还提供了一种区块链数据存储装置的实施例。
本说明书的区块链数据存储装置的实施例可以应用在电子设备上。装置实施例可以通过软件实现,也可以通过硬件或者软硬件结合的方式实现。以软件实现为例,作为一个逻辑意义上的装置,是通过其所在电子设备的处理器将非易失性存储器中对应的计算机程序指令读取到内存中运行形成的。
从硬件层面而言,如图11所示,为本说明书的区块链数据存储装置所在电子设备的一种硬件结构图,除了图11所示的处理器、内存、网络接口、以及非易失性存储器之外,实施例中装置所在的电子设备通常根据该电子设备的实际功能,还可以包括其他硬件,对此不再赘述。
图12是本说明书一示例性实施例示出的一种区块链数据存储装置的框图。
请参考图12,所述区块链数据存储装置120可以应用在前述图11所示的电子设备中,所述装置120包括:
获取模块1201,获取待存储的区块链数据的key-value键值对;
转换模块1202,将所述待存储的区块链数据的key-value键值对转换成逻辑的树形结构上的根节点、中间节点和叶子节点;所述根节点、中间节点用于存放所述区块链数据的key中的字符;所述树形结构上的任一节点通过其hash值与上一层的节点链接;所述叶子节点包含若干条在逻辑上分离的数据记录;所述数据记录中存储的数据内容包括所述区块链数据的value;
存储模块1203,将所述根节点、中间节点和叶子节点的key-value键值对存储在数据库中;其中,所述叶子节点、所述中间节点和所述根节点的key-value键值对中,value为节点的存储内容,key为节点的存储内容的hash值;
将所述叶子节点的key-value键值对存储在数据库包括:
将所述叶子节点包含的若干条数据记录中的至少部分数据记录,聚合成为与承载所述数据库的物理存储介质的单位存储单元的存储容量适配的若干数据块,在所述物理存储介质中存储。
在本实施例中,所述叶子节点为bucket数据桶。
在本实施例中,所述区块链数据的key对应的字符串包括字符前缀和字符后缀;所述根节点、中间节点用于存放所述字符前缀中的字符;所述数据记录中存储的数据内容包括所述字符后缀和所述区块链数据的value。
在本实施例中,所述数据记录包括所述数据内容的key和所述数据内容构成的key-value键值对。
在本实施例中,所述数据内容的key和所述数据内容,为逻辑上分离的数据。
在本实施例中,所述叶子节点包括在逻辑上分离的数据头部分和数据体部分;所述数据头部分包含用于集中存储所述数据内容的key的若干条数据记录;所述数据体部分包含用于集中存储所述数据内容的若干条数据记录;其中,所述数据头部分包含的数据记录与所述数据体部分包含的数据记录一一对应。
在本实施例中,所述叶子节点的hash值,为所述叶子节点中的数据头部分包含的数据内容的hash值;所述树形结构上的叶子节点,通过其数据头部分包含的数据内容的hash值,链接至其上一层的节点。
在本实施例中,所述根节点、中间节点包括多个用于存放所述区块链数据的key中的字符的位置,每个位置中包括多个用于存放所述区块链数据的key中的字符的槽位;所述槽位用于存储与该节点链接的下一层节点的hash值。
在本实施例中,所述根节点、中间节点用于存储所述区块链数据的key中的至少部分字符;所述根节点、中间节点存储的所述至少部分字符,为所述根节点、中间节点中的所述位置所代表的字符,与所述位置中填充了hash值的槽位所代表的字符,进行拼接生成的字符串。
上述实施例阐明的系统、装置、模块或单元,具体可以由计算机芯片或实体实现,或者由具有某种功能的产品来实现。一种典型的实现设备为计算机,计算机的具体形式可以是个人计算机、膝上型计算机、蜂窝电话、相机电话、智能电话、个人数字助理、媒体播放器、导航设备、电子邮件收发设备、游戏控制台、平板计算机、可穿戴设备或者这些设备中的任意几种设备的组合。
在一个典型的配置中,计算机包括一个或多个处理器 (CPU)、输入/输出接口、网络接口和内存。
内存可能包括计算机可读介质中的非永久性存储器,随机存取存储器 (RAM) 和/或非易失性内存等形式,如只读存储器 (ROM) 或闪存(flash RAM)。内存是计算机可读介质的示例。
计算机可读介质包括永久性和非永久性、可移动和非可移动媒体可以由任何方法或技术来实现信息存储。信息可以是计算机可读指令、数据结构、程序的模块或其他数据。计算机的存储介质的例子包括,但不限于相变内存 (PRAM)、静态随机存取存储器 (SRAM)、动态随机存取存储器 (DRAM)、其他类型的随机存取存储器 (RAM)、只读存储器 (ROM)、电可擦除可编程只读存储器 (EEPROM)、快闪记忆体或其他内存技术、只读光盘只读存储器(CD-ROM)、数字多功能光盘 (DVD) 或其他光学存储、磁盒式磁带、磁盘存储、量子存储器、基于石墨烯的存储介质或其他磁性存储设备或任何其他非传输介质,可用于存储可以被计算设备访问的信息。按照本文中的界定,计算机可读介质不包括暂存电脑可读媒体(transitory media),如调制的数据信号和载波。
还需要说明的是,术语“包括”、“包含”或者其任何其他变体意在涵盖非排他性的包含,从而使得包括一系列要素的过程、方法、商品或者设备不仅包括那些要素,而且还包括没有明确列出的其他要素,或者是还包括为这种过程、方法、商品或者设备所固有的要素。在没有更多限制的情况下,由语句“包括一个……”限定的要素,并不排除在包括所述要素的过程、方法、商品或者设备中还存在另外的相同要素。
上述对本说明书特定实施例进行了描述。其它实施例在所附权利要求书的范围内。在一些情况下,在权利要求书中记载的动作或步骤可以按照不同于实施例中的顺序来执行并且仍然可以实现期望的结果。另外,在附图中描绘的过程不一定要求示出的特定顺序或者连续顺序才能实现期望的结果。在某些实施方式中,多任务处理和并行处理也是可以的或者可能是有利的。
在本说明书一个或多个实施例使用的术语是仅仅出于描述特定实施例的目的,而非旨在限制本说明书一个或多个实施例。在本说明书一个或多个实施例和所附权利要求书中所使用的单数形式的“一种”、“所述”和“该”也旨在包括多数形式,除非上下文清楚地表示其他含义。还应当理解,本文中使用的术语“和/或”是指并包含一个或多个相关联的列出项目的任何或所有可能组合。
应当理解,尽管在本说明书一个或多个实施例可能采用术语第一、第二、第三等来描述各种信息,但这些信息不应限于这些术语。这些术语仅用来将同一类型的信息彼此区分开。例如,在不脱离本说明书一个或多个实施例范围的情况下,第一信息也可以被称为第二信息,类似地,第二信息也可以被称为第一信息。取决于语境,如在此所使用的词语“如果”可以被解释成为“在……时”或“当……时”或“响应于确定”。
以上所述仅为本说明书一个或多个实施例的较佳实施例而已,并不用以限制本说明书一个或多个实施例,凡在本说明书一个或多个实施例的精神和原则之内,所做的任何修改、等同替换、改进等,均应包含在本说明书一个或多个实施例保护的范围之内。