共计 5371 个字符,预计需要花费 14 分钟才能阅读完成。
【背景】
随着区块链技术的倒退和利用场景的逐渐丰盛,越来越多的人开始接触区块链。但在过程中,很多人提过这样的问题:“底层用区块链零碎和用数据库有什么区别呢?”、“区块链零碎是不是就是一个 OLTP 数据库系统?”…
直观的角度来看,残缺的区块链零碎外部肯定会蕴含一个「存储模块」,整体而言,区块链零碎的确能够起到长久化数据的作用。
然而如果从这个角度登程,间接将区块链零碎看作是一个数据库,这样的观点也是有待商讨的。在作出最终比拟之前,咱们先来剖析一下传统数据库系统的运行机制以及区块链零碎外部存储模块的性能职责。
【传统 OLTP 数据库 VS 区块链存储】
▲ 传统 OLTP 数据库存什么?
现阶段的数据库系统、存储引擎的设计个别是面向某一通用场景的,比方 sql 型数据库、NoSQL 型数据库(kv 数据库、文档数据库等),而不是面向具体业务场景的,那个别的 OLTP 数据库外部的数据分为四大类:
数据库外部的治理性质的元数据。这部分数据基本上对用户是通明的,负责数据库外部的治理与管制逻辑;
用户自定义数据。这部分数据是用户通过 API 向数据库写入的数,数据库系统个别不关怀这部分数据的具体内容,而是侧重于如何正确、残缺的将这些数据保留到长久化设施;
索引数据。索引数据个别是数据库设计中不可或缺的一个组成部分。为了保障数据库“读数据”性能的响应工夫在用户可承受范畴内,简直所有数据库系统都须要或多或少的引入索引;
日志数据。日志数据是一种数据库外部数据,其内容个别是记录数据变更行为,个别用于数据库宕机重启后的数据恢复。在不同的数据库中,日志数据的内容差距也十分大,比方:有的数据库应用日志来记录存储层的数据变更(磁盘上地位 X 开始,间断 N 个字节从 Value1 变成了 Value2),有的数据库应用日志来记录用户的写入命令申请(比方插入操作,内容为 key=1,value=”ABC”)等等。
▲ 区块链存储模块有何不同?
当咱们站在区块链零碎外部“数据存储”性能的角度对待“区块链零碎”时,咱们会发现,区块链零碎具备确定性的零碎架构、确定性的外部业务逻辑,以及一些通用的数据组织格局(比方:区块是一种 append-only 模式的数据、只有虚拟机执行指令的过程中会批改状态数据等)。区块链零碎中的数据存储只须要满足这一套运行逻辑过程中的长久化需要即可,也就是说,区块链零碎为其存储模块划定了比通用数据库更小的模块性能边界。
▲区块链存贮存什么?——世界状态
从使用者角度来看,一个最惯例的区块链服务是由一个区块链网络提供的。区块链网络由多个节点形成,用户能够向区块链服务发送交易,区块链网络中的所有参加共识的节点会针对交易的内容、执行过程、执行后果达成统一的决定,并将执行后果返回给发动交易的用户。
上述过程是一个最惯例的区块链服务应用流程。然而,如果从区块链网络中的节点的角度登程,节点感知的过程或者变动有哪些呢?
咱们略去网络交互、共识、执行等等的细节阐明,间接探讨咱们关注的重点——“世界状态”:
如果咱们将区块链能够看为一个分布式的状态机:所有节点从同一个创世状态开始,顺次执行雷同程序的交易,驱动各个节点的状态依照雷同操作序列一直变动,实现所有节点在同一交易序列执行实现后,状态完全一致。而这个状态,就称为“世界状态”。
在区块链网路中,所有诚恳节点本地保护的“世界状态”是统一的,这个“世界状态”就是区块链存储模块要重点关注的内容。
解释了“世界状态”的定义,咱们还是要关注“世界状态”到底包含什么内容:首先,一个区块链零碎中的数据状态的更迭,肯定是由“用户交易”驱动的,那么咱们只须要保障‘世界状态’可能涵盖交易的“申请内容、执行过程和执行后果”三局部内容,就能够满足上述“分布式状态机”中的一致性要求。
接下来,咱们将从三个角度别离剖析“世界状态”中要存储的具体内容。
区块链零碎通用的存储内容——块链构造
区块链零碎中,区块通过保留前序区块的标识(个别都是用区块哈希)来造成逻辑上的一条链。这样做的目标和意义本文不再赘述,这里重点关注的是这个“区块”的数据组织和存储模式。
区块外面会存储哪些内容呢?个别的区块链零碎中,区块会被分为区块头和区块体两个局部。
“区块体”局部绝对简略,这个局部负责将交易内容依照一个确定的顺序存储下来;存储交易内容是很好了解的,但“一个确定的程序”是指什么呢?回顾上文,“世界状态”是由交易驱动的,不同的交易执行程序可能会导致数据变更操作的程序不同,进而很可能导致世界状态的不同。因而“区块体”的内容,即体现了残缺的交易内容,还同时体现了确定的交易程序。
“区块头”局部会蕴含很多的元素,区块头中个别会蕴含一个最重要的区块哈希字段,这个区块哈希个别会用于示意以后零碎的世界状态。这就要求区块哈希的计算起源至多蕴含:前一区块的区块哈希(代表了前序的世界状体)、以后区块对世界状态造成的变更影响(以后区块蕴含的所有交易的内容、执行后果和执行过程中对账本数据的批改)。然而,区块哈希的具体计算形式能够由执行层来决定,不同零碎间计算形式不尽相同 1。
注:目前也存在一些新型的区块链零碎采纳了 DAG 模型 [3] 组织交易,甚至造成区块构造,这种状况本文临时不做剖析。
账本数据——UTXO 模型账本 VS 账户模型账本
区块数据外部蕴含了交易内容,然而交易的内容个别只定义了对区块链上数据的批改申请,“执行过程”和“执行后果”这两局部数据可能并不蕴含于交易内容中。本节将首先探讨交易“执行过程”对“世界状态”的影响。为此,咱们引入了“账本数据”的概念。
目前比拟通用的账本数据组织模式包含:UTXO 模型和账户模型。
UTXO 模型绝对比较简单,零碎中仅反对一种运行模式:“UTXO”在不同的账户间流通。在这种运作模式下,一笔交易代表了一次流通行为,具体的流通过程为:输出的 UTXO 被销毁,输入的 UTXO 被创立,输出总额须要大于或等于输入总额[4]。
在 UTXO 模型下的账本数据定义较为直观:以后链上全量的未生产的 UTXO(包含 UTXO 上的绑定的脚本)。因为在这种模式下,如果交易执行胜利,则输入无效,如果交易执行失败,则输入有效。而执行过程中的锁定、解锁等脚本的验证过程不会产生额定的账本数据批改。因而交易执行过程不波及账本数据的更改。
然而账户模型的账本组织形式会稍微简单一些。个别账户模型的账本都是能够反对“智能合约”的,账户模型下的区块链零碎会建设一个“账户空间”。
“账户空间”是由很多个账户组成的,其中的账户可能分为多种类型。以以太坊为例,零碎中以一个通用的数据结构定义了一般账户、合约账户两种类型。一般账户的行为包含:发动交易、相互转账、创立账户等等;而合约账户则对应了一份部署在链上的智能合约,相应的,合约账户会治理所有在这份智能合约中定义的、须要存储到区块链账本中的 key-value 数据对,咱们称之为“状态数据”。
在交易执行的过程中,可能波及到“账户数据”和“状态数据”的变更,这些扭转可能既不会体现在合约内容自身中,也不会体现在交易执行后果中。因而,为了保障“世界状态”的完整性,通过交易执行批改后的整个“账户空间”的数据状态,须要被纳入“世界状态”的领域。
那么作为世界状态的一部分,零碎会规定一种特定的形式(以太坊的 MPT 树、Fabric 的 bucketTree 等等)计算出一个能反馈账户空间整体状态的、易于比拟的后果值,这个后果值会参加到上一节提到的“区块哈希”的计算中。(某些计算过程中的数据可能也会被长久化,比方 MPT 会将计算树根过程中的门路上的节点数据也存储在账本数据中)
交易执行后果 / 回执数据
“UTXO 模型”的零碎交易执行后果比拟间接,所有的非法交易的执行后果就是胜利标识 +“交易输入”内容;所有非法交易的执行后果就是失败标识,而交易的输出局部保持原状。
然而“账户模型”的区块链零碎中,个别会蕴含转账交易和智能合约调用交易。交易的执行后果个别要通过虚拟机执行失去,思考合约调用的场景,交易的执行后果很可能并不体现在交易内容和账户空间变更后果中;因而,交易的回执数据须要独立存储,为了对立账户创立、账户间转账、智能合约调用等多种交易的执行后果,零碎个别会设计对立的回执数据组织格局。
至此,交易数据 + 账户空间 + 交易执行后果三者一起形成了账户模型下的零碎的最根本的世界状态。相应的,这类零碎中的存储模块,须要提供这三类数据的存储性能。在具体设计和实现存储模块时,能够联合区块链零碎的运作模式,综合思考吞吐量和提早等指标,依据不同数据的业务特点设计和应用不同的底层存储引擎。
索引数据
一个残缺的区块链零碎除了提供上述写入性能外,还须要提供历史数据的查问性能。如果零碎中只存储了上述世界状态数据,那么用户对历史数据的查问只能通过遍历所有区块实现,这样的代价和提早很显然是无奈承受的。
因而,个别的区块链零碎都会思考设置一种根本的索引——交易索引。借由这种索引,零碎可能疾速的确认交易所在的区块,甚至能够更细粒度的间接定位到交易存储于这个区块内的哪个地位。索引的内容个别就是“交易哈希(交易标识)”到“交易所在地位”的映射。
这样的索引设计会给零碎长久化区块的过程带来肯定的“写放大”,单无论从数据量还是从必要性角度登程,个别的区块链零碎都会抉择承受这种代价。
【区块链存储相干的“一致性”】
至此,一个简略通用的区块链零碎必要的存储内容曾经介绍结束。然而这还不是区块链零碎的存储模块须要思考的全部内容。在区块链网络中,所有诚恳节点在雷同的区块执行结束后,须要领有完全一致的世界状态。这个世界状态能够由区块确认后的“区块哈希”示意。
然而,区块链零碎个别会运行在一个拜占庭网络环境中;那么,区块链零碎随时存在着数据落后的状况,整个网络也存在着随时新增和删除节点的状况。无论是落后的诚恳节点,还是新增进入网络的诚恳节点,都须要尽快追赶上网络中的其余诚恳节点。
那么落后的节点如何“追赶”其余的诚恳节点呢?这里咱们再从新扫视一下“交易”这个角色。前文提到:“区块链零碎的整体数据状态是依赖交易的执行而一直向后演进的”,那么交易就能够看作是区块链零碎世界状态变更的“日志”。
基于这样的了解角度,落后节点“追赶”其余诚恳节点能够通过同步诚恳节点的区块来实现,因为区块中的区块体包含了全量的交易内容,那么落后节点能够通过重放其短少交易,最终达到与其余节点统一的世界状态。这个复原的过程,落后节点不须要参加共识,而是借由一系列校验协定来确保数据的完整性和正确性。
进一步剖析上述交易重放流程,如果是 UTXO 模型的零碎,交易的内容和执行过程比较简单;然而对于账户模型的零碎,则交易可能是合约执行申请,那么执行这笔交易的过程就不可避免的会波及执行环境的创立,应用虚拟机执行残缺的合约指令等等步骤。那么,对于节点间网络环境较好的场景,是不是有进一步提高落后节点“追赶”速度的策略呢?
由此,在特定的场景下,比方“联盟链 + 账户模型”的零碎中,数据同步的过程能够引入另一种策略:如果在交易执行的过程中,零碎将所有的账本数据(也就是账户空间)批改相干的操作都记录下来,造成一份账本操作日志数据(能够类比 MySQL 中 Binlog 的 row 模式思路);那么在做数据同步时,落后节点能够间接拉取区块数据、账本操作日志数据和交易回执数据,实现后并将拉取账本操作日志数据按序利用到本地账本数据上,实现利用日志的过程后,落后节点便实现了与其余诚恳节点统一的“世界状态”的构建。
基于上述思路,存储模块能够引入另一种与世界状态无关的数据——操作日志数据,这些数据与交易的执行过程强相干。在某些网络带宽富余、智能合约计算逻辑简单但账本数据批改不频繁的场景下,上述思路是一种无效减速节点间数据同步的解决方案。
【小结与思考】
至此,本文第一个引入的“区块链存储模块是不是数据库”的问题,能够做一个简略的总结:笔者认为,数据库和区块链存储是能够辨别开来对待的概念,区块链存储模块无论从性能边界、服务对象还是本身的拓展优化思路上,都是和“区块链零碎”这个场景强相干的。本系列后续的文章会针对区块链零碎的外部逻辑和场景,别离探讨各种数据的存储模式和存储设计思路。然而 OLTP 数据库系统,其思考的场景则是一个更广阔、更通用的层面。
最初,可能局部读者会思考这样一个问题:本文始终在强调“区块链存储系统的专用性和为区块链场景定制”的思路,那在事实业务场景应用区块链底层平台时,平时常听到的“通用型平台”这个概念又该如何了解呢?
其实,“为区块链零碎的外部运作机理提供定制化的存储模块性能”,与“区块链零碎整体对外提供通用的业务反对”,是两个层面的问题,两者并不矛盾。本文探讨的存储模块的定制化,专用性,是针对区块链零碎的外部运行逻辑定制化的设计存储性能,然而区块链零碎中账本组织模型的设计和智能合约性能的存在,则容许用户为多种业务场景自定义合约逻辑、自定义账本“状态数据”。
作者简介
郭威
趣链科技根底平台部 区块链存储钻研小组
参考文献
[1] https://github.com/ethereum/g…
[2] https://github.com/hyperledge…
[3] https://developer.confluxnetw…
[4]《精通比特币 第二版》