Blurring the Lines between Blockchains andDatabase Systems: the Case of Hyperledger Fabric(翻译)

Blurring the Lines between Blockchains and Database Systems: the Case of Hyperledger Fabric

ABSTRACT(摘要)

在过去的几年里,市场上出现了不计其数的区块链系统,每个区块链系统都声称要以这样或那样的方式彻底改变分布式交易处理的方式。许多区块链特性,例如拜占庭容错,在现代环境中确实是有价值的补充。然而,尽管围绕着技术的炒作,区块链系统必须面对的许多挑战是基本的事务管理问题。这在很大程度上是与已经存在了几十年的传统数据库系统共享的。

       对于那些模糊了区块链系统和传统数据库系统之间界限的系统来说,这些相似之处尤其明显。一个很好的例子就是Hyperledger Fabric,这是一个由IBM开发的开源区块链系统。通过实现并行的交互处理,Fabric的工作流是由传统数据库系统中的乐观并发控制机制高度驱动的。这就提出了两个问题:(1)像Fabric这样的系统和一个经典的分布式数据库系统之间究竟存在哪些概念上的相似点和不同之处?(2)是否有可能通过将数据库技术转移到区块链来提高数据库的性能,从而进一步模糊这两种类型系统之间的界限?为了解决这些问题,我们首先从数据库研究的角度探讨Fabric,在数据库研究中我们观察到事务管道中的弱点。然后,我们通过将理解良好的数据库概念转换到Fabric来解决这些问题,即事务重新排序和事务早期中止。我们在Smallbank基准测试和特定工作负载下的实验评估表明,我们的改进版本Fabric++比普通版本显著提高了成功事务的吞吐量12倍,同时将平均延迟降低了近一半。

CCS CONCEPTS(ccs 的概念)

信息系统→分布式数据库操作;

ACM参考格式:

Ankur Sharma, Felix Martin Schuhknecht, Divya Agrawal和JensDittrich。2019. 区块链和数据库系统之间的界限变得模糊:超级账本结构的例子。2019年数据管理国际会议(SIGMOD ' 19), 2019年6月30日至7月5日,荷兰阿姆斯特丹。ACM,荷兰阿姆斯特丹,18页。https://doi.org/10.1145/3299869.3319883

1 介绍

    区块链是现代分布式事务处理中最热门的话题之一。然而,从数据库研究的角度来看,人们可能会提出这样一个问题:是什么使这些系统比已经存在了很长时间的传统分布式数据库如此特别?

    答案在于拜占庭式容错:虽然分类分布式数据库系统需要一组可信的参与者,但区块链系统能够处理一定数量的恶意行为节点。这个特性使许多新的应用程序领域,比如组织之间的事务,不完全信任彼此。

    在拜占庭式容错方面,区块链系统比分布式数据库系统有明显的优势。不幸的是,就事务处理的其他本质方面而言,经典的数据库系统要比区块链系统领先几十年。

这方面的一个很好的例子是订单-执行交易处理模型,像比特币[1]和Ethereum[2]这样的杰出系统实现了这个模型:在订单阶段,所有的同行首先同意一个全局交易订单,通常使用一个共识机制。然后,每个对等点在状态副本上按该顺序本地执行事务。虽然这种方法很简单,但它有两个严重的缺点:首先,事务的执行是按顺序进行的。其次,由于每个事务都必须在每个对等点上执行,所以系统的性能不会随对等点的数量而变化。当然,并行执行和可伸缩性都是分布式数据库系统多年来已经确立的特性。

1.1 Catching up (赶上)

不过,仍有一些区块链系统试图迎头赶上。一个突出的例子是Hyperledger Fabric [10], IBM推出的一个流行的开源区块链系统。它没有实现订单-执行模型,而是遵循复杂的 模拟-订单-验证-提交 模型。该模型受到数据库系统中乐观并发控制机制的高度影响:在对事务进行实际排序之前,会对其进行并行模拟。然后,在订购之后,Fabric在验证阶段检查订单是否与之前计算的模拟效果不冲突。最后,提交非冲突事务的影响。这种模式的优势是显而易见的:并行交易执行以及因此而产生的大规模交易能力——这些特性对于任何即将到来的区块链系统来说都是强制性的,以高性能为目标。我们坚信Fabric将技术从数据库世界转向区块链的雄心是朝着正确方向迈出的一步。然而,其并行事务处理的实现仍存在一些问题,极大地限制了并发所能获得的收益。这些问题可以通过两个简单的实验很容易地识别出来。

最上面的是(有意义的交易)红色的代表的是流产的内容,下面的内容代表(空白的交易) 

图1:在配置BS=1024、RW=8、HR=40%、HW=10%、HSS=1%的情况下,如第6节所述触发有意义的事务时,普通Fabric的每秒事务数。此外,我们还显示了启动空白事务时的吞吐量

在第一个实验中(图1,顶部的条),我们提交了一组有意义的事务,它们来自于一个资产转移场景,并报告吞吐量,分为失败的和成功的事务。这个实验揭示了织物的一个严重问题:大量的操作最终被中止。所有这些中止的原因都是序列化冲突,这是并发执行的一个负面副作用。如果我们希望增加成功事务的数量,我们基本上有两种选择:要么(a)增加系统的总体吞吐量,要么(b)将被Fabric中止的事务转换为成功的事务。不幸的是,选项(a)几乎不适用于织物。我们可以在第二个实验中看到这一点(图1,底部栏),其中我们提交没有任何逻辑的空白事务。实际上,空白事务和有意义事务的总吞吐量本质上是相等的。这表明,系统的总体吞吐量不是由事务处理的核心组件所控制的,而是由其他辅助因素所控制的:加密计算和网络开销。

1.2 Fabric++

因此,选项(b)是关键:我们必须将被Fabric中止的事务转换为成功的事务。我们通过将一个众所周知的技术从数据库系统转换到Fabric来实现这一点:事务重新排序。我们检查事务语义并以一种大大减少序列化冲突数量的方式安排事务,而不是任意地对事务排序。此外,我们尽可能早地从管道中删除那些不再有机会提交的事务。事务的这种早期中止进一步改善了这种情况,因为没有机会提交的事务不考虑重新排序。

总的来说,我们采取了以下步骤来进一步“数据库化”fabric:

(1)为了为讨论提供基础,我们首先从一个总体的角度考察了Hyperledger Fabric 1.2版本的交易流程。(第二节)。

(2)我们仔细检查并发控制领域的相关工作,并讨论哪些技术与fabric相关,哪些没有考虑。(第3节).这直接将我们引向我们正在集成的技术。

(3)在分析Fabric事务流的基础上,详细讨论了其不足之处,并描述了如何利用数据库技术来解决这些问题。(第四节)。

(4)将数据库技术过渡到Fabric的事务管道。准确地说,我们首先改进交易订单。默认情况下,系统在模拟后任意订购事务,导致不必要的序列化冲突。为了解决这个问题,我们引入了一种高级事务重排序机制,旨在减少一个块内事务之间的序列化冲突数量。这种机制显著地增加了通过系统的有效事务的数量,从而增加了总体吞吐量(5.1节)。

(5)接下来,我们推进事务的中止。默认情况下,Fabric在合并前检查事务是否有效。这种延迟中止不必要地使系统因处理没有机会提交的事务而受到惩罚。为了解决这个问题,我们在管道的各个阶段引入了早期中止的概念。我们尽可能早地识别出无效的操作并中止它们,以确保管道不会被那些最终没有机会提交的事务所限制。这个概念需要一种细粒度并发控制机制,我们也可以通过它扩展Fabric(第5.2节)。这些修改极大地扩展了香草结构,将其变成了我们所说的Fabric++。

(6)我们对Smallbank基准测试和自定义工作负载下的Fabric++的优化进行了广泛的实验评估。我们表明,与普通版本相比,我们能够显著增加成功事务的数量。此外,我们通过改变块大小、通道数量和客户端来表明我们的优化对系统的扫描能力也有积极的影响。此外,使用Caliper基准测试,我们还展示了Fabric++比vanilla Fabric产生更低的事务延迟(第6节)。

2 HYPERLEDGER FABRIC

首先,我们必须了解Fabric的工作流程。让我们在下一节中描述它在version 1.2中的行为。

2.1 Architecture (架构)

Fabric是一个许可的区块链系统,这意味着网络的所有对等点在任何时间都是已知的。用户被分组到组织中,这些组织通常是他们的宿主。在一个组织中,所有的同事都相互信任。每个对等体运行Fabric的一个本地实例。此实例包括分类账的副本,其中包含经过系统的所有交易的有序序列。这包括验证和无效事务。除了分类帐,每个peer还包含当前状态的形式的状态数据库,它表示的状态后应用所有验证事务在分类帐的初始状态。除了在模拟阶段和验证阶段扮演重要角色的对等点之外,还有一个单独的实例叫做订购服务,它是订购阶段的核心组件,被认为是值得信任的。

2.2 High-level Workflow 

在其核心,Fabric遵循模拟-订单-验证-提交工作流,如图2所示。

Figure 2: High-level workflow of Fabric.

2.2.1 仿真阶段。在模拟阶段,客户将一个交易建议分包给对等点的子集,称为背书对等点或背书人,以进行模拟。背书对等点的这个子集是在所谓的背书策略中定义的。由于组织之间不完全信任对方,因此通常指定,每个参与的组织至少有一个对等点必须模拟事务建议。背书者现在对当前状态的本地副本并行地模拟事务建议。如此阶段的名称所示,此时在当前状态下,模拟的任何效果都不可容忍。相反,每个背书人在模拟过程中建立一个读集和一个写集来捕捉效果。模拟之后,每个背书人都将其读和写设置返回给客户端。与此同时,背书人还会返回一个加密签名。如果所有返回的读和写集都是相等的,那么客户端将处理实际事务。它包含以前计算的读集和写集以及所有签名。然后客户端将该事务传递给订购服务。

2.2.2 排序阶段。在订购阶段,委托服务从客户端接收交易。在所有接收到的事务中,它建立一个全局顺序,并将它们打包成包含一定数量的事务的块。默认情况下,事务本质上是按照它们到达服务的方式进行处理的,而不以任何方式检查事务语义。然后,订单服务将每个形成的块分发给网络的所有用户。注意,系统不能保证所有的对等方同时收到一个块。但是,它保证了所有的对等点以相同的顺序接收相同的块。

2.2.3验证阶段。一旦一个块到达对等点,它的验证阶段就开始了。对于块中的每个事务,验证包括两个检查:首先,Fabric测试事务是否遵守背书策略,以及所有包含的签名是否适合读写集。如果不是这样,就意味着背书人或客户以某种方式篡改了交易。在这种情况下,系统将事务标记为无效。如果事务通过了第一个测试,Fabric将检查是否发生任何序列化冲突。由于在对事务进行排序之前,对事务的模拟是并行进行的,因此模拟的效果可能与已建立的顺序发生冲突。因此,Fabric将与以前的事务冲突的事务标记为无效的事务。

3 RELATED WORK(相关工作)

在深入研究我们所应用的优化之前,我们必须讨论一下该领域的相关工作。

2.2.4提交阶段。在提交阶段,每个对等点都将包含有效和无效操作的块分配到其本地分类账。此外,每个对等点将有效事务所做的所有更改应用到其当前状态。

在这项工作中,我们将成熟的数据库技术过渡到区块链的世界。正如在引言中提到的,我们基本上将数据库并发控制领域的两种主要技术应用到Fabric:事务重新排序和事务早期中止。当然,协同控制是一个广泛而活跃的研究领域,而机警者可能会想知道,我们为什么只关注这些技术。答案在于这样一个事实,像Fabric这样的区块链系统,尽管是一个并行事务处理系统,但与并行数据库系统有四个不同之处:

(a)像Fabric这样的区块链系统按海量的块提交,而不是按单个事务的海量提交。提交的粒度对适用的优化的类型有很大的影响。

(b)像Fabric这样的区块链系统是一个分布式系统,状态在整个网络上完全复制,事务在所有节点上操作。与此相反,并行数据库系统通常安装在本地的单个节点上,或者在网络上安装它们的状态分区,这样事务就在网络的一个子集上操作。分布和复制的类型对优化选项有显著影响。

(c)在Fabric中,在多个节点中并行模拟单个事务,建立信任。各个节点模拟事务的状态可能不同。在并行数据库系统中,情况要简单得多:针对系统中出现的唯一状态,只执行一次事务。

(d)像Fabric这样的区块链系统的性能主要由加密签名计算、网络通信和信任验证所控制。相反,并行数据库系统的性能在很大程度上受到低层组件的影响,例如选择用于并发控制的锁定机制。因此,尽管与数据库优化密切相关,区块链优化发生在不同的级别上。

现在让我们把有关的工作做完。从本质上讲,交易控制技术可以分为两类:(1)旨在提高整体交易吞吐量的方法[13,17,20,26]和(2)试图将失败的交易转变为成功的交易的方法[24,28]。

3.1 Class 1: Transaction Throughput (交易吞吐量)

[26]、[20]和[13]的工作都是为了提高并行事务的执行。在[26]中,作者提出了一种机制,可以收集一批事务并分析这些事务之间的访问依赖关系。然后将得到的依赖图划分为不相交的子图。由于不同子图中的事务不会相互冲突,因此可以安全地并行执行它们。[20]和[13]更进一步,不仅分析了整个事务之间的依赖关系,而且还分析了事务片段之间的依赖关系。准确地说,他们首先将事务分解为可能的片段,然后分析这些片段之间的依赖关系,同时考虑整个事务之间现有的依赖关系。这样,它们就能够实现更高的并行度。一般来说,这三篇论文提出的设计允许装备系统,否则将遵循纯粹的顺序执行,与部分并行执行。在区块链环境中,遵循顺序执行模型的系统可以从这种技术中获益。显然,Fabric不是这种方法的正确候选对象,因为默认情况下它已经并行化了模拟阶段。

[17]旨在从底层的角度改进并发控制。它对vcc组件提出了一些有趣的优化,比如时间戳分配、版本存储、验证、索引管理和恢复。这些技术可以应用于织物的底层存储系统中。然而,改进Fabric的低级组件不会提高整体性能,总体性能主要由顶级组件主导,这些顶级组件包括加密、网络和信任验证。

不幸的是,类(1)的技术并不适合提高Fabric等区块链系统的事务吞吐量。我们在介绍中的图1中看到了这样做的原因。对于空白事务,并发控制机制基本上不需要做任何工作。对于有意义的事务,模拟和验证必须同步。然而,吞吐量等于。这意味着,直接影响事务处理(如并发控制)的技术不能提高吞吐量。相反,系统由一些与事务处理没有直接关系的因素主导,比如加密计算和网络。因此,没有考虑类(1)的优化。

3.2 Class 2: Transaction Abort & Success (事务中止和成功)

[24]与我们的工作有关,因为它具有相同的动机:减少由于序列化冲突而不必要地终止的事务数量。在实现多版本并发控制(multi-version concurrency control, MVCC)的本地并行数据库系统中[16,21,25,27],作者建议使用共享锁额外保护每个经常访问的条目。使用这样的锁可以防止访问这些热条目的事务之间的读写冲突导致不必要的终止。实际上,这需要获得一个锁来确保潜在的冲突事务,确保它们按照非冲突的顺序提交。不幸的是,这种策略很难适用于Fabric的分布式事务处理模型。自织物模拟多个节点并行和提交事务,这种技术需要一个可信细粒度的分布式锁服务同步访问,导致过多的网络沟通和协调工作。

在[28]中,作者还旨在通过影响提交顺序来减少中止事务的数量。然而,他们采用了与[24]完全不同的策略:当事务T想要提交并且检测到一个读写冲突与一个已经提交的事务,那么它不会直接中止。相反,它检查是否可以简单地将混合时间回顾性地更改为它的开始时间。如果此更改没有触发与另一个并发事务的读-写、写-写或写-读冲突,则允许T在开始时提交。虽然这种技术可以应用于Fabric的订购服务中,但其效果是非常有限的。这是由于该方法的简单性导致的,该方法只允许将事务的综合时间更改为它的开始时间,而不是更改提交序列中其他可能的时间点。这浪费了大量的优化潜力。相反,我们的事务重排序机制考虑对一个块内的所有事务进行提交重排序,目的是找到最佳的全局顺序。

事务重新排序的好处最近也在[11]中得到了研究。在OLTP系统上下文中,作者指出,通过重新排序事务批,成功事务的数量最多可以提高2.7倍。

因此,类(2)方法是改进诸如Fabric这样的区块链系统的关键,这些方法旨在增加成功事务的数目并清除必须中止的事务。我们对事务重新排序和早期事务中止的优化就属于这个类。在下一节中,我们将详细阐述这些技术的重要性。

4 BLURRED LINES: FABRIC VS DISTRIBUTED DATABASE SYSTEMS(模糊的线条:Fabric与分布式数据库系统)

了解了Fabric的工作流之后,我们就能够讨论它与分布式数据库系统的关系。我们尤其对Fabric的某些方面感兴趣,这些方面(a)在概念上与分布式数据库系统共享,但(b)具有应用数据库技术的潜力。

4.1 The Importance of Transaction Order(交易顺序的重要性)

我们首先要看的是排序机制。这种组件也存在于任何具有事务语义的分布式数据库系统中,因此是将数据库技术转换到Fabric的一个很好的候选者。

如第2节所述,Fabric依赖于单个可靠的订购服务来订购事务。由于Fabric在执行订单之前模拟绑定到建议的智能契约,因此订单实际上会对事务之间的序列化冲突数量产生影响。同样,这是与任何并行数据库系统共享的属性,它将事务执行与事务提交分离开来。

在对事务进行排序时,可以使用各种不同的策略:最简单的选择是任意地对它们进行排序,例如按照它们到达的顺序。虽然这种到达顺序可以快速建立,但它可能会导致序列化冲突,而这可能是不必要的。这些冲突增加了无效事务的数量,这些事务必须由客户端重新提交。不幸的是,香草Fabric恰恰遵循了这种幼稚的策略。这是由订购服务不应该以任何方式检查事务语义(如读写集)的设计决策引起的。相反,它只是按照事务到达的顺序保留事务。这种策略可能会有问题,如表1中的示例所示。在这个例子中,四个事务按照它们到达的顺序被调度,即T1从T1到T2从T3到T4,其中T1将key k1从版本v1更新到v2。由于事务T2、T3和T4在它们的模拟过程中都在版本v1中读取k1,因此它们没有机会提交,因为它们操作的是k1值的过期版本。它们将在验证阶段被识别为无效,客户端必须重新提交相应的事务建议,从而导致新一轮的模拟、订购和验证。

表1:对于T1从T1到T2从T3到T4的顺序,只有1 / 4的事务是有效的:T2, T3和t4读取键k1中过时的版本v1,之前已经被T1更新为v2。

有趣的是,对于上一个示例中的四个事务,存在一个无冲突的顺序。如表2所示,在表T4⇒T2⇒T3⇒T1中,所有四个事务都是有效的,因为它们的读和写集合不会按顺序冲突。

这个示例表明,Fabric的普通订货程序错过了消除不必要的序列化冲突的机会。虽然这个问题对于区块链域来说是新的,因为区块链通常只提供事务的串行执行,但是在数据库社区中,这个问题实际上是众所周知的。通过事务的重新排序,存在旨在最小化序列化冲突数量的重新排序机制[11,18,29,30]。但是,在数据库系统中,通常避免在处理之前缓冲大量传入事务,因为必须实现低延迟。因此,在这样的设置中,重新排序并不总是一个选项。幸运的是,由于区块链系统以任何方式缓冲传入的事务,并将它们分组为块,这使我们有机会应用复杂的事务重新排序机制,而不会引入显著的开销。

表2:T4 从T2 起、T3 从 t1 起的顺序使得所有四个事务都是有效的。

我们将在5.1节中向Fabric添加这样的事务重新排序机制,这将显著增强通过系统的有效事务的数量。

4.2 On the Lifetime of Transactions(关于事务的生命周期)

我们从数据库的角度来看的第二个方面处理了管道内事务的生存期。在Fabric中,根据验证标准,通过系统的每个事务都被划分为有效或无效。在普通版本中,这种分类发生在验证阶段,就在提交阶段之前。这种形式的延迟中止的一个严重的缺点是,一个已经在早期阶段违反验证标准的事务仍然被处理和分布在所有对等点上。这会使整个系统承担不必要的工作,限制有效事务的性能。此外,这个概念还延迟了对客户端的中止通知。

我们必须区分违例发生在哪个阶段。首先,在模拟阶段可能已经发生了冲突,即所谓的跨块冲突,这意味着来自当前处于模拟阶段的后一个块的事务与来自前一个块的有效事务发生冲突。其次,冲突可能会像在排序阶段一样发生,其形式是单个块中匹配事务之间的块内冲突。

让我们分别在4.2.1节和4.2.2节中单独研究这两个场景

4.2.1仿真阶段的违例(跨块冲突)。

为了理解模拟阶段中的问题,让我们看看下面的情况以及普通版本的Fabric是如何处理它的。让我们假设有四个事务T1、T2、T3和T4,它们目前处于排序阶段,最终形成一个大小为4的块,并发送给所有对等点进行验证。在对等P中开始验证该块之前,事务建议T5的智能契约在P中开始模拟。在对等P中开始验证该块之前,事务建议T5的智能契约在P中开始模拟。为此,它需要对整个当前状态获取一个读锁(读锁可以被多个模拟阶段共享,因为它们不修改当前状态)。当模拟运行时,块必须等待验证,因为它必须获得当前状态的排他写锁。在模拟运行时,块必须等待验证,因为它必须获得当前状态的独占写锁。这种情况下的问题是:如果T1、T2、T3或T4写入一个由T5读取的键的值,那么T5将模拟陈旧的数据。因此,在读取时,事务实际上是无效的。不过,在Fabric的普通版本中,在t5的验证阶段之前无法检测到这种陈旧的读取。因此,t5将继续它的模拟,并通过有序阶段,只是在最后失效。

4.2.2 Violation in the ordering phase (within-block conflicts).排序阶段冲突(块内冲突)。

除了跨块的冲突之外,块内的事务之间也可能存在冲突。在排序阶段将事务按特定顺序排列之后,就会出现这些冲突。例如,4.1节表1中的示例显示了一个调度,其中三个事务T2、T3和T4分别与同一块中先前调度的事务T1发生冲突。不幸的是,在Fabric的普通版本的订货程序中没有检测到这些冲突。包含t2、T3和t4的块将分布在网络的所有对等点上进行验证,尽管块中有3/4的事务实际上是无效的。与前面一样,这源于订购服务不检查事务语义的设计决策。

上述情况表明,Fabric错过了在冲突发生时中止事务的几个机会。与此相反,数据库系统通常非常热衷于终止事务[14],因为它减少了网络流量并节省了计算资源。在数据库上下文中,尽早“清理”管道的概念称为早期中止,它以各种方式应用这个概念。例如,除了违反某些标准的事务的早期中止之外,数据库系统通过在查询计划中下推选择和投影操作,尽可能早地从查询结果集中消除记录。

为了克服上述问题,我们将在Fabric的事务处理管道的几个阶段应用早期中止的概念。在此基础上,我们保证充分利用现有资源,并进行充分的有意义的工作。我们将在5.2节中对此进行详细说明。

5 FABRIC++

我们概述了Fabric的问题,以及它们如何与数据库系统上下文中已知的关键问题相关联。现在让我们来看看我们如何对付它们。首先,在5.1节中,我们介绍了一种事务重新排序机制,其目的是最小化不必要的块内冲突的数量。其次,在5.2节中,我们介绍了Fabric管道的几个阶段的早期事务中止。这还涉及到引入细粒度并发控制机制。

5.1 Transaction Reordering ()

当重新排序一组事务时,必须面对多个挑战。首先,我们必须识别哪些S的交易实际上与它们执行的行为相冲突。同样,这些冲突可能会发生,因为S的事务是在完全隔离的情况下模拟的。由于S的提交发生在稍后的阶段,它们没有机会看到彼此潜在的冲突的修改。准确地说,如果Ti写入一个由Tj读取的键,那么Ti和Tj(记为Ti -> Tj)这两个事务之间就会发生冲突。在这种情况下,Ti必须被安排在Tj之后(表示为Tj⇒Ti),以使时间表可序列化,否则,读取Tj将会过时。不幸的是,这个问题通常更加复杂,因为可能会出现冲突的循环,因此简单的重新排序无法解决这个问题。例如,如果我们有冲突Ti -> Tj -> Tk -> Ti的循环,那么这三个动作的顺序就不是可序列化的。因此,在重新排序事务之前,我们的机制实际上必须首先删除S的某些事务,以形成无循环的子集S`⊆S,从中可以生成可序列化的调度.

从高层次来看,必须完成以下五个步骤:(1)首先构建s的所有交易的冲突图(2)然后确定冲突图中的所有周期。在此基础上,我们确定每个事务在哪个周期中发生。接下来,我们逐步中止在周期中发生的事务,从大多数周期中发生的事务开始,直到所有周期都被解决。最后,我们从维护事务开始构建一个可序列化的调度。算法1的伪代码实现了这五个步骤。

5.1.1的例子。为了理解原理并讨论一些实现细节,让我们看一个具体的例子。让我们假设有一组从T0到T5的6个事务需要考虑重新排序。这六个事务具有读和写集,如表3所示。总的来说,它们访问10个唯一的key K0到K9.

func reordering(Transaction []S ) {

//步骤1:对于S buildConfl ictGraph()中的每一个事务,
//检查读写集并构建冲突图
Graphcg = buildConflictGraph(S)
//Step2:在冲突图中,我们必须识别所有发生的循环。我们通过使用divideIntoSubgraphs()中的Tarjans算法[2]将cg划分为强连通子图来实现这一点。
Graph[] cg_subgraphs = divideIntoSubgraphs(cg)
}

步骤(1):根据这些信息,我们现在必须生成事务的冲突图,就像算法1的第4行中的函数buildConflictGraph()所做的那样。为了有效地做到这一点,我们将表3中的行解释为长度为10的位向量。让我们将它们称为vecr(Ti)用于读取访问,将vecw(Ti)用于事务Ti的写入访问。对于每个事务Ti,我们现在对所有j, i在vecr(Ti)和vecw(Tj)之间执行一个位&操作。如果&操作的结果不是0,我们就识别了一个读写冲突,并在相应事务之间的冲突图中创建了一条边。作为一个结果.

表3:由6个操作访问的10个唯一键,它们在读集和写集中分开。

我们得到了六个事务的冲突图C(S),如图3所示。

注意,这个算法在事务数量上具有二次复杂度。尽管如此,我们应用它是因为考虑的事务数量在实践中非常小,这是由于块大小的消除,因此开销可以忽略不计。

步骤(2):为了识别循环,我们在第9行divideIntoSubgraphs()函数中应用Tarjan的算法[22]来识别所有强连接子图。一般来说,这可以在O(N + E)除以节点数N和边数E的线性时间内完成,并产生如图4所示的三个子图。

利用Johnson的算法[15],我们可以识别强连通子图中的所有周期。同样,这一步可以在O((N + E)·(C +1))的线性时间内完成,其中C为循环数。因此,如果子图中没有循环,那么这一步的开销就非常小。

图4:图3冲突图的三个强连通子图。

我们确定第一个子图(绿色)包含两个循环 第二个子图(红色)包含循环 第三个子图(黄色)只包含一个节点,因此是无循环的。

步骤(3):根据这些信息,我们可以为每一个出现在周期中的事务构建一个取消注释表,如算法1的第15到18行所示。表4可视化了我们的示例的结果。如果事务Ti是循环cj的一部分,则将相应的单元设置为1,否则设置为0。表的最后一行对每个事务包含多少个周期进行了汇总.

表4:如果一个事务是循环cj的一部分,则对应的单元设置为1,其他设置为0。最后一行包含每个事务出现的周期总数。

步骤(4):我们现在迭代地从出现在大多数周期中的事务开始删除参与周期的事务。算法1的第33到40行显示了相应的伪代码。正如我们所看到的,T0和T3都出现在两个循环中,所以我们首先处理它们。如果我们可以在两个事务(比如T0和T3)之间选择,我们会选择下标较小的那个。这就保证了我们的算法是确定性的。我们移除T0,它清除了所有出现T0的循环,即c1和c2。事务T2和事务T4仍分别参与c3周期。我们移走了T2,清除了c3,从而进入了最后一个循环。

由此我们知道集合S0= {T1, T3, T4, T5}可以生成一个可序列化的调度,导致无循环冲突图C(S0)(第46行),如图5所示。

图5:无循环冲突图C(S0),只包含事务T1、T3、T4和T5。

步骤(5):生成最终的调度本质上是两个部分的竞争性执行,直到所有节点都被调度:(a)源节点在当前子图中的位置(第53至61行)和(b)所有可从源获取的节点的调度(第64至70行)。

我们在节点C(S0)处开始部分(a), C(S0)表示下标最小的事务,即T1。从这个开始节点,我们必须找到一个源节点,因为源必须被安排在最后。T1有两个父结点,即T3和T4,所以它不是源。我们沿着这条边走到T3,它还没有被访问过,但也不是一个源,因为它也有T4作为父节点。我们沿着边缘到T4,它还没有被访问过,是一个源。因此,我们可以安全地将T4调度到调度中的最后一个位置,我们称之为位置4。现在,部分(b)启动时,必须在T4可到达的所有节点之前进行调度。T4有两个子结点T1和T3。我们沿着边到T1,它还没有被安排。但是,由于T1有一个从T3进入的边,我们也不能直接对它进行安排。首先,访问T3并确定它有一个以T4形式存在的父节点,即我们开始时的源。有了这个信息,我们知道T3必须被安排在位置3,T1必须被安排在位置2。这结束了第(b)部分,因为所有可到达节点都已被调度。接下来,我们在仅剩下的节点T5上重新启动。由于T5不仅是一个源,而且是一个汇聚,我们可以立即将其调度到位置1。这导致了最终的进度表T5(从T1到T3到T4),该进度表被返回给定序者。

请注意,我们的重新排序机制不能保证中断最少数量的事务,因为这将是一个NP-hard问题。但是,它提供了一种非常轻量级的方法来生成一个具有少量中止的可序列化调度。

5.1.2 Batch Cutting. (批量切割)在交易重组上下文中,我们必须讨论和扩展订购服务中的一种机制,即批量切割,在第2节中,为了简单地去除织物,我们省略了这种机制。当订购服务接收到以源源不断的流形式出现的交易时,它会根据多种标准决定何时“削减”一批交易以完成它并形成交易块。在香草版中,只要符合以下三个条件之一,就可以切成片:(a)批处理包含一定数量的事务。(b)批处理已达到一定的字节大小。(c)自收到该批的第一个事务以来已经过了一段时间。在Fabric++中,我们通过一个附加条件扩展了这些条件。如果(d)批处理内的事务访问一定数量的惟一键,我们还可以删除批处理。这个条件确保了我们的重新排序机制的运行时间,特别是步骤(1)的时间,保持有限。

5.2使用高级并发控制的早期事务中止

前面描述的重新排序机制不仅试图最小化不必要的中止数量,而且还支持某种形式的早期中止。由于参与冲突周期而从S中删除的事务可以在订购阶段而不是稍后在验证阶段中止。这确保了更少的事务分布在网络上。

在下面,我们希望在管道中尽早终止事务的概念达到极限。除了在冲突周期中发生的早期中止事务外,我们还可以集成另外两个早期中止的应用程序,正如我们将在5.2.1节和5.2.2节中描述的那样。第一个已经在模拟阶段发生了。下面让我们看看这是如何工作的。

5.2.1仿真阶段提前中止  为了在模拟阶段实现早期中止,我们首先必须通过更细粒度的并发控制机制来扩展Fabric,该机制允许在对等体中并行执行模拟和验证阶段。有了这样的机制,我们就有机会在模拟过程中识别过时的读取。

为了理解这个概念,让我们再次考虑4.2.1节中的示例。有了细粒度并发控制机制,当绑定到提议T5的智能契约进行模拟时,包含T1、T2、T3和T4的块不必为验证花费时间。相反,在T5进行模拟时,这四个事务将以原子方式应用它们的更新。这种设计的结果是,对于T5执行的每次读取,我们都可以检查读取的值是否仍然是最新的。一旦我们检测到过时的读取,我们就可以中止对事务建议的模拟。此外,我们直接通知相应的客户中止,这样它就可以立即重新提交建议。

下面让我们讨论一下我们的细粒度并发控制机制是如何工作的,以及如何在Fabric++中实现它。在现代数据库系统中,先进的并发控制机制已经建立[16,19,21,23,24,27]。这些技术不是锁定整个存储,而是通常在记录级别甚至单个单元/值级别执行细粒度的锁定。由于数据库系统的存储和Fabric对等点中使用的存储在概念上没有区别,因此可以在这里应用类似的技术。

图6:使用我们的细粒度并发控制的并行化与早期中止。

正如第2节中所讨论的,Fabric以键值存储的形式实现其当前状态,该存储将每个单独的键映射到一对值和版本号。版本号实际上由执行更新的事务的ID和包含事务的块的ID组成。在Fabric的原始版本中,版本号的唯一用途是标识过时的读取。在验证阶段,对于每个事务,我们检查读值的版本号是否仍然与当前状态的版本号匹配。

我们可以更进一步,利用可用的版本号来实现保护当前状态的无锁并发控制机制。为此,在Fabric++中,我们首先删除读写锁,因为它不必要地对模拟和验证阶段进行了排序。使用每个值维护的版本号足以确保与普通版本具有相同的事务隔离语义。由于不再获得锁,我们需要一种机制来确保验证阶段执行的更新不会被并行运行的模拟阶段看到。为了实现此行为,在模拟过程中,我们必须检查每个读取值的版本号,并测试它是否仍然是最新的。

图6使用一个具体示例可视化了这个概念。在模拟阶段的开始,我们首先识别进入分类账的最后一个块的块id。我们将这个块id称为最后一个块id。在我们的示例中,last-block-ID = 4。在模拟绑定到事务建议Texec的智能契约期间,任何读操作都必须遇到包含比last块id高的附加id的版本号。如果它确实看到了更高的块id,这意味着在模拟阶段,在验证阶段有效的已验证事务修改了Texec的读集中的值,因此,读集是过时的。

在我们的示例中,在模拟阶段读取balA =70的操作发生在验证阶段将balA更新为50之前。这反映在balA的版本号上,即block-ID = 4。因此,该读取是最新的,模拟将继续进行。与此相反,balB的读取发生在验证阶段将balB更新到100之后。这反映在balB的版本号上,即block-ID = 5。由于5比last-block-ID = 4高,我们可以直接将Texec归为无效,因为稍后事务将没有机会通过验证阶段。请注意,我们的无锁机制的总体正确性是通过版本号的原子更新来确保的。

5.2.2在订货阶段提前中止。 除了模拟阶段的早期中止之外,如5.2.1节中所解释的,我们还可以将类似的概念转换到排序阶段。由于Fabric以整个块的粒度执行提交,同一块中读取相同键的两个事务必须读取该键的相同版本。例如,让我们考虑两个事务T6和T7,其中T6在同一个块中被安排在T7之前(T6从T7开始)。如果T6在各自的模拟中读取密钥k的版本v1和T7读取密钥k的版本v2,则T7无效。如果在T6和T7的模拟之间,对k值的更改是由来自前一个块的有效事务提交的,那么就可能发生这种版本不匹配。因此,一旦我们检测到同一块中事务之间的版本不匹配,我们就可以提前终止后一个事务。同样,该策略确保只有那些事务最终进入一个块,这些事务有实际的提交机会。

6 EXPERIMENTAL EVALUATION (实验评价)

在上一节中,我们以几种方式扩展和修改了Fabric的核心组件,将其转换为Fabric++。现在是时候从有效性的角度来评估这些修改了。首先,我们感兴趣的是通过系统的有效/成功和无效/失败事务的吞吐量。其次,我们对某些系统配置和工作负载特征对系统的影响感兴趣。

7 CONCLUSION (结论)

在这项工作中,我们在超级账本结构和分布式数据库系统的情况下,确定了当代区块链系统的交易管道的高度相似之处。我们详细分析了这些相似之处,并利用它们将成熟的技术从数据库系统上下文转换到Fabric,即事务重新排序以消除序列化冲突以及事务的早期中止,这些事务没有机会提交。在一个扩展的实验评估中,我们在Smallbank基准和自定义工作负载下测试了Fabric++和普通版本,我们表明,Fabric+能够在每秒成功事务数方面显著优于Fabric,最高达到12倍。此外,我们可以将事务延迟减少近一半,同时保持系统的伸缩能力不变。

8 ACKNOWLEDGEMEN (感谢)

这项工作由德国研究基金会(DFG)通过合作研究中心“理解和控制隐私的方法和工具”(SFB 1223)资助。

A  HYPERLEDGER FABRIC: A RUNNING EXAMPLE(超级账本结构:运行示例)

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页