超越分布式事务

该文是Salesforce的软件架构师Pat Helland于2016年12月发表的针对其在2007年CIDR(创新数据库研究会议)上首次发表的同名文章的更新和缩写版本。他曾经发表“不变性改变一切”。

业界谈到分布式事务通常指两段提交2PC事务(Spring/JEE中JTA等)或者Paxos与Raft,这些事务都有其明显缺点和局限性,Pat Helland在本文讨论的是另外一种基于本地事务情况下的事务机制,它是基于实体和活动(Activity)的概念,其实类似DDD聚合根和领域事件的概念,这种工作流类型事务虽然需要程序员介入,依靠消息系统实现,但可以实现接近无限扩展的大型系统。Pat文中提出了重要的观点:“如果你不能使用分布式事务,那么你就只能使用工作流。”

以下是Pat Helland的Life Beyond Distributed Transactions这篇著名文章的翻译:

事务是非常强大的机制,我在它上面花费了我40年职业生涯中的大部分时间。1982年,我首先在Tandem NonStop系统上实现事务机制。这个系统有一个每年平均故障时间,包括跨地理位置的分布式的两阶段提交,为强一致性事务提供了极好的可用性。

新的创新,其中包括谷歌的Spanner,提供强一致性环境,在非常大的规模基础上提供了卓越的可用性。构建一个以支持高度可用的分布式事务应用程序是卓越的创新和重大的挑战。不幸的是,这个产品不能被广泛使用。

在大多数分布式事务系统中,单个节点的失败会导致事务提交失败。这反过来导致应用程序被卡住。在这样的系统中,系统越大,系统越可能停机。当一个飞机需要所有发动机都必须工作时才能飞行,增加发动机只会降低飞机的可用性。如果没有特殊的机制来容忍中断,那么在数千个节点上运行分布式事务系统是不切实际的。当应用程序开发人员使用非高度可用的分布式事务构建系统时,解决方案很脆弱,必须丢弃。自然选择就是放弃它(放弃两段提交事务)...

相反,应用程序在没有提供事务性保证前提下仍能满足其业务需求。

本文探讨并在拒绝传统分布式事务的世界中实施大规模任务关键型应用程序时使用的一些实用方法。包括管理细粒度的应用程序数据,随着应用程序的增长,这些应用程序数据可能会随着时间的推移重新分区。包括一些设计模式用来支持在这些可重新分区的数据之间发送消息。

这里的目标是减少人们手工制作大型可扩展应用程序所面临的挑战。另外,通过观察这些设计模式,业界可以通过创建平台来更轻松地开发可扩展的应用程序。最后,尽管本文的目标是同类性质可扩展的应用程序,但这些技术对于支持可伸缩的异构应用程序(如支持移动设备)也非常有用。

目标

本文重点介绍如何在只有本地数据库或本地事务系统时构建成功的可伸缩企业应用程序。可用性不是重点,主要瞄准规模和正确性。

1.可扩展应用程序的讨论

大多数可扩展应用程序的设计者都了解业务需求。问题在于事务和可扩展系统交互的问题,只有概念和抽象却没有名称,也没有清晰的理解。本文的一个目标是启动一个讨论,以提高对这些概念的认识,为可扩展的程序带来一套通用的术语和一致的方法。

2.考虑应用程序的几乎无限缩放

文章提出了一个关于几乎无限缩放的影响的非正式的思考实验。也就是说可以允许客户,可购买商品实体,订单,发货,生病患者,纳税人,银行账户以及所有其他业务概念的数量随着时间不断显着增长,这就是无限扩展的定义。通常情况下,每件事情都不会太大; 只是数量越来越多。如果CPU,DRAM,存储或其他资源首先饱和,那并不重要。在某种程度上,需求的增加会导致在一台机器上运行程序需要在大量的机器上运行。这个思想实验使我们考虑数十或数十万台机器。

几乎无限的缩放是一种松散的,不精确的,刻意的无定形的方式,它激发人们需要非常清楚何时何地可以知道某个机器上装有什么东西,以及如果无法确保它适合一台机器,该怎么办。此外,您想要与数据和计算负载几乎线性地缩放。当然,用一个大的日志以N-log-N的速度进行缩放将会很好。

3.描述可扩展应用程序的一些常见模式

几乎无限的扩展对业务逻辑有什么影响?我断言缩放意味着在编写程序时使用称为实体的新抽象。一个实体一次只能在一台机器上运行,而应用程序一次只能操纵一个实体。几乎无限缩放的结果是,这种程序化的抽象必须暴露给业务逻辑的开发者。

通过命名和讨论这个尚未命名的概念,我们也许可以商定一致的方案方法,并对构建可扩展系统所涉及的问题有一个一致的理解。

此外,实体的使用对用于连接它们的消息传递模式有影响。这导致了状态机的创建,这些状态机不一致性包括无辜的应用程序开发人员试图为业务问题构建可伸缩解决方案时所产生的消息传递不一致。

4.一些假设

考虑以下三个假设,假设这些基于经验是真实的,这些假设是没有道理的。。

(1)应用层面和规模不可知论

我们首先假设每个可伸缩应用程序至少有两层:
代码高层 ---> 感知规模的API ----> 代码下层

这些层在缩放的感知上有所不同。他们可能有其他的区别,但这些与这个讨论无关。

该应用程序的下层理解是:更多的计算机被添加扩大系统规模。除了其他工作之外,它还管理上层代码到物理机器及其位置的映射。下层是可以识别的,因为它理解这个映射。我认为下层为上层提供了一个与尺度无关的编程抽象。有很多与规模无关的编程抽象的例子,包括MapReduce。

使用这种与规模无关的编程抽象,编写应用程序代码的上层而不用担心缩放问题。通过坚持与规模无关的编程抽象,您可以编写应用程序代码,而不必担心在部署日益增加的负载时发生的变化。

随着时间的推移,这些应用程序的底层可能会演变成一个新的平台或中间件,从而简化了与规模无关的API的创建。

(2)事务范围

在分布式系统上提供高度一致的事务的概念方面已经做了许多学术工作。这包括2PC(两阶段提交),1个 Paxos和最近的Raft。 经典2PC在一台机器发生故障时会阻塞,除非事务协调员和参与者本身是容错的,比如Tandem NonStop系统。Paxos和Raft不会因为节点故障而阻塞,但会像Tandem的系统一样进行额外的协调工作。

这些算法可以被描述为在分布式系统上提供强一致的事务。他们的目标是允许任意原子更新传播到许多机器上的数据。更新存在于跨越多台机器的单个事务范围中。

不幸的是,在许多情况下,这不是应用程序开发人员的选择。应用程序可能需要跨越信任边界,不同平台以及不同的运营和部署区域。当你对分布式事务“说不”时会发生什么?

即使在今天,在这篇论文写了10年之后,真正的系统开发人员也很少尝试在超过几台计算机上实现强大的一致性事务。相反,他们承担多个独立的事务范围。每台电脑都是一个独立的范围,内部有本地事务。

(3)大多数应用程序使用至少一次消息

如果你是一个短暂的Unix风格的进程,TCP / IP是非常棒的,但考虑一个应用程序开发人员面临的困境,他的工作是处理消息并修改数据库中相应的一些持久数据。该消息已被消费但尚未确认。只有数据库被更新后消息才被确认。如果发生故障,则重新启动并重新处理消息。

这个困境来源于这样的事实:除了通过应用动作之外,消息传递不是直接耦合到持久数据的更新。虽然可以将消息的消费与持久数据的更新结合起来,但这并不常见。两者紧密耦合的丧失会导致失败窗口的消息被传递多次。为了防止丢消息,消息传递至少一次(banq注:Apache kafka在1.0之后提供了Stream的紧耦合更新,不同数据源需要不同Stream插件)。

这种行为的后果是应用程序必须容忍消息重试和无序传递。

一些意见分析

撰写评论文章的好处是可以表达狂野的意见。这里有一些这样的文章。

(1)可扩展的应用程序使用唯一标识的实体

该意见认为,每个应用程序的上层代码必须处理一个称为实体的数据集合。单个实体的大小没有限制,除了必须在单个事务范围内(即一台机器)生存。

每个实体都有一个唯一的标识符或键,实体键可以是任何形式,它必须唯一标识一个实体及其包含的数据。

对实体的陈述没有任何限制。它可能被表示为SQL记录,XML,JSON,文件或其他任何东西。一种可能的表示形式是SQL记录的集合,可能跨越许多表,其主键的实体键作为其前缀。

实体表示不相交的数据集。每个数据只在一个实体中。

一个应用程序由许多实体组成。例如,订单处理应用程序封装了许多订单,每个订单都由一个唯一的订单ID标识。要成为可扩展的订单处理应用程序,来自一个订单的数据必须与其他订单的数据不相交(banq注:主键ID通过全局分布式主键生成器唯一分配)。

(2)原子事务不能跨越实体

每台计算机被假定为一个单独的事务范围。本文稍后介绍原子事务不能跨实体的论点。程序员必须始终确保每个事务中数据包含在的单个实体中(banq注:有些类似DDD的聚合根实体概念)。

从程序员的角度来看,唯一标识的实体就是事务范围。这个概念对用于设计可缩放的应用程序具有强大的影响。需要探讨的一个含义是,在设计几乎无限的缩放比例时,可能不能保持事务一致。

(3)消息被分配给实体

大多数消息传递系统不考虑数据的分区,而是针对无状态进程使用的队列。标准做法是在消息中包含一些数据,通知无状态应用程序代码在哪里获取所需数据。这是实体的关键。实体的数据由应用程序从某个数据库或其他持久存储中获取。

一些有趣的趋势正在发生。首先,这组实体的大小正在变得比适合一台计算机的大。每个单独的实体通常适合一台计算机,但是它们的集合不一样。越来越多的无状态应用程序正在路由以基于某种分区方案来获取实体。

其次,抓取和分区方案正被分成应用程序的下层。这是故意与负责业务逻辑的上层隔离的。

该模式通过使用实体键进行路由来有效地定位实体。无状态的Unix风格的进程和应用程序的低层都只是为业务逻辑提供的与规模无关的API的实现的一部分。上层规模不可知的业务逻辑简单地通过唯一标示将消息定位到某个实体。

(4)实体管理每个合作伙伴状态(活动)

与规模无关的消息能够在实体之间有效的传递。发送实体以其持久状态显示,并由其实体键标识。它发送一个消息给另一个实体,并通过它的实体唯一键来识别它。实体接收者由与规模无关的上层业务逻辑和表示其状态的持久数据组成。这是通过它的实体键来标识的。

回想一下这样的假设:消息至少被传递一次。接受者实体可能有些困扰,因为必须忽略的冗余重复消息。在实践中,消息分为两类:影响实体状态的消息和不影响状态的消息。不影响实体状态的消息很容易 - 它们是幂等的。改变状态的消息需要更多的关注(banq注:改变状态的消息一般称为事件)。

为了确保幂等性(即保证重试消息的处理是无害的),接收者实体通常被设计为记住消息已经被处理。成功处理后,重复的消息通常会生成新的另一个响应以表示该消息重复。

接收到的消息的实体创建了基于伙伴为基础的状态。每个状态只能存在每个实体中,
术语“活动activity ”这个词语适用于管理双方关系,会对关系中每个伙伴状态有影响的消息。每个活动都是精确地生活在一个实体中,一个实体有一个为其伙伴实体准备的活动。(banq注:这个活动是一种业务活动,业务工作流)

除了管理消息传递方式之外,还可以使用活动来管理松散耦合的协议。在一个原子事务不可能的世界里,尝试性的操作被用来进行谈判以获得共同的结果。这些都是在实体之间执行的,由活动管理。

建立工作流程以达成一致是充满挑战的,这些挑战在其他地方都有详细记录。本文并没有断言活动解决了这些挑战,而是为针对解决这些挑战而存储的状态奠定了基础。

几乎无限的缩放规模要求竟然导致了出人意料的细粒度的工作流风格的解决方案。

参与者都是实体,每个实体使用有关其他实体的特定知识来管理其工作流程。在实体内维护的双方知识被称为活动。

活动的例子有时是微妙的。订单应用程序将消息发送到发货应用程序。它包括货运ID和发送订单ID。消息类型可用于激发运送应用程序中的状态更改,以记录指定的订单已准备好出货。通常情况下,实施者不会设计重试,直到出现错误。应用程序设计师偶尔也会考虑活动的计划。

实体

本节更深入地研究实体的性质。

1.不相交的事务范围

每个实体都被定义为一个拥有一个唯一标识的数据集合,这个标识只存在于一个事务范围内。原子交易可能总是在一个实体内完成。(banq注:类似DDD中的聚合根实体)

2.唯一键的实体

应用程序上层的代码自然是围绕具有唯一键的数据集合来设计的。客户ID,社会安全号码,产品SKU和其他唯一标识符可以在应用程序中看到。它们被用作查找应用程序数据的键。交易原子性的保证只能在由唯一标识的实体中。

3.重新分配和实体

之前提到的假设之一是新兴的上层是规模不可知的,下层决定了随着规模的变化,部署如何演变。随着部署的演变,特定实体的位置可能会发生变化。应用程序的上层不能对实体的位置做出假设,因为这不会成比例。

实体可使用Hash散列或基于键范围分配策略进行跨区分配。

比如key是ABC ABZ DEF FAW在A区服务器 FXQ GHI JKL KZU LMN在B区服务器,PAZ PJH TVA UTV ZZZ在C区服务器。

4.原子事务和实体

在可扩展的系统中,您不能假设在这些不同实体之间实现更新修改的事务。每个实体都有一个唯一的标识,每个实体很容易放入一个事务范围。回想一下这样的前提:几乎无限的缩放会导致实体的数量不可避免地增加,但是个体的大小仍然足够小以适应事务范围(即一台计算机)。

你怎么知道两个独立的实体保证在同一个事务范围内,因而可以自行在自己事务中原子更新?只有当全局只有一个唯一的key主键时,你能知道它真的是只有一个实体!

如果Hash散列用于实体键的分区,则不知道具有不同键的两个实体何时落在同一个服务器内。如果键范围分区用于实体键,大部分时间相邻键值会驻留在同一台机器上。偶尔会遇到不幸,而你的邻居会在另一台机器上。

一个简单的测试案例,通过键范围分区中的邻居之间的计算原子性通常会成功。后来,当重新部署将实体移动到机器上时,潜在的错误就出现了。更新不再是原子的。你永远不能指望在同一个地方居住的不同的实体键值(不要期望实体固定在哪个服务器内被创建)。

简而言之,应用程序的底层将确保每个实体键(及其实体)驻留在一台机器上。不同的实体可能在任何地方。(banq注:DDD中仓储工厂模式就是负责一个个聚合根实体的创建)

规模不可知的编程抽象必须具有将实体作为原子性边界的概念。理解实体,使用实体关键字以及明确承诺实体之间缺乏原子性是规模无关的编程必不可少的知识。

大规模的应用程序悄悄地在今天的行业中已经做到这一点; 只是没有一个实体的概念的名称。从上层应用程序的角度来看,它必须假定实体是代表一个事务的范围。假设部署变化时会产生更多的中断。

5.考虑候选索引

我们习惯于使用多个键或索引来处理数据。例如,有时一个客户被社会安全号码(有时是信用卡号码,有时是街道地址)引用。假设进行大量的缩放,这些索引不能驻留在同一台机器上,也不能驻留在单个大型集群中。无法知道关于单个客户的所有数据都驻留在单个事务范围内。虽然实体本身驻留在单个事务范围内。所面临的挑战是,用于创建这些候选索引的信息副本必须被假定为与实体自身信息驻留在不同的事务范围中。

首先考虑确保候选索引驻留在相同的事务范围内。当几乎无限的缩放开始时,实体的集合被涂抹在巨大数量的机器之中。主索引和候选索引信息必须位于相同的事务范围内。确保这一点的唯一方法是使用主索引找到候选索引。这将把你带到相同的事务范围。如果您在没有主索引的情况下启动并且必须搜索所有事务范围,则每个候选索引查找都必须在几乎无限数量的范围内检查,因为它会使用候选键进行匹配查找。这将最终成为站不住脚的。

唯一合乎逻辑的选择是做两步查找。首先,查找候选键。其次,使用实体键访问实体。这与关系数据库非常相似,因为它就是使用两个步骤通过候选键访问记录的。但是几乎无限缩放的前提意味着两个指标(主要和候选)不能被认为是在相同的事务范围内。


过去自动管理的候选索引现在必须由应用程序手动管理。通过异步消息传递的工作流风格更新是剩下的唯一选择。当您从候选索引读取数据时,您必须了解它可能与实体本身不同步。候选索引现在更难。这是一个巨大系统的残酷世界中的生活事实。(banq注:索引是为查询服务,可使用读写分离)

6. 在实体之间传递的消息

本节考虑使用消息来连接独立的实体。它检查命名,事务和消息,消息传递语义以及重新分区实体的影响。

(1)消息在实体之间进行通信

如果您无法在同一事务中的两个实体之间更新数据,则需要一种机制来更新不同事务中的数据。实体之间的连接是通过消息。

(2)异步发送事务

由于消息在实体之间传递,与发送消息的决定相关的数据在一个实体中,并且消息的目的地在另一个实体中。根据一个实体的定义,这些实体不能被原子更新。消息不能通过这些不同的实体自动发送和接收。

如果应用程序开发人员在处理事务时发送消息,发送消息,然后事务中止,那将会非常复杂。这意味着你没有任何措施记住发生的事情,但这些事情确实发生了。出于这个原因,消息的事务排队是必要的。

如果消息在发送事务提交之后才能在目的地被看到,则消息对于发送事务是异步的。每个实体在一个事务中会进入一个新状态。消息是来自一次事务的刺激,并到达另外一个实体引起新的事务。

(3)命名消息的目的地

考虑应用程序的规模无关部分的编程,因为一个实体想要发送消息给另一个实体。规模不可知的代码不知道目标实体的位置。实体主键就是关键。

实体键能够实现应用程序的规模感知,将实体主键与实体的具体位置相关联。

(4)重新分区和消息传递

当应用程序与规模无关的部分发送消息时,较低级别的扩展感知部分搜索目标并且传递消息至少一次。

随着系统规模的扩大,实体在移动。这通常被称为重新分区。实体的位置以及因此消息的目的地可能在不断变化。有时消息会追溯到旧的位置,只是为了找出讨厌的实体已经发送到别处什么地方聊。现在,消息将不得不这么做。

随着实体的移动,发送者和目的地之间的先进先出队列的清晰度偶尔中断。消息被重复。稍后的消息在较早的消息之前到达 生活变得更加混乱。

由于这些原因,与规模无关的应用程序正在不断演进到以支持所有应用程序对可见消息的幂等处理。这也意味着在消息传递中可进行重新排序。

活动:应对杂乱的消息

本节讨论如何应对消息重试和重新排序的挑战。它将活动的概念介绍为管理与合作伙伴实体的关系所需的本地信息。

1.重试和幂等性

由于任何消息都可能被多次传递,因此应用程序需要一个处理重复消息的规则。尽管可以为消除重复消息构建低级支持,但在几乎无限的扩展环境中,低级支持需要了解实体。当实体由于重新分区而移动时,必须知道哪些消息已经传送给实体。在实践中,这种低级管理的知识很少发生; 消息可能会被传递多次。

通常,应用程序的规模不可知(更高级别)部分必须实现机制来确保传入消息是幂等的。这对问题的本质并不重要。重复的消除当然可以构建到应用程序的可感知尺度的部分。这还不可用。因此,这是很难做到的。

2.什么是幂等

如果后续的处理执行没有对实体进行实质性改变,则消息的处理是幂等的。这是一个无定形的定义。

如果消息没有改变被调用的实体,但只是读取信息,则其处理是幂等的。即使写入了描述读取的日志记录,情况也是如此。日志记录对实体的行为没有实质性的影响。

3.自然的幂等性

为了实现相同的目标,消息不会造成实质性的副作用是至关重要的。一些消息任何时候处理都不会引起实质性的工作。这些自然是幂等的。

只从一个实体读取一些数据的消息自然是幂等的。如果消息的处理确实改变了实体,但没有实质性的改变呢?那些也自然是幂等的。

现在变得更加困难。一些信息所隐含的工作实际上引起了实质性的变化。这些消息不是自然而然的幂等。应用程序必须包含机制来确保这些也是幂等的。这意味着要以某种方式记住信息已经被处理,以便后续的尝试不会有实质性的改变。

下一节将考虑处理不自然是幂等的消息。

4.将消息记为状态

为了确保对那些不自然是幂等的消息进行幂等处理,实体必须记住它们已被处理。这个知识是状态。状态在消息处理时变化。(banq注:此时消息可称为事件)

另外,如果需要回复,则必须返回相同的回复。毕竟,你不知道原始发件人是否收到回复。

5.管理每个合作伙伴的状态

为了跟踪收到的关系和消息,规模不可知的应用程序中的每个实体必须以某种方式记住关于其合作伙伴的状态信息。它必须以伙伴为基础来捕捉这个状态。让我们把这个状态命名为一个活动(banq注:类似事件)。参见图6.每个实体如果与许多其他实体交互,可能会有许多活动。活动跟踪与每个合作伙伴的交互。

每个实体都由一组活动组成,也许还有一些跨越活动的其他数据。

考虑处理由许多购买物品组成的订单。为每个单独项目的运输保留库存将是一个单独的活动。仓库将管理每个物料的订单和实体。事务不能跨越这些实体。

在订单实体内,每个库存项目将分开管理。消息传递协议必须分开管理。订单实体中包含的每个库存项目数据代表一个活动(订单条目代表一个事件活动,类似账本,每一行记录发生的进出金额)。虽然它不是这样命名的,但这种模式经常存在于大型应用程序中。

在一个几乎无限扩展的应用程序中,您需要非常清楚关系。你不能只是做一个查询来弄清楚什么是相关的。一切都必须通过双方关系网络正式地结合在一起。编织是用实体键完成的。因为合作伙伴离我们有一段距离,所以当合作伙伴到达时,你必须正式把你对合作伙伴的状态理解为新的知识。已知的关于远方的本地信息被称为活动。(banq注:通过DDD有界上下文定位聚合根实体,聚合根实体之间通过事件完成)


6.通过活动确保最多一次接受

处理不自然幂等的消息需要确保每个消息最多处理一次(即消息的实质性影响最多只发生一次)。要做到这一点,必须记住消息的一些独特特征,以确保消息不会被多次处理。

实体必须永久地记住从消息接受切换相应状态的处理不会产生实质性影响。

通常,一个实体使用其活动来逐个与其合作伙伴地实施这种状态管理。这是非常重要的,因为有时一个实体支持许多不同的合作伙伴,每个合作伙伴都会传递一种与这种关系相关的消息模式 ,包含每个合作伙伴的状态集合使其成为可能。

活动:应对没有原子性

本节讨论如何在无需分布式事务的情况下进行大幅扩展系统的决策。

管理分布式协议是艰苦的工作。在几乎无限可扩展的环境中,不确定性的表示必须以围绕每个伙伴关系的细粒度的方式进行。这些数据是使用活动的概念在实体内部管理的。

1.远处的不确定性

分布式事务的缺乏意味着在试图跨越不同实体做出决策时接受不确定性。不可避免地,分布式系统的决策需要一段时间来接受不确定性。当分布式事务可以被使用时,这种不确定性就体现在数据锁上,并由事务管理器进行管理。

在不能依靠分布式事务的系统中,不确定性的管理必须在业务逻辑中实现。结果的不确定性是在业务语义中而不是在记录锁中。这简直就是一种工作流程。这不是魔术。

如果你不能使用分布式事务,那么你就只能使用工作流。

实体和消息的概念提出,导致这样一个结论:规模不可知的应用程序必须使用工作流程来管理不确定性。这需要跨多个实体达成协议。

考虑一下企业间常见的互动风格(办公流程)。企业之间的合同包括时间承诺,取消条款,保留的资源,等等。业务功能的行为本身就包含了不确定性。虽然比使用分布式事务更复杂,但真实世界就是这么工作的...(banq注:比如一个企业每个月定期向银行汇款,这个过程是一个事务过程,通常通过流程规划实现,如果第一次失败了,从银行接受失败数据,然后重新再次报盘发送,这种重试过程就是确保数据精确送达银行的事务机制)

这只是工作流程的一个论点。

2.活动与不确定性的管理

实体有时在与其他实体交互时接受不确定性。这种不确定性必须在逐个合作伙伴的基础上进行管理,并且可以被视为在合作伙伴的活动状态中具体化。

很多时候,不确定性是由关系来表示的。有必要跟踪伙伴。活动跟踪每个合作伙伴进入一个新的状态。

如果订单系统为某个商品保留了仓库的库存,则仓库那边系统在不知道这个商品已经被保留库存情况下会为其重新分配库存。这是可接受的不确定性。之后,仓库会发现自己再次预留库存是否有必要。这解决了不确定性。

仓库库存管理员必须保留每个订单的关系数据。由于它连接物品和订单,这些将按订单条目组织。每个条目都保存有关该条目未完成订单的信息。条目内的每项活动(每个订单一项)管理订单的不确定性。

3.执行暂时的业务操作

为了达成跨实体的协议,一个实体要求另一个实体接受不确定性。一个实体发送另一个可能稍后取消的请求。这被称为暂时性操作。在这一步的最后,一个实体同意遵守另一个的意愿。(banq注:暂时性操作类似信用卡的预扣款请求,是一种尝试性的、实验性的、试探性的请求)。

4.暂时性操作,确认和取消

暂时性操作的基本要素是有权利能取消。如果请求实体决定不跟随当前业务前进,则发出取消操作(banq注:如果被预扣款的信用卡没有信用额度,则拒绝)。如果决定继续,它会发出确认操作。

当一个实体同意进行暂时操作时,同意让另一个实体决定其结果,它实际就接受不确定性,增加了混乱。随着确认和取消的到来,不确定性下降,减少了混乱。随着老问题的解决和新的问题的到来,随着生活的不断增加和不断减少,这是正常的。

再次说明,这是一种工作流程,但它是以实体作为参与者的细粒度的工作流程。

5.不确定性和几乎无限缩放

不确定性管理通常围绕双方协议进行。可能有多个双方协议。这些作为链接和活动来跟踪远方的已知状态,将它们组织在一起,形成一个细密的双方协议网络,使用实体键作为链接和活动。

考虑购房和与托管公司的关系(banq注:中国是二手房资金托管中心)。买方与托管公司签订委托协议,卖方,抵押公司以及参与交易的所有其他各方也同意。

当你去签署文件买房子,你不知道交易的结果。但是你得接受,直到托管关闭,你一直是不确定的。控制决策的唯一方是托管公司。

这是一个集中的双方关系,用来让大量的当事方在不使用分布式事务的情况下达成一致。

在考虑近乎无限的规模时,考虑双方关系是很有趣的。通过建立双方的临时/取消/确认(就像传统的工作流程一样),你可以看到实现分布式协议的基础。正如在第三方托管公司,许多实体可能通过组合方式参与协议的完成。

因为这种关系是双方的关系,一个简单的活动概念就像是“我记得那个伙伴的东西”,成为管理庞大系统的基础 - 即使数据存储在实体中,而且你不知道实体在哪里。你必须假设它很远。不过,它可以按照与规模无关的方式进行编程。

现实世界中很多应用程序喜欢全局性事务这样的奢侈品。不幸的是,当系统出现故障时,我们大多数人并没有意识到是我们自己引入脆弱性。

相反,试验性工作的不确定性管理交给了应用程序的开发者手中。必须将其作为预留库存、针对信用额度的预扣款以及其他应用程序特定的业务概念来处理。


结论

像往常一样,计算机行业正在变化中。

今天,新的设计压力正在被强加到只想解决业务问题的程序员身上。现实正在把他们带入一个几乎无限缩放的世界,并迫使他们陷入与现实生活无关的设计问题。今天是这样,2007年这篇文章首次出版时就是这样。

不幸的是,努力解决电子商务、供应链管理、财务和医疗应用程序等业务目标的程序员越来越需要思考如何在没有分布式事务的情况下进行扩展。大多数开发人员根本无法访问提供可扩展分布式事务的强大系统。

我们正处在一个可以看到构建这些应用程序的模式的时刻,但是还没有人能够一致地应用这些模式。本文认为,这些新生的模式可以更为一致地应用于几乎无限缩放设计的应用程序开发中。

本文介绍并命名了大量应用中出现的几种模式:

• 实体,是已命名(带有键key)数据的集合,可以在实体内原子更新,但从不在实体间自动更新。

• 活动,由用于管理与单个合作伙伴实体之间的消息关系的实体内状态集合组成。

在实体内的活动中达成决策的工作流程。当看到几乎无限的缩放规模时,你会意识到这是由工作流程的细粒度特性决定的。

许多应用程序已经悄悄地使用实体和活动这样概念设计,只不过名称不同而已。实体和活动这些名词可能根本不是正式的,也没有被一贯使用。如果使用不一致,则会发现错误并最终进行修补。通过讨论并持续使用这些模式,可以建立更好的大规模应用程序,并且作为一个行业,我们可以更接近于构建解决方案,使业务逻辑程序员能够专注于业务问题而不是规模问题。

原文:
Life Beyond Distributed Transactions - ACM Queue
相关案例:
两个领域事件驱动的开源项目介绍


[该贴被admin于2018-01-11 17:47修改过]