论文地址:分布式事务之外的存在状态:一个叛逆者的观点——帕特·海兰,2007

    它需要真正的技巧,把一些东西剥离回它的本质,并以这样一种方式清楚地解释它,使其后果变得明显。在我看来,帕特·海兰在本文中出色地完成了这一点,并帮助读者更深入地思考了基本问题。

    我真希望本周早些时候能在reactconf上听到帕特的讲话。目前,我们中的那些人不能在那里将不得不等待幻灯片成为可用。

    本文探讨并列举了在拒绝分布式事务的世界中实现大规模任务关键型应用程序的一些实用方法。

    为什么拒绝?
    (a) 人们在实践中并没有使用分布式事务来构建大规模系统,而且
    (b)如果他们真的试图这样做的话,“项目的创始人是因为性能成本和脆弱性使他们不切实际。”

    论文中的所有内容都来自一个简单的思维实验:

    ……假设客户、可购买实体、订单、发货、医疗保健患者、纳税人、银行账户以及应用程序操纵的所有其他业务概念的数量随着时间的推移而显著增加(几乎无限)。通常情况下,个体的东西不会变得很大;我们只是得到越来越多的东西。

    现在你做什么?很明显,你不能把它放在一台机器上,所以你不得不把以前运行在一台或少量机器上的东西分散到更多的机器上。

    缩放意味着在编写程序时使用称为“实体”的新抽象。一个实体一次只能存在于一台计算机上,应用程序一次只能操作一个实体。几乎无限扩展的一个结果是,这个抽象必须公开给业务逻辑的开发人员。

    当你通读这篇文章时,你会发现Helland所指的“实体”并不仅仅是一个“持久对象”,因为应用程序开发人员自然会把这个词等同起来,而是Eric Evans所说的“聚合实体”

    每个实体都有唯一的标识符或键。实体表示不相交的数据集。每个数据都位于一个实体中。一个实体的数据永远不会与另一个实体的数据重叠。

    实体是原子性的边界
    实体存在于可序列化的单一范围内,因此我们总是保证能够在单个实体的范围内执行原子事务。此外:

    • 规模无关的编程抽象必须有实体的概念作为原子性的边界。

    • 不能有跨越多个实体的事务,原因很简单,就是无法保证多个实体将驻留在同一个系统上。(记住,我们在这里讨论的是聚合实体)。

    • 实体的表示没有限制。它可以存储为SQL记录、XML文档(这是2007年的记忆!),文件、blob或任何其他方便且适合应用程序需要的内容。一种可能的表示形式是SQL记录的集合(可能跨越多个表),其主键以实体键开头。

    让我们停下来想一想其中的含义。如果使用SQL存储来持久化状态,并且聚合实体映射到多个表,则要为该聚合实体提供原子性的单一可序列化范围,我们需要确保这些表中的所有相关行都是同位的。此外,这也意味着我们需要对所有这些表使用相同的分区策略。

    在Pivotal,我们开发了一个提供SQL接口的分布式存储,称为GemFire XD。正是由于这个原因,GemFire允许您为表指定分区和托管规则。例如:
    屏幕快照 2020-03-15 下午8.52.21.png

    这一结果也有助于解释文档存储的一些流行性,其中嵌入数据的文档自然地将聚合实体建模为可序列化和分发的单元。例如,MongoDB文档声明:

    在MongoDB中,写操作在文档中是原子的级别,没有一个写操作可以原子化影响多个文档中的多个
    Collections。一种嵌入的非规范化数据模型数据将表示实体的所有相关数据组合在一起在一个文档中。
    另一种表示方式,比如基于实体的事件存储,也很好地适应了这种模式。

    我们还必须承认,替代索引不能与主索引或给定实体位于相同的可序列化范围内(有关详细信息,请参阅本文)。
    规模无关的应用程序不能原子地更新实体及其备用索引!上层规模不可知的应用程序必须设计为理解备用索引可能与使用其主索引(即实体键)访问的实体不同步。

    我喜欢这里的cqr,因为它使这种区别非常明确(并在有用的地方提供非规范化视图以提高效率)。

    实体是寻址的单位
    …单个应用程序中的实体集的大小越来越大,无法容纳单个数据存储。每个实体都适合一个商店,但它们的集合却不适合。无状态应用程序越来越多地基于某种分区方案路由以获取实体。其次,获取和分区方案被分离到应用程序的较低层中,并故意与负责业务逻辑的应用程序的上层隔离。这有效地推动消息目的地成为实体密钥。

    消息被发送到实体。因此,我们研究的是一个分布式系统,其中(聚合)实体封装状态,并通过消息传递单独寻址。这个词从来没有在报纸上使用过,但对我来说,这个词叫“分布式参与者”,每个聚合实体都由一个参与者表示。

    就发送事务而言,消息传递是异步的。

    对于应用程序开发人员来说,在处理事务时发送消息、发送消息,然后终止事务是非常复杂的。这将意味着你没有引起某事发生的记忆,但它确实发生了!因此,事务性的消息传递是不必要的。

    随着系统的扩展和实体的移动,消息必须跟随。此外:

    当实体移动时,发送方和目的地之间的FIFO队列的清晰度偶尔会被破坏。信息重复。以后的消息比以前的消息来得早。生活变得更加混乱。由于这些原因,我们看到规模无关的应用程序正在发展,以支持所有应用程序可见消息的等幂处理。这也意味着在消息传递中重新排序。

    Helland接着描述了在合作伙伴基础上管理保护状态所需的活动跟踪。这些活动工作流(小“w”)需要在没有原子性的情况下达成一致。

    正如多年来所讨论的,在实体内的活动中,实现决策的工作流程。当我们看到几乎无限的伸缩性时,工作流的细粒度特性令人惊讶。

    两层体系结构
    Helland建议在每个可伸缩的应用程序中应该(至少)有两层。

    应用程序的较低层理解这样一个事实,即添加更多的计算机以使系统具有可伸缩性。除了其他工作外,它还管理上层代码到物理机器及其位置的映射。较低的层可以感知比例,因为它理解这种映射。我们假设下层为上层提供了一个规模无关的编程抽象。

    使用这个抽象,上层编写时不必担心这些伸缩问题。

    通过坚持与规模无关的编程抽象,上层应用程序代码的编写不必担心规模问题。

    考虑到我们之前讨论过的关于实体和消息传递的所有假设,您可以看到这是如何实现的。较低层的讨论让我想到了介观和插入其中的框架。

    总结
    **
    最好还是用他自己的话总结:

    今天,我们看到新的设计压力强加给那些只想解决业务问题的程序员。他们的现实将他们带入一个几乎无限扩展的世界,并迫使他们陷入与手头的实际业务基本无关的设计问题。

    几年后,我们可能会看到新的中间件或平台的开发,这些中间件或平台提供了对这些应用程序的自动化管理,并消除了在样式化编程范式中开发的应用程序的扩展挑战。