Eris:使用网络内并发控制实现无需协调的一致性的事务

最近师兄安排我读一篇论文,自己粗略翻译一下论文的主要部分,欢迎大家批评指正

Eris:使用网络内并发控制实现无需协调的一致性的事务

摘要

  • 分布式存储系统旨在为 跨多片分区实现可扩展性并且通过复制实现容错的结构 提供强一致性和隔离性保证。传统上,达成上述目标需要原子提交和复制协议的昂贵组合,还会导致额外的协调开销。我们的系统(Eris)使用了不同的方法。它将并发控制功能的一个核心部分(multi-sequencing)移植进了数据中心网络本身。这个网络负责协调地为事务排序,并且一个新的轻量级事务协议保证了原子性。
  • 最后的结果是Eris既避免了复制也避免了事务协调开销:我们展示了它可以在一个单位从客户端到存储系统的往返时间内处理一大类的分布式事务,正常情况下不需要明确的片间或副本间的协调。它以低于10%的开销提供了原子性,一致性和容错性,吞吐量(巨大提升),延迟比标准基准上的常规设计降低72%-80%。

1 Introduction

  • 分布式存储系统如今面临事务语义和性能之间的担忧。为了满足大规模应用的需要,这些存储系统必须为了可扩展性而划分,为了可用性而复制。支持强一致性和严格的串行化会赋予系统 与单系统隔离地执行每个事务 同样的语义——将编程人员从一致性和并发性的推导中解放出来。不幸的是,这样做经常与有着高可扩展性和严格延迟限制的现代应用的性能要求不相容。交互式应用现在要求联系每个节点上的几百个或几千个独立存储服务,潜在地造成了每个单独的事务的延迟限制达到亚毫秒级。
  • 一般常识告诉我们事务处理系统因为协调的代价而不能满足这些性能需求。一个传统的架构需要通过一个令人头晕的协调协议数组足够小心地设计每个事务,比如,用于复制的Paxos协议,为了保持原子性的两阶段提交协议和为了隔离性的两阶段锁协议——每个都增加了自身的开销。正如我们在第8部分展示的那样,通过对大小的排序或其他操作,上述做法会增加延迟并减少吞吐量。
  • 这篇论文借助Eris(一个新的高性能分布式事务处理系统)对一般常识发起挑战。Eris面向数据中心环境下的高吞吐量和低延迟做优化。针对一类重要的事务,Eris可以零协调开销执行,既没有并发控制,原子提交的协调开销,也没有复制的协调开销,针对完全一般的事务,Eris能做到开销最小。它能够执行各种各样的工作负载,包括TPC-C,与非事务非复制系统相比,开销减低10%。
  • Eris架构以新方式划分了事务隔离性,容错性和原子协调的责任。Eris使用独立事务(independent transactioin)将事务排序的核心问题分离出来,然后使用新的集成网络协议优化处理。独立事务表示一个跨多片的一次性代码块的一次原子执行。这种抽象是有用的——许多工作负载可以使用独立事务独立地表示——也是更多复杂操作的一块积木。
  • Eris的主要贡献是一个新协议,这个协议可以无须明确协调地为独立事务的执行和事务提交的共识建立线性化的顺序。Eris使用自身的数据中心网络作为并发控制机制来指定事务顺序。我们定义并实现了一个新的网络层的抽象(multi-sequencing),保证信息以一致的顺序被传送到每个片上的所有副本并探测丢失的信息。Eris使用能确保可靠传输应用层协议增强这个网络层抽象。在正常的情况下,这个协议能够在从客户端到服务器的单往返时间内提交独立事务,不需要请求服务器彼此间的通信。
  • Eris是基于最近的 使用网络层排序 来排序副本系统中的请求 的工作。在一个分区系统上排序事务基本上比在单副本组中排序操作更有挑战,正如位于不同片的服务器看到不同的操作集合,但必须确保它们按照一致的顺序执行跨片事务。Eris使用一个新概念multi-stamp,multi-stamp为排序事务提供足够多的信息并且可以被一个网络内定序器很容易地实现。
  • 虽然独立事务很有用,但是它们并不能捕捉到所有可能的操作。我们展示了独立事务可以被用作积木来执行完全一般的事务。Eris使用“初事务”来收集读取依赖关系,然后使用单一“结论性的”独立事务提交它们。虽然这样做增加了上锁的开销,但是借助基础独立事务原语的高性能,Eris继续比常规的必须分别处理复制和协调的方法表现得好。
  • 我们在实验上评估Eris并验证了Eris提供了很大的吞吐量,延迟较常规方法(两阶段提交,使用Paxos协议和锁机制)降低72%-80%。因为Eris可以在单一往返时间内执行大多数事务而无需服务器间通信,在TPC-C基准测试中,Eris达到同样的性能所需代价只有一个无事务无副本系统的3%。这验证了强事务性,一致性和容错性保证可以实现,没有性能惩罚。

2 背景

  • 我们研究为可扩展性而分区,为容错性而复制的系统。数据在不同的分片之间进行分区,每个分片包含多个副本和所有分片数据的副本。用户(如前端服务器)提交需要被处理的事务。我们在这里限制自己在所有节点都在同一个数据中心的系统中。
  • 一个存储系统应当提供一些保证。每个事务应当被应用到它影响的所有片,或者一个都不影响(原子性)。执行应当与按顺序执行每个事物效果相同(严格可序列化隔离)。即便每片中的一些节点失效,这些保证也适当保持(容错性)。

2.1 现状:广泛协调

  • 现存的系统一般使用分层方法实现这些目标,如表1所示。一个复制协议(如Paxos)在每个数据片内都提供了容错。在片间,一个原子的提交协议(如两阶段提交)提供了原子性并与一个并发控制协议(如两阶段锁或者乐观并发控制)结合,虽然具体的协议不一样,但许多系统使用这个结构。
  • 一个后果是协调单一事务提交需要多轮协调。例如,图2展示了在常规分层架构中提交一个事物所需的协议交换。两阶段提交协议的每个阶段都需要异步执行一个复制协议来使得 事务协调决定 持久。此外,两阶段锁要求在准备和提交操作之间保持锁,阻塞产生冲突的事务。这个结合严重影响性能。

3 Eris的设计原则

  • Eris使用了一种不同的方法做事务的协调,使得Eris能够达到跟更高的性能。Eris基于下面三个原则:

3.1 原则一:排序与执行分离(Separating Ordering from Execution)

  • 传统的协调协议在执行事务的同时建立了它们的执行顺序,比如(执行顺序)作为执行期间获得锁的结果。Eris明确地从事务的执行过程中分离出构建事务顺序的任务,使得Eris能够使用一个优化的协议进行事务排序。
  • 为了做到这一点,Eris协议基于一个特定的事务模型:独立事务。独立事务在多个片上应用同时的原子改变,但是禁止跨片数据依赖(我们在4.1中给出了更精确的定义)。独立事务有关键的属性,以全局序列顺序在每个片上顺序执行它们可以保证可串行性。也就是说,建立一个全局的序列顺序允许事务执行无须进一步协调即可继续执行

3.2 使用网络内并发控制实现快速排序(Rapid Ordering with In-Network concurrency Control)

  • 我们可以多快建立独立事务的全局顺序?现存的系统需要事务中每个参与的片彼此协调以确保事务以一致的顺序在每个涉及到的片上处理。这要求在事务执行之前至少一轮通信,妨碍了系统性能。
  • Eris通过使用自身的网络排序请求来以最小的延迟建立事物的全局排序。最近的工作表明网络层的处理元素可以被用来为去往副本组的每个信息指定一个序列号,使得探测信息丢失或传送失序成为可能。Eris进一步使用了这个方法,使用网络排序去往不同片的多个操作流。关键原语multi-sequencing原子地为一个信息的每个目的地设置一个序列号,建立了信息的全局顺序,确保了任何接受者都可以检测出丢失或重新排序的信息。Eris使用这个建立了一个事务处理协议,除非信息丢失,否则无须协调。

3.3 统一复制和事务协调(Unifying Replication and transaction Coordination)

  • 传统的分层设计为跨片事务的原子提交和单一片中操作复制使用不同的协议。虽然这种分离提供了模块性,最近观察到这导致了两层间多余的协调。将跨片协调和分片内复制集成得到的统一的协议能够达到更高的吞吐量和更低的延迟。
  • 这个方法与Eris的网络内并发控制结合的特别好。因为请求被网络排序,一个片上每个单独的副本可以以相同的顺序独立地处理请求。因此,在普遍情况下Eris可以在单轮往返时间内执行独立事务,无需跨片或者片内协调。

4 Eris结构

  • Eris以新方式为不同的保证划分责任,使Eris能够无需协调地执行许多事务。协议本身被分成了3层,如图3所示:
    • 网络内并发控制层使用新的网络原语建立事务的一致的排序,既包括片内的也包括跨片的,但不保证信息传送的可靠性。
    • 独立事务层为排序的操作增加可靠性和原子性,确保每个事务在每个片内所有的非错误副本上最终执行(或者全部失败)。对重要的一类事务来说,这种排序和可靠性的结合足够保证线性化。
    • 一般事务层通过独立事务建立完全一般的事务并依靠其它层提供的线性化执行来为完全一般的事务提供隔离性。

4.1 事务模型

  • Eris中的事务有两种类型。核心事务排序层处理独立事务。这些可以被许多应用直接使用,这样做提供了更高的性能。Eris也支持一般事务。
  • 独立事务是在一个参与者集合上原子执行的一次性操作(比如存储过程)。也就是说,事务由一段在子数据片集运行的代码组成。这些存储过程不能与客户端交互,在执行过程中不同的片也不能通信,每个片必须无须协调地独立来到同一个提交点或终止点,比如,通过always commit的方式。这个独立事务的定义来自于Granola,本质上与H-store的作者以“强两阶段”之名提出的定义相同。
  • 像H-store架构一样,在我们的Eris的实现中,底层数据存储在每个参与者上按顺序执行独立事务,而没有并发。这允许Eris避免了锁的开销和锁存型同步的开销,基于锁存器的同步总体上站到传统数据库管理系统设计执行开销的30%。这个架构限制事务执行在单线程中。多线程系统可以每个核操作一个逻辑分区,代价是潜在增长的分布式事务数量。
  • 虽然独立事务模型是限制性的,但是它可以捕捉许多非常常见的事务类别。任何只读事务可以被表示为一个独立事务。Eric的语义使它成为一个一致的快照读取。任何一轮的always commit的读/写事务(比如无条件地递增一组值)是一个独立事务。最后跨片复制的数据(这对经常访问的数据来说很常见)可以通过一个独立事务一致地更新,先前的工作表明许多应用大部分或全部由独立事务组成。比如,TPC-C,一个被设计用来表示事务处理工作负载的工业标准基准,可以整体使用独立事务表示,尽管它很复杂。
  • 一般事务提供了一个标准的交互事务模型。客户端开始一个事务,然后在不同的片上执行一系列的读写操作,每个都依赖于之前操作的结果。最后客户端决定提交还是终止事务。这些可以被用来实现任何事务处理工作量。

5 网络内并发控制

  • 传统的事务处理系统是网络无关的,一切——从排序操作到确保传送到正确的分区——都依赖于应用层协议。最近的工作已经验证了对于副本系统,通过使用网络层的先进的处理能力建立排序原语来加速协调是有可能的。然而,大规模事务处理带来了重大的新挑战。
  • 使用一个专用的排序组件本身不是一个新的idea。排序器已经被用于加速达成共识和加速事务处理系统,并且已经通过RDMA NICs+网络内处理组件的软件方式实现。尤其是NOPaxos展现了有可能建立一个网络层设备,为所有去往复制组的数据包指定全局一致的连续序列号。序列号允许接受者拒绝不按序到达的信息并检测丢失的信息(作为序列号间的空白)。这些反过来使一个优化的的复制协议成为可能,只有当信息丢失或顺序重排时,副本才需要协调。
  • 我们可以对事务处理采取同样的做法吗?在这篇论文中,我们展示了现存的网络层机制(包括NOPaxos的OUM)并不适用于这个目标。它们基于一个去往单一目的组的信息集合建立顺序,然而无需协调的事务执行需要传送到多个目的片的信息的一致的顺序。Eris的贡献是一个网络内并行控制原语,它建立了这样一个次序并且允许接收者检测丢失的信息,以及在可编程交换架构中实现这一原语的策略。
  • 实现这个排序的一部分是使事务参与者集合向网络层明确。传统上,客户端通过分别向每个组发送组播信息(或者,分别向每个组的每个成员单播信息)实现向多个组发送事务。这使得在网络层不可能保证一个有意义的顺序:如果不知道哪些信息对应着同样的逻辑操作,就不能保证不同的事务参与者间有一个一致的顺序。为了解决这个语义空白,我们引入了两个新概念:
    • 组播(groupcast):一个扩展的组播原语,将消息传递给客户端指定的多播组。
    • 多重排序组播(groupcast):一个专门的组播,保证信息以全局一致的顺序传送给所有的组播接收者。这个原语不保证传输可靠性,但是它保证接收者可以检测丢失的信息。
  • 这个设计的一个重要目标是最小化网络所需的逻辑。这简化了实现并增加了系统总体的可靠性。端到端的保证在应用中强化。我们定义的原语,复杂到支持Eris事务处理算法并因此动态地提升系统性能,也简单到可以在各种各样的网络设备上容易并有效地实现。

5.1 为什么要多重排序(multi-sequencing)

  • 我们的工作把OUM模型扩展到了事务处理的多组环境。这要求为多个副本组使用相同的保证原子地排序信息。为了解释这样一个排序机制的需求和实现过程中的挑战,我们研究两个稻草人方案:

    5.1.1 总体全局排序:

  • 考虑使用单一排序器将OUM方法直接应用到整个存储系统。所有的事务通过这个排序器制定一个序列号并发送,然后将事务们转发给系统中所有片的所有副本。因为单一的全局序列号,这个设计能够同时确保顺序(所有的接受者都已相同的顺序处理信息)和丢失检测(接受者会被通知任何丢失的信息)。然而,它要求每个服务器接收设计系统中任何片的每一条信息,明显妨碍系统的性能。
  • 注意采纳这种设计是不可能的,因此信息在保持顺序和丢失检测的情况下只被传送到它涉及到的片中的副本。借助全局序列号,从一个接收者接收信息序列为n,n+2并不能区分出是需要接收n+1(即发送了但没收到)还是n+1并没有发送给它的片(即不需要接收)。

    5.1.2 多重独立排序

  • 或者考虑通过将每个片视为一个独立的OUM组来应用OUM方法。发送到一个片的信息独立于其他片进行排序,然后传送到片的所有副本。不像总体全局排序,使用这种方法信息只传送到需要运行它们的片。此外,一个片上的副本们可以在片内检测丢失的信息。然而排序和检测并不是跨不同的片保证的。如果事务T1和T2都发送给片A和片B,有可能A的排序器先处理T1,后处理T2,而B的排序器先处理T2,后处理T1。也有可能A的排序器处理过的事务在到达B之前丢失,反之亦然。这些异常可能导致违反系统正确性。
  • 为了确保一个正确的一致的排序需要的是一种方法,能够确保传送到多个多播组的信息跨所有参与组原子地被排序。我们下面的设计以两个部分达到了这个目标。Groupcast为应用程序提供了直接发送信息到多个多播组的方式,而且多重排序(multi-sequencing)确保了跨所有目的组的原子排序。这是用一个名为“multi-stamp”的新技术实现。

5.2 Groupcast(新定义的组播)和多重排序组播(groupcast)

  • 我们先定义组播(groupcast)和多重排序组播(groupcast)原语的属性。
  • Groupcast:传统的组播把信息发送到一组预定义好的参与者,比如,IGMP组。分区副本事务处理系统中的通信不适合这种通信模式。事务必须传送到多个副本组,每个 事务涉及到的 片一个。涉及到哪些组取决于事务细节。
  • 取而代之,我们提出了groupcast原语,一个信息被发送到多个多播组。目的集合是特定的。在我们的设计中,这一点通过发送信息给一个专门的IP地址实现。使用SDN(software defined network,软件定义网络)规则,对匹配此目的IP地址的数据包专门处理。位于IP和UDP报头之间的附加报头制定了一个目的组的列表,数据包被发送到每个组的每个成员。
  • 多重排序组播(groupcast):多重排序(multi-sequencing)使用额外的排序保证扩展了组播(groupcast)原语,换句话说,它提供了下列属性:
    • 不可靠性:并不能保证任何信息会被传送到它的接收者。
    • 部分排序:所有多重排序组播(groupcast)的信息的集合是部分排序的,根据“任何有相同目的组的两个信息是可比较的”的限制。此外,如果m1<m2,而且接收器传送m1和m2,那么它将先传送m1,再传送m2。
    • 丢失检测:设R(m)表示信息m的接收者集合,对任何信息m,要么(1)每个R(m)中的接收者传送m或者m的丢失警告(DROP-NOTIFICATION),要么(2)R(m)中的接收者都不发送m或者m的丢失警告(DROP-NOTIFICATION)。
  • 多序列因此可以在有着不同接收者集合的信息间建立一个排序关系。这是和OUM的一个重要的区别,OUM只支持单一多播组内的排序。多重排序(Multi-sequencing)要求任意两个有共同接收者的信息间存在排序关系,比如R(m1)和R(m2)交集不为空。

5.3 多重排序(multi-sequencing)设计

  • 多重排序组播(groupcast)是使用一个中心化的网络层排序器实现的。随时为系统指定一个排序器,当它失效的时候可以被替换掉。依赖于实现(5.4部分),排序器可以是一个终端主机,一个中间机或者一个足够强大的交换机。所有的多重排序组播(groupcast)数据包通过这个排序器路由排序器会修改它们来反映它们在全局序列中的位置。接收者然后确保它们只是按照序列号顺序处理信息。
  • 多重排序组播(groupcast)的挑战在于排序器应该如何修改数据包。正如上面描述的那样,附加单一序列号创建了一个全局队列,使之满足排序的需求,但不能满足丢失检测的需求。为了两种需求都得到满足,我们引入了一个新概念,多印记(multi-stamp)。
  • 在多印记中,为每个目的组包含全套计数器有两个目的。首先,每个接收者都可以确保排序和丢失检测属性。它会检查其组的合适的序列号。如果这个值小于最后一次传送的数据包的值,这表明这是一个乱序的包并丢弃。如果序列号比下一个期望的包的序列号大,这表明这是一个潜在丢弃的数据包,于是应用(如Eris)被通知。第二步,接收者可以根据报的序列号请求一个丢失的数据包,甚至可以从其他组请求丢失的数据包。
  • 容错和“代”:多重排序要求排序器保持状态:每个目的组的最新序列号。当然,排序器可能失效。我们不是驶入保持排序器的状态持久化,这需要排序器的同步复制和复杂的协议,而是让排序器只保持软状态,并将排序器失效暴露给应用程序。
  • 为了解决排序器失效,我们为系统引入一个全局的“代数(epoch number)”,这个数由排序器保存并且和多印记(multi-stamp)一起加入到组播(groupcast)报头。排序器故障转移的责任在SDN控制器,当它怀疑排序器已失效时(比如超时之后),它会选择一个新的排序器,增加epoch number,并且在新的排序器中安装epoch number。注意,按字典上的epoch number为主,multi-stamp为辅的顺序满足部分排序多重排序要求(多重排序中部分事务间是可以排序的,但不是全序关系(total ordering),故称之为部分排序(partial ordering))。
  • 当接收者收到的一个多重排序组播(groupcast)信息有着比它之前收到的更高的epoch number,它会传送一个NEW-EPOCH通知给应用程序(如Eris)。这通知应用程序一些数据包可能丢失了,应用程序负责就 哪些上一代的包 在处理来自下一代的信息之前 成功地被传送 达成一致。正如在OUM中,SDN必须给连续的排序器养个安装严格递增的epoch number。为了容错,我们使用标准方法复制控制器,这是一种常见的做法。或者,只要时钟能足够好地同步已在这种情况下保持单调,一个新的排序器可以根据最新的物理时钟值设置它的epoch number。

5.4 实现和可扩展性

  • 我们的Eris网络层实现包括网络内排序器,一个SDN控制器和一个终端主机库,以便与Eris事务协议等应用程序接口。SDN控制器使用POX实现,管理组播(groupcast)成员,并安装通过排序器路由组播(groupcast)流量的规则。终端主机库提供发送和接收多重排序组播(groupcast)信息的api。尤其是,它监控传入的多标记(multi-stamp)信息上的合适的序列号,如果必要的话,发送DROP-NOTIFICATION警告或NEW-EPOCH通知给应用程序。
  • 排序器本身可以以多种方式实现。我们已经构建了基于软件的模型,运行在使用网络处理器实现的常规的终端主机和中间机上,并评估了它们的性能,如表1所示。然而,性能最高的选项是在交换机中实现多重排序组播(groupcast)功能。这个可以由支持每个数据包处理的可编程网络数据平面架构来实现。
  • 内部交换设计(In-switch designs):为了获得最佳性能,我们设想多重排序和组播(groupcast)直接在网络交换机内实现。可编程网络硬件架构,如Reconfigurable Match Tables [13], Intel FlexPipe [52], Cavium XPliant [65], and Barefoot Tofino [6]提供必要的处理能力。
  • 我们已经使用P4语言实现了多重排序组播(groupcast),支持这些未来体系结构上的编译。完整的P4源代码是可用的。虽然这类产品预计明年将会推出,但是评估这种方法所需的硬件还没有商业可用。然而,我们可以分析我们的设计的资源使用情况来理解网络内并发控制的可行性和潜在可扩展性。
  • 考虑可重构匹配表(RMT),这个结构提供了一个匹配头字段并执行操作的阶段流水线。他也提供有状态的内存,其中一个寄存器可以存储每个分片计数器。如果必要的功能可以在数据包处理流水线中表达,这个设计允许以Tb的线速度处理。那么,可扩展性的障碍就是单一
  • 组播(groupcast)数据包可以被解决的分片数量。两类资源约束管理此限制。第一个是在每个数据包上可以增加多少有状态的计数器。RMT提议指定32个阶段,每个阶段有4-6个寄存器连接的的ALU,支持每个数据包有128-192个目的地。第二,包含用于匹配和操作的字段的包头向量被限制为512B,假定32位片ID和计数器值,在说明IP和UDP包头后,允许116个同时目的地。对于非常大的系统,事务可能涉及超过一百个分片,可能有必要对全局消息(去往所有分片)使用特殊处理。
  • 中间机原型:鉴于有足够能力的交换机还不可用,我们在Cavium Octeon II CN6880网络处理器上实现了一个多印记(multi-stamp)排序器。这个设备包括32个MIPS64核心,提供了对4个10Gb/s的以太网接口的低延迟访问。我们在评估中使用中间机实现(第8部分),虽然它既没有使用大量优化的实现,也没有使用特殊强大的硬件(CN6880发布于2010年),它可以处理美妙处理6.19M个多重排序数据包接近每个链接10Gb/s(7M个包每秒)的最大容量。
  • 终端主机排序:一个替代的设计选项是在一个终端主机上实现排序功能。这为网络基础架构无法修改的环境提供了更方便的部署选项。代价是这样提高了延迟(每个事务接近10微秒),而且系统的吞吐量可能被排序器能力限制。在24核Xeon E5-2680机器上,我们在Linux用户空间的的多重排序器的直接实现可以每秒排序1.61M个请求,足以满足小型部署的需求。低级的优化和新硬件比如RDMA NICs可能会提升这个能力。

6 处理独立事务

  • Eris的独立事务处理层为独立事务提供单拷贝线性化(或者称之为严格线性化)语义。独立事务拥有在每个分片上一次执行它们的属性,保证了严格可序列化的行为,只要它们以一致的顺序执行即可。网络多重排序只是为事务建立这样一个(一致的)顺序。然而,他不保证可靠的传输。因此,为了正确性,Eris必须建立在应用层建立可靠传输的语义,确保副本们就提交哪些事务达成共识,而不是事务的顺序。在正常情况下,Eris能够只消耗客户端到所有副本的单轮往返时间执行独立事务。

6.1 概述

  • Eris使用基于仲裁的协议保证安全——即使当服务器和底层网络行为不同步时——和可用性——即使当任何片中2f+1个副本中的f个副本因崩溃而失效时。Eris客户端使用多重排序组播(groupcast)直接将独立事务发送给涉及到的分片中的副本,并等待来自每一个分片的大多数仲裁的回复。在每个分片上有一个副本被作为“指定的学习者(Designated learner)”。事实上只有这个副本同步地执行事务,其他副本只是记录事务并稍后执行。因此,Eris要求客户端在考虑一个仲裁完成之前,等待一个来自DL的回复(因为它最先执行,所以理应回复比其他得快,由它来发送事务结果,其他副本发送确认信息)。使用DL有两个目的。第一,它允许单向往返执行,不需要猜测和回滚:只有DL执行请求,除非它失败了并且被替换了,否则它涉及该分片提交的每个事务(NOPaxos使用同样的原则)。第二,每个分片上只有DL发送事务结果给客户端,其他的副本只发送确认信息,避免客户端不必要的网络拥塞。
  • Eris必须对复制失败和网络异常有足够强的适应能力。在我们的多重排序抽象中这些异常由DROP-NOTIFICATION(当网络中一个多重排序组播(groupcast)事务发生丢失或乱序时)和NEW-EPOCH(当一个排序器被替代时)组成。在Eris中,DL失效可以完全在片内由一个类似于标准领导者变更协议的协议[40,43,51]处理。然而,DROP-NOTIFICATION和NEW-EPOCH通知需要跨片协调。对于DROP-NOTIFICATION,丢失事务的所有参与的分片必须就是否抛弃信息达成同样的决定。对于NEW-EPOCH通知,分片必须确保它们以一致的状态过渡到新一代。
  • 为了管理这两种失败情况的复杂性,我们引入了一个新元素到Eris架构:失败协调器(FC)。FC是协调副本们从数据包丢失和排序器失效中一致地恢复的服务。FC必须使用标准方法复制以保持可用。但是复制和协调的开销不是问题:Eris仅在罕见的失败情况下调用FC并产生开销,并不是在正常路径上。
  • 由副本维护的状态总结在图4中。状态的两个重要部分是view-num和epoch-num,追踪当前DL和多重排序的epoch。具体而言,view-num值为v的DL是副本号v mod N,N是片上副本的数量。Eris副本和FC使用当前epoch-num标记所有信息,不接收前代的信息(除了跨代阶段)。如果一个副本曾经从后一代接收信息,它必须在继续之前使用FC过渡到新一代。
  • Eris包含5个子协议:正常情况的协议,处理丢失信息的协议,在片内更改DL的协议,更改epoch的协议和定期同步副本状态并允许所有副本安全地执行事务的协议。下面我们列出所有五个。为了简洁,一些细节被忽略。完整的细节在一份相关的技术报告中。在这些协议中,发送但未被正确的回复确认的信息会被重发。尤其是,客户端重复地重试事务直到它们收到正确回复为止。使用维护一张 来自每个客户端的最近的事务组成的 表的 标准技术保证“最多一次”的语义。
Replica属性名 属性含义
replica-id <分片序号,副本序号>
status normal/ViewChange/EpochChange中的一个
view-num 指示片内哪个副本是DL
epoch-num 指示副本当前接收来自哪个排序器的事务
log 按序列排序的独立事务和NO-OP
temp-drops 一组形式为
perm-drops 指示FC已经提交了哪些事务永久丢失
un-drops 指示FC已经提交了哪些事务处理

6.2 正常情况

  • 在正常情况下,客户端通过多重排序层提交独立事务,每个按顺序接收信息的副本只是响应客户端,DL执行事务并包括结果。因此事务仅在单轮往返处理。图5解释了这个过程。
    • 首先,客户端使用多重排序组播(groupcast)将事务发送给所有涉及到的片的所有副本组。
    • 副本接收事务,把它记录进日志并使用
    • 客户端等待来自每个分片的视图一致的仲裁回复。
  • 这里,视图一致的仲裁回复是来自分片中大多数副本的回复,每个回复(包括来自DL的回复)须在匹配txn-index,view-num,epoch-num三个字段。
  • 注意,如果副本的perm-drops(永久丢弃)和temp-drops(临时忽略)字段与事务标识符相匹配,副本将不会执行这个事务。如果事务标识符与与副本的perm-drops字段匹配,副本向日志中事务的位置插入NP-OP并继续,否则副本必须等待。正如下面讨论的那样,事务的标识符与perm-drops或temp-drops字段匹配表示FC认为事务确定或可能失败。

6.3 信息丢失

  • 当因网络异常导致副本错过发送给它所在的片的信息时,副本会从多重排序层收到DROP-NOTIFICATION,这里,原子性要求要么每个参与的分片都学习并执行丢失的事务(见6.2部分),要么都不执行这个事务。这个过程由FC协调,FC与系统中其他节点联系以恢复错过的事务。如果任何节点有缺失的事务的复制品,FC把它发送给其他副本。否则,FC使用一轮“同意”来确保所有的副本同意丢弃事务并继续。
    • 当分片中的副本探测到它错过了某个事务时,它发送
    • FC接收到FIND-TXN并(既定FC还没有发现或丢弃这个发生缺失的事务)向所有分片的所有副本广播
    • 当一个副本收到了TXN-REQUEST,如果它收到了一个匹配txn-id,它回复
    • FC等待来自每个分片的TEMP-DROPPED-TXN组成的仲裁或一个HAS-TXN,以先到者为准。像之前那样,每个仲裁必须是视图一致的并包括视图的DL。如果FC先收到了HAS-TXN并且还没有丢弃这个事务,它会保存这个事务并发送
    • 当副本接收到FC的回应时,如果接收到的是TXN-FOUND,它把事务添加到un-drops字段中,将事务添加到日志并回应客户端。如果它收到的是TXN-DROPPED,它将txn-id加入perm-drops字段中并且如有必要将NO-OP添加到日志。无论哪种情况,副本都可以执行后续事务。
  • 作为优化,在执行这个程序之前,接收到DROP-NOTIFICATION的副本首先与片内其他副本联系,如果它们中有一个接收到了缺失的信息,它(接收到缺失信息的副本)可以(使用缺失的信息)回复它(接收到DROP-NOTIFICATION的副本),允许前者可以正常的处理事务。如果成功了,这允许副本在不涉及FC的情况下恢复丢失的信息。在我们的实验中,这一优化十分重要,因为信息缺失影响到分片的所有副本的情况是很罕见的。

6.4 “指定的学习者”失效(Designated Learner Failture)

  • 因为只有DL执行事务,并且取得进展的能力取决于每个具有DL的分片,Eris有一个view更改协议来替换DL(如果它失效了)。为了确保系统保持正确,新的DL必须学习之前的view提交的所有事务。它也必须学习之前的view中大多数分片发送的TEMP-DROPPED-TXN,而且从FC获得结果之前不要处理这些事务。
  • view的改变是使用一个类似于Viewstamped Replication[45,51]的协议实现的。由于空间限制,我们只提供一个简洁的概述,细节在[42]。
  • 任何怀疑DL已失效的副本停止处理请求并发送一个VIEW-CHANGE信息给下一个view的DL,信息中还包括它当前的状态。新的view中的DL等待大多数发来了这一信息,然后使用他收到的最长的日志和收到的temp-drops, perm-drops, un-drops的并集组装新的view的状态,将新日志中任何与perm-drops字段中的txn-id匹配的事务重写为NP-OP。如果新日志中有任何事务与temp-drops字段中的txn-id匹配但在un-drops字段中没有对应的txn-id,DL必须等待FC来做有关那些txn-id的决定,如果有必要会重试——询问FC并发送必要的HAS-TXN。DL然后发送包含新状态的START-VIEW信息给其他副本,副本们采纳新的状态并过渡到新的view。
  • view的改变(或者换代)可能导致旧的view中的DL执行了最终被丢弃的事务,要求应用层回滚,Eris使用应用程序状态转换处理这种可能性。

6.4 换代

  • Eris也需要处理多重排序层的换代,比如,排序器失效。至于丢弃的信息,FC处理管理这个过程。它确保所有分片中的所有副本以一致的状态在新一代开始,即副本了解在前一代提交的事务,并且没有副本知道事务中的其他参与者不知道的事务。
  • 当一个副本从网络层收到NEW-EPOCH通知时,它停止处理请求并发送EPOCH-CHANGE-REQ信息给FC,然后FC向每个副本请求副本的当前状态(或足够的元数据)以及不继续处理任何属于lower-numbered epoch的操作的承诺。FC等待每个分片中来自简单多数的副本的这种承诺。然后FC 只使用那些FC开始以来的最近一代的那些回应 决定新一代中每个分片的初始状态。每一分片都会收到它收到过的最大view-num和包含FC收到的所有该分片参与的事务(如果某个事务被FC先前丢弃了,就被替换为了NO-OP)的日志。FC将那个状态(也就是FC决定的初始状态)作为START-EPOCH信息发送给所有副本,副本使用新的view-num和日志,执行任何新的事务并清除它们的temp-drops,perm-drops和un-drops。到达一个一致的状态后,副本们能够处理来自新排序器的信息。FC保留并重新发送这些STRAT-EPOCH信息直到每个分片的大多数副本确认新的epoch,协议的完整细节见[42]。

6.6 同步

  • 在独立事务的正常处理(见6.2部分)中,只有每个分片的DL同步地执行独立事务,其他副本仅记录事务。为了避免那些副本的应用程序状态变得太过时,Eris使用了和NOPaxos完全一样的同步协议。每个分片的DL定期与其他副本同步日志,并告知它们执行其中的独立事务是安全的,完整细节见[42]。

6.7 正确性

  • Eris保证在稳定期内线性化的执行独立事务和活跃性。完整的证明和模型检查的TLA+规范在[42],这里我们给出一个主要安全论证的概述。
  • 定义:如果分片发送了一个给事务t的view一致的回复仲裁,事务t在日志槽中的分片上提交。如果一个view一致的仲裁发送tau的TEMP-DROPPED-TXN,一个给txn-id为tau的“丢弃承诺”被提交。如果一个日志中的事务在后续epoch和view中是同一分片中所有副本的日志记录的事务的前缀,我们称这个日志(包括事务和NO-OP)是稳定的。
  • 我们先考虑单一分片的行为:
    • 不变量:如果一个事务在日志槽中被提交,那么在后来的view或epoch中它将在同样的分片的任何副本的日志的该槽中。类似,如果一针对一个txn-id的“丢弃承诺”被提交,那么在同一分片中的开始后续的view的任何副本将在它们的temp-drops字段包含txn-id。
    • 这保证了在任何时间,分片的DL有所有先前提交过的事务和当前的“丢弃承诺”的记录。这一协议通过携带大多数副本的状态的并集开始一个新的view或epoch以确保不变量,任何先前提交的事务或“丢弃承诺”中,至少这些副本中的一个一定参与过。
    • 因为副本以序列顺序添加事务到它们的日志中,一旦DL的日志中的一个事务被提交,它的日志中任何早前的事务也必须以大多数存在,并且将因此存活于后来的view和epoch。此外,不存在于它的日志中的低编号(low epoch or view)事务不能存活到后续的view和epoch:任何日志中的NO-OP对应着“丢弃承诺”提交后FC丢弃的事务。未来的DL将在view更改期间发现这个“丢弃承诺”,而且FC将不会启动该事务(被丢弃的事务)的下一个epoch。因此,DL的日志是稳定的,意味着单个分片的行为和单个正确节点的行为无法区分。
  • 然后,我们考虑多个分片的聚合行为。因为先前的不变性和事实上FC在一个来自每个片的“丢弃承诺”后只丢弃一个事务,下面的不变量保持(完整证明见[42]):
    • 不变量:如果事务t被分片s提交,那么对于其他所有参与的分片s':如果s'已经提交了t'>t,那么s'就已经提交了t。这里,>是由多重排序层指定的部分顺序。
    • 也就是说,事务以一种遵照多重排序产生的顺序的方式原子地在分片间自动执行。既然多重排序保证了一个任何潜在冲突的事务都可比较的部分顺序,那么任何关于这个顺序的执行豆浆没有冲突环,因此可串行化。此外,该顺序还考虑了连续排序器接收到的事务的实时顺序,使Eris的事务执行可线性化。

7 建立一般事务

  • 许多(并非全部)重要的操作可以表达为独立事务。一个例外是依赖于存储在另外一个分片上的数据的有条件的更新。比如,只有当资金充足时才能将资金从一个账户移动到另一个账户的银行交易。在很多情况下,这些操作可以通过仔细的分区(甚至是状态重复)得到避免。比如,通过将两个账户放在同一个分片上。然而,为了支持所有工作负载我们扩展了Eris来支持可以有跨片依赖的一般事务。
  • Eris通过将一般事务分解成多个独立事务来运行一般事务。因此一般事务的执行运行于独立事务的执行的上层。这简化了设计:一般事务的实现事实上可以依赖于独立事务处理层是正确的并提供线性化执行。也就是说,可以假设一个正确的单机可以顺序地处理独立事务。
  • 在这些更一般的事务存在的情况下支持强隔离性需要额外的并发控制机制。Eris使用严格的两阶段锁。分片为每个数据对象维护读写锁,只有在存在未完成的一般事务时才用。在锁定期间,任何影响相应数据对象的独立事务或一般事务等待,直到锁被释放为止。

7.1 一般事务协议

  • 我们首先考虑完整的读/写集先验已知一般事务。这些事务以两阶段提交。在第一阶段客户端发送一个初步事务,执行读操作并获得所有的读写锁。在第二阶段,客户端发送一个结论事务,要么提交(commit)一般事务,要么终止(abort)一般事务。提交(commit)安装事务所做的修改。在两种情况下,事务的锁都被释放。
  • Eris也能够执行读写集合一开始不知道的事务,即他们是依赖于状态的。为此,Eris像Calvin一样应用侦查查询(reconnaissance queries)。也就是说,在发送一般事务的初步组件之前,客户端发送 单信息的,非事务性的读取 来决定完整的读写集。初步事务检查侦查搜索返回的值是否有效。如果任何有所改变,一般事务将被终止。否则,结论事务可以以上述方式进行。

7.2 处理客户端失败

  • Eris客户端是他们自己的事务管理者。因为客户端可能失败,Eris必须能够终止一个由失败的客户端开始的一般事务来允许系统维持进度。一般来讲,解决这个问题是复杂协作终止协议的范畴。然而因为Eris建立在独立事务的原子执行的基础上,它允许一个简单的解决办法。当一个Eris的副本因客户端长时间上锁也不发送结论commit还是abort而怀疑客户端已经失败时,副本可以通过将abort指令通过独立事务层排序并作为独立事务发送来简单地单方面终止一般事务。这确保了即使客户端同时尝试发送commit,所有参与的分片也能作出彼此相同的commit/abort决定。

7.3 讨论

  • Eris在核心的独立事务原语上建立了一般事务层,模块化简化了设计,尤其是处理客户端失败的设计。这一分层设计非常实际,因为Eris能够在单次往返中提交独立事务。在之前的系统比如Granola中这样的方法可能不实用,因为在之前的系统中独立事务仍然设计显著的协调开销。因此,Granola为独立事务和一般事务使用独立的专门的协议,两者间的转换有着复杂的流程和巨大的开销。
  • 此外,Eris网络内并发控制的使用阻止了死锁,缓解了一大类并发引起的abort和复杂的死锁检测机制:在由线性化层执行的单个原子步骤意味着等待图中不可能成环。结合独立事务处理协议的吞吐量和延迟优势,这允许Eris更好地应对高争用。

猜你喜欢

转载自www.cnblogs.com/fkd15061188/p/8970047.html