CAP定理在分布式系统设计中的最新应用

                   
banq
18-01-05 1 3587 5

本文翻译自国外InfoQ和计算机杂志上一篇2012年旧文,本文就有关数据同步进行了讨论,特别关注业务事务的不变性与一致性如何在分布式系统中巧妙保证,探讨了长时间运行的事务的补偿机制。这些对分布式系统设计都有很大帮助。

原文大意如下:

CAP理论认为,任何联网的共享数据系统只能在三个属性中的两个。但是,通过明确处理分区,设计人员可以优化一致性和可用性,从而实现三者之间的某种权衡。

自CAP定理推出以来的十年中,设计师和研究人员已经使用(有时滥用)CAP理论作为探索各种新型分布式系统的依据。NoSQL运动也将其作为反对传统数据库的论据。

CAP定理指出,任何网络共享数据系统最多可以有三个理想的属性中的两个:

1.一致性(C)相当于拥有一份最新的数据;
2.该数据的高可用性(A)(用于更新);
3.容忍网络分区(P)。

(banq注,关于CAP定理所有你不知道的中CAP解释更加易懂:

1.一致性。每一次读取都会让你得到最近的写入结果
2.可用性。每个节点(如果没有失败)总是能执行查询(读取和写入)操作
3.分区容忍。即使节点之间的连接关闭,其他两个属性也会得到保证。

分区P基本可以理解为出现网络故障导致的通讯中断,形成两个以上的各自为政的孤岛服务器节点。


CAP的这种表达是服务于它的目的,这是为了让设计师的思想敞开,上升到更广泛的系统中进行权衡; 的确,在过去的十年中,已经出现了大量的新系统,以及关于一致性和可用性的相对优点的争论。“三分之二”的表述却是有误导性的,因为它往往过分简化了属性之间的紧张关系。而这些细微差别却很重要。CAP仅禁止设计空间的一小部分:在分区存下的完美可用性和完美一致性。这种情况是很少能实现的。

虽然设计师仍然需要在分区存在情况下在一致性和可用性之间进行选择,但是处理分区和从分区中恢复的灵活性有着令人难以置信的好处。现代CAP目标应该是最大限度地提高一致性和可用性的组合,这对于特定的应用是有意义的。这种方法结合了分区发生和事后恢复的两种方式,从而帮助设计人员考虑CAP,超越其历史上的限制。

为什么“2/3”是误导

理解CAP最简单的方法是考虑分区两侧的两个节点。允许其中任意一个节点更新状态将导致两个节点变得不一致,从而放弃C,同样,如果选择是保持一致性,分区中任意一侧必须是一种不可用的状态,从而放弃A. 只有当节点一直完美通讯时才有可能保持两者的一致性和可用性,这又丧失P。因此普遍认为,对于广域系统来说,设计者不能放弃P,因此在C和A之间有一个困难的选择。从某种意义上说,NoSQL运动是关于首先关注可用性和其次才是一致性的选择; 遵循ACID属性(原子性,一致性,隔离性和持久性)的数据库则正好相反。

在20世纪90年代中期,我和我的同事们正在构建各种基于群集的广域系统(本质上是早期的云计算),包括搜索引擎,代理缓存和内容分发系统。由于收入目标和合同规范,系统可用性非常重要,所以我们发现自己经常选择通过采用缓存或日志记录更新等策略来优化可用性,实现之后的冲突解决校验。虽然这些策略确实提高了可用性,但是增加的代价是一致性降低。

这个一致性与可用性的争论的第一个版本表现为ACID与BASE,BASE在当时并没有得到很好的接受,主要是因为人们喜欢ACID的特性而不愿放弃。CAP定理的目的是证明探索更广阔的设计空间,因此是一种“2/3”的设计。

CAP定理首先出现在1998年秋季,1999年出版,并在2000年的“分布式计算原理专题讨论会”上发表了主题演讲 ,从而证明了这一点。
(这个定理被Eric Brewer在2000年分布式计算原理研讨会上提出。2002年,来自麻省理工学院的Seth Gilbert和Nancy Lynch发表了一个Brewer猜想的正式证明,使其成为了一个定理。根据Brewer的说法,他只是想让社区开始谈论这个问题,但他的话最终被解释为一个定理了。)

但是“2/3”的观点在几个方面有误导性。

首先,因为当分区很少时,或者当系统没有被分区时就没有理由放弃C或A. 其次,C和A之间的选择可以在相同的系统中以非常细的粒度出现多次; 子系统不仅可以做出不同的选择,而且可以根据操作甚至特定的数据或用户来选择。
最后,这三个属性是连续的,不是像二进制那样不是0就是1。可用性显然是从0到100%连续的,同时也有很多级别的一致性,甚至分区也有细微差别,包括系统内部是否存在分区的高低程度。

探索这些细微差别需要推动传统处理分区的方式,这是基本的挑战。因为在分区很少出现情况下,CAP应该在大多数情况下可以允许完美的C和A,但是当分区存在或被感知时,检测分区并明确说明分区的策略是有序的。这个策略应该有三个步骤:检测分区,进入明确的分区模式,可以限制一些操作,并启动恢复过程来恢复一致性,并弥补分区过程中犯的错误。


ACID,BASE和CAP

ACID和BASE代表了在一致性 - 可用性两点之间进行选择的设计哲学。ACID事务属性注重一致性,是关系数据库的传统方法。我和我的同事在20世纪90年代后期创建了BASE,以捕捉新兴的高可用性设计方法,并明确选择和范围。包括云在内的现代大规模广域系统都使用了两种方法的组合。

尽管这两个术语都比较精确,BASE这个缩写代表:基本可用,软状态,最终一致。软状态和最终一致性是存在分区的情况下能够很好地工作一种技术手段,这种手段能够提高可用性。

CAP和ACID之间的关系比较复杂,常常被误解,部分原因是ACID中的C和A虽然和CAP中C和A是相同的字母,但是表达不同的概念,选择可用性只会影响一些ACID保证。四个ACID属性是:

原子(A)。所有的系统都受益于原子操作。当我们将焦点放在可用性时,分区的两边应该仍然使用原子操作。而且,更高级别的原子操作(ACID暗示的那种)实际上简化了分区发生故障以后的恢复过程。

一致性(C)。在ACID中,C意味着事务预处理所有的数据库规则,例如唯一键。相比之下,CAP中的C仅指单一拷贝一致性,这是ACID一致性的严格子集。ACID的一致性也无法在分区之间保持。分区恢复将需要恢复ACID一致性。更一般地说,在分区中保持不变性也许是不可能的,因此需要仔细考虑哪些操作是不允许的,以及如何在恢复过程中恢复不变性。

隔离(I)。隔离性是CAP定理的核心:如果系统需要ACID隔离,则在分区过程中最多可以在一侧进行操作。一般而言,可序列化需要通信,这样就会面临跨分区的失败情况。在分区恢复过程中,通过补偿机制实现跨越分区的相对弱的正确性是可行的。

耐久性(D)。与原子性一样,尽管开发人员可能选择通过软状态(以BASE的形式)以避免它,因为其开销昂贵,但是没有理由禁止选择持久性。一个微妙之处是,在分区恢复过程中,可以反转在操作过程中在不知不觉中违反了不变的持久操作。然而,在恢复的时候,通过双方比较长的历史资料对比可以发现和纠正违反不变性的操作。一般来说,在分区的每一边运行ACID事务能够使得分区恢复变得更容易,并且使用一个框架来实现补偿事务有助于分区恢复。

Cap-latency连接

在其经典的解释中,CAP定理忽略了延迟,尽管在实践中,延迟和分区是深度相关的。在操作上,CAP的本质是,在发生了分区(网络故障)以后,有一段timeout时间,在这个时间内程序必须做出基本的决定:

1.取消操作,从而降低可用性,或

2.继续进行操作,从而导致风险不一致。

例如重试通信可以实现一致性,比如通过Paxos或两阶段提交2PC,这些都是只是延迟了决策。程序在某个时刻总是必须做出决定; 无限期地重试通信本质上是选择C而不是A。

因此,实际上,一个分区是通信的一段时间范围。在一段时间范围内未能达到一致意味着存在一个分区,因此这个操作必须在C和A之间进行选择。这些概念反映了关于延迟的核心设计问题:双方如果没有沟通通讯情况下会继续运行前进吗?

这种务实的观点引起了一些重要的后果。首先是没有分区的全局概念,因为一些节点可能检测到一个分区,而另一些节点可能不会。第二个结果是节点可以检测分区并进入分区模式,这是优化C和A的核心部分。

最后,这个观点意味着设计者可以根据目标响应时间故意设定时间范围; 边界更紧的系统可能会更频繁地进入分区模式,有时网络只是缓慢的,而不是实际的分区。

有时为了避免在大范围内保持一致性的高延迟,放弃强C是有意义的。雅虎的PNUTS系统通过异步实现维护远程副本同步而导致不一致。但是,它在本地机器实现主节点,从而减少延迟。这个策略在实践中运行良好,因为用户可据根据用户(正常)地理位置实现自然分区。理想情况下,每个用户是最靠近主数据的。

Facebook使用相反的策略:主数据始终在一个位置,所以远程用户通常有一个更接近但可能是陈旧的副本。但是,当用户更新其页面时,即使有更长的延迟时间,更新也会直接写入主数据节点。20秒后,用户会看到到最近的数据副本,在这个时候数据应该是反映最新数据。

CAP混乱

CAP定理的各个方面经常被误解,特别是可用性和一致性的范围,这可能会导致不希望的结果。如果用户根本无法访问服务,除非部分服务在客户端上运行,否则C和A之间没有选择。这种通常被称为断线操作或脱机模式,这种例外情况变得越来越重要。某些HTML5功能(特别是客户端持久性存储)使未连接的操作更容易。这些系统通常选择A而不是C,因此必须需要长时间的分区恢复(以保证一致性)。

一致性的范围反映了这样的想法:在某个边界内,状态是一致的,但是在这个边界之外就无法保证。例如,在主分区内,可以确保完整的一致性和可用性,而在分区之外,服务不可用。Paxos和原子多播系统通常符合这种情况。在Google中,主分区通常驻留在一个数据中心内;而Paxos被广泛用于确保全球范围内实现共识,如Chubby, 和高度可用的持久存储如Megastore。

独立的,自洽的子集可以在分区的情况下自行运行,尽管不能确保全局的不变性。例如,对于设计人员在节点间预分配数据的分片(sharding),很有可能每个分片在分区故障发生过程中都会持续独立运行。相反,如果相关状态被跨分区划分,或者全局不变量是非常必要时,那么充其量只有一方可以继续保持运行,最坏的情况是都无法继续运行。

选择一致性和可用性(CA)作为“2/3”是否合理?正如一些研究人员指出的那样,精确地说放弃P的意思是不够清晰的。设计师是否可以选择不分区呢?如果选择是CA,然后才是分区?最好从概率上考虑:选择CA意味着分区(网络故障)的概率远小于其他系统故障的概率如灾难或多个同时发生的故障。

这样的观点是有道理的,因为真实的系统会在一些失败都下失去了C和A,所以这三个属性都是程度的问题。在实践中,大多数集群组都假定在一个数据中心(单个站点)内没有分区,因此在单个站点内可以设计CA; 这样的设计,包括传统的数据库,都是CAP之前的默认选择。考虑到全球地区的高延迟,为了获得更好的性能,在大范围内放弃完美的一致性是相对常见的。

CAP混淆的另一个方面是丧失一致性的隐藏成本,这是需要掌握系统的不变性。一致性系统的微妙之处在于即使设计者不知道它们是什么,不变性也会保持不变。因此,广泛的合理的不变性将工作得很好。相反,当设计者选择A时,则需要在分区之后恢复不变性,因此必须对所有不变性都是明确掌握的,这是既具有挑战性又容易出错的。在微观CPU核编程方面,类似相同的并发更新问题,多线程编程比顺序编程更难一样。

管理分区

设计师面临的挑战是减轻分区(网络故障)对一致性和可用性的影响。关键的想法是非常明确地管理分区(网络故障),不仅包括检测,还包括一个特定的恢复过程和对一个分区中可能违反的所有不变性的总结。这种管理方法有三个步骤:


1.检测分区的开始,
2.进入明确的分区模式,可能会限制一些操作
3.通信恢复时启动分区恢复。

最后一步旨在恢复一致性,并补偿程序在分区时各自运行犯的错误(数据不一致)。

正常情况下的操作是一系列的原子操作,因此分区总是在正常操作之间开始。系统超时后,检测到分区,检测端进入分区模式。如果确实存在分区,则双方都进入此模式,但也可以进行单向分区。在这种情况下,另一方根据需要进行通信,或者该方正确响应或不需要进行通信; 无论如何,操作应保持一致。但是由于检测端操作不一致,必须进入分区模式。使用法定数量选取的系统就是这种单向分区的一个例子。一方区域如果有符合法定数量的节点(比如共3个节点,有两个节点在一个区域就是符合法定数量)则可以继续,但另一方不能。支持断开操作的系统显然具有分区模式的概念,

一旦系统进入分区模式,两种策略是可能的。首先是限制一些操作,从而降低可用性。其次是记录有关在分区恢复过程中将有帮助的操作的额外信息。继续尝试通信将使系统能够识别分区何时结束。

哪些操作在发生分区时应该继续进行?

决定限制哪些操作主要取决于系统必须维护的不变性。给定一个不变性集合,设计者必须决定是否在分区模式下保持一个特定的不变性,或者有意在恢复过程中恢复它。例如,对于表中的键必须是唯一的这种不变性约束,设计人员通常决定冒风险违背这个不变性,并允许在分区中使用重复相同键。重复键很容易在恢复过程中检测到,假设可以合并,设计人员可以很容易地恢复全局的不变性(全局键的唯一性约束)。

然而,对于在分区中必须保持的不变性,设计者必须禁止或修改可能违反它的一些操作。(一般情况下,没有办法预知操作是否实际上将违反不变性,因为对方的状态并不可知。)一些外部化的活动,如信用卡充值,属于这种情况。在这种情况下,对付办法是记录下意图(用户操作意图,如命令/事件等)并在恢复后执行。这种情况通常属于工作流的一部分,比如有明确的订单处理状态的,在分区结束之前推迟操作几乎没有什么坏处。设计师以一种用户看不到的方式放弃了A。用户只知道他们下了订单,系统稍后会执行。

更一般地说,分区模式引起了用户界面体验的挑战,即用户传达任务是正在进行但没有完成。研究人员已经详细探讨了这个问题,对于断开连接的操作,这只是一个很长的分区。例如,Bayou的日历应用程序以不同的颜色显示潜在的不一致(暂定)条目。这样的情况在工作流程应用程序(如使用电子邮件通知的商务应用程序)和离线模式的云服务(例如Google Docs)中都会定期显现。

关注明确的原子操作而不仅仅是读写操作的一个原因是:分析高级操作对不变性的影响要容易得多。本质上,设计者必须建立一个表格,查看所有操作和所有不变性规则的交叉乘积,并为每个条目决定操作是否违反不变性约束。如果是这样,设计师必须决定是否禁止,推迟或修改操作。在实践中,这些决定还可以取决于已知状态等。例如,在存放某些数据的家庭节点的系统中,通常可以在家庭节点上进行5个操作,但是不能在其他节点上进行。

跟踪双方操作历史的最好方法是使用版本向量( version vectors),它捕获操作之间的因果关系。向量的元素是一对(节点,逻辑时间),每个已更新对象的节点和最后一次更新的时间都有一个条目。如果一个对象有A和B的两个操作版本,如果A的时间大于或等于B,并且A的时间中至少有一次更大(时间数值是不断增大的),则A比B新。

如果无法对版本向量排序,则更新是并发的,可能不一致。因此,根据双方的版本向量历史资料,系统可以容易地知道哪些操作已经按照已知的顺序执行,哪些操作是同时执行的。最近的研究证明,如果设计者选择关注可用性,那么这种因果一致性通常有最好的结果。

分区恢复

在某个时候,通信恢复,分区结束。在分区过程中,每一边都是可用的,各自都运行了一些操作,其中因为分区推迟了一些操作,并且违反了一些不变性约束。此时,系统知道双方的当前状态和历史操作记录,因为它在分区模式下保持了仔细的历史操作事件日志。这种情况下当前状态不如历史事件日志有用,系统可以从中推断出哪些操作实际上违反了不变性,哪些结果已经无法收回,包括发送给用户的响应。设计师必须在恢复过程中解决两个难题:

1.双方的状态必须保持一致
2.必须对分区模式下的错误进行补偿(让双方状态一致性,符合全局不变性约束)。

更容易修复当前状态的办法是:从分区时的状态开始,以某种方式向前滚动两组操作,从而一直保持两边一致的状态,。Bayou通过将数据库回滚到正确的时间来显式执行此操作,并以明确的,确定的顺序重播全套操作,以使所有节点达到相同的状态。类似地,源代码控制系统,如并行版本系统(CVS)从共享一致点和前滚更新开始合并分支(banq注:事件溯源也属于这种,区块链也是)。

大多数系统不能总是合并冲突。例如,CVS偶尔会出现用户必须手动解决的冲突,而具有脱机模式的wiki系统通常会在生成的文档中留下需要手动编辑的冲突。

相反,一些系统总是可以通过选择某些操作来合并冲突。一个恰当的例子就是Google文档中的文本编辑,它限制了应用样式和添加或删除文本等的操作。因此,虽然解决冲突在一般意义上是不可解决的,但实际上,设计者可以选择在分区过程中限制某些操作的使用,以便系统在恢复过程中自动合并状态。推迟具有风险的操作(banq注:所谓风险是可能违背不变性约束的操作,或在分区的情况下干脆停止写操作。)是一个相对简单的实施办法。

市面上通用框架一般是使用交换操作(commutative operations)实现状态自动状态收敛一致。系统连接历史操作日志,按照某种顺序排序,然后执行它们。交换性意味着能够将操作重新排列为以全局顺序为优先的顺序。不幸的是,仅使用交换操作比看起来更难; 例如,加法是可交换的,但是边界检查则不是(例如零余额)。

马克·夏皮罗(Marc Shapiro)及其同事在INRIA 18,19最近的工作大大改善了交换操作在状态融合中的应用。该团队开发了可交换的复制数据类型(CRDT),这是一类在分区之后可证明地收敛的数据结构,并描述了如何使用这些结构:

1.确保分区过程中的所有操作都是可交换的,或者
2.在格上表示值,并确保分区期间的所有操作相对于该格是单调递增的。

后一种方法通过移动到每一边的最大值来收敛状态。这是一个正规化formalization,亚马逊在其购物车中就是这么做的,分区之后,收敛值是两个两个购物车的联盟,这个联盟是一个单调集合操作。选择这种方案的结果是被删除的项目可能会重新出现。

但是,CRDT也可以实现添加和删除项目的分区容忍性。这种方法的本质是保持两套数据集:每套都有添加和删除的项目,不同的是集合的成员。每个简化集合进行收敛,因此差异化也是如此进行。在某些时候,系统可以简单地通过从两个集合中移除走已删除的项目来进行清理。但是,这种清理通常只有在系统没有分区的情况下才有可能。换句话说,设计者必须在分区期间禁止或推迟某些操作,但是这些操作不会限制敏感的可用性。因此,通过CRDT来实现状态,设计者可以选择A,并且在分区之后仍然保证状态自动收敛。

5
banq
2018-01-05 17:39

接上篇:

补偿错误

除了计算分区后状态之外,修复分区过程中出现的错误还有点难度。分区模式操作的跟踪和限制确保知道哪些不变性可能被违反,这又使得设计者能够为每个违反这样的不变性的错误操作创建恢复策略。通常,系统在恢复期间发现违规行为,并且必须在当时执行任何修复操作。

有不同的方法来修正不变性约束,包括诸如“最后的写入操作胜利last writer wins”(忽略之前的一些更新操作),更聪明的方法,合并操作和人为升级等微不足道的方法。后者的一个例子是飞机超额预订:登机是在某种意义上的分区恢复,不变的是必须拥有和乘客一样数量的座位。如果乘客太多,有些会失去座位,理想的情况是客户服务会以某种方式补偿那些订了同样的座位的多余乘客。

飞机的例子也表现出这样一个外在外错误(已经发生外部影响的错误):如果航空公司说某个乘客订票了却没有座位,修复这个问题就容易多了(人工补偿)。这是延迟风险操作的另一个原因:在恢复时,事实是已知的。赔偿的概念确实是纠正这种错误的核心。设计师必须创造补偿性操作,既能恢复不变性约束,又能更广泛地纠正外在的错误。

从技术上来说,CRDT只允许本地验证不变性约束 - 这是一个限制,但是不需要补偿,却会降低方法的功能。但是,使用CRDT进行状态收敛的解决方案可能允许暂时违反全局不变性约束,在分区之后收敛状态,然后执行任何所需的补偿。

从外在化错误中恢复通常需要一些关于外部化输出的历史。考虑醉酒人“拨号”情况,在这种情况下,一个人不记得在前一晚喝醉的时候打电话。从日常的角度来看,这个人的状态可能是合理的,但日志仍然显示了一个电话列表,其中一些可能是错误的。电话是人的状态(醉酒中毒)的外部影响。因为这个人不记得这些电话,所以可能很难弥补他们造成的麻烦。

在机器上下文中,计算机可以在分区中执行两次命令。如果系统能够从两个重复的订单中区分两个人为的订单,则可以取消其中一个重复的订单。如果是外部化的,则一种补偿策略是自动生成电子邮件给客户,说明系统意外地执行了两次订单,但是错误已经被修复,并附上下一张订单的折扣券。但是,如果没有适当的历史记录,那么捕捉错误的负担就落到顾客身上(靠顾客维权了)。

一些研究人员已经正式探讨了补偿交易作为处理长期事务的方式。长时间运行的事务面临着分区决策的变化:为了确保一致性,或者尽早释放并将未提交的数据公开给其他事务,但允许更高的并发性,最好是长时间保持锁定状态?一个典型的例子是试图将所有员工记录更新作为单个事务处理。以正常方式序列化此事务必须锁定所有记录以防止并发操作。补偿交易则采用不同的方法,将大交易分解成一个Saga,它由多个子事务组成,每个子事务都沿着自己方式提交。 因此,为了中止退出较大的事务,系统必须通过undo每个已经提交的子事务,通过发出一个新事务(即补偿事务)来撤消每个已经执行的子事务。

通常,我们的目标是避免中止使用了错误提交数据的其他事务(也就是不会级联中止事务)。这种方法的正确性不取决于可串行性或隔离性,而取决于事务顺序对状态和输出的净效应。也就是说,在补偿之后,数据库基本上是否在相当于从未执行子事务的地方结束?等同性必须包括外部化的行动; 例如,退还重复购买与事务开始之前没有向客户收取费用时几乎是一样的,但是可以说是等同的。分区恢复中也有同样的想法。服务或产品提供者不能总是直接撤销错误,而是要承认错误并采取新的补偿措施。如何最好地将这些想法应用于分区恢复是一个公开的问题。“自动柜员机中的补偿问题”描述了一个应用领域中的一些问题。

当分区存在时,系统设计者不应该盲目牺牲一致性或可用性。使用前面提出的方法,他们可以通过在分区期间仔细管理不变性约束来优化这两个属性。随着更新的技术(如版本向量和CRDT)进入简化其使用的框架,这种优化应该变得更加广泛。但是,与ACID交易不同,这种方法需要相对于过去的策略进行更周到的部署,最好的解决方案将在很大程度上取决于服务的不变性约束和操作的细节记录。

自动取款机中的补偿问题

在自动取款机(ATM)的设计中,强一致性似乎是合乎逻辑的选择,但在实践中,A胜过C.原因很简单:更高的可用性意味着更高的收入。无论如何,ATM设计作为一个良好的环境来审查在分区中补偿不变侵犯所涉及的一些挑战。

基本的ATM操作是存款,取款和余额查询。关键不变性约束是,余额应该是零或更高值,不能为负值。因为只有取款需要进行违反不变性的检查(不能取万款余额是负值),所以需要特殊的处理,而其他两个操作总是可以执行的。

ATM系统设计者可以选择在分区期间禁止提款,因为当时不可能知道真实的余额,但这会损害可用性。相反,现代的ATM机使用替代模式(分区模式)将净提取限制在k值以内,其中k可能是200美元。在此限制以下,提款完全可以正常工作; 当余额达到限额时,系统拒绝提款。因此,ATM选择一个复杂的可用性限制,允许提款但限制了风险。

当分区结束时,必须有一些方法来恢复一致性,并弥补系统分区时所犯的错误。恢复状态很容易,因为这些操作是可交换的,但补偿可以有多种形式。低于零的最终余额违反了不变性约束。在正常情况下,ATM发放了钱,导致了错误成为外部错误。银行通过收取费用并期待还款来进行补偿。鉴于风险是有限的,问题并不严重。但是,假设在分区的某个时间点(ATM不知道)的余额低于零,但后来的存款又使其恢复。在这种情况下,银行可能会追溯性地收取透支费用,或者由于客户已经支付了必要的款项,所以可能会忽略违规。

一般来说,由于通讯延误,银行体系的正确性不一致,通过审计和赔偿纠正。另一个例子是“check kiting”,在这个例子中,一个客户可以在多个分支机构之间在通信中断并在通讯恢复之前从其中取钱。可能会发生透支,也许会导致法律诉讼形式的赔偿。

(banq总结:CAP定理关键是对P的处理,P是分区容忍性,首先因为网络故障或堵塞出现了通讯消息丢失,一个整块网络分成两个区了,在这种情况下,你只能在一致性C和可用性A之间有所侧重选择,想要两个都完美实现是不可能,除非不存在分区可能,比如一直在一个数据中心内部。在分区出现的情况下,一般选择A可用性,记录下分区这段时间的操作日志,等分区结束后,对分区两边进行操作同步,这时数据同步操作有很多方式:使用带有时间戳的版本比较;可交换的复制数据类型(CRDT)。除了数据同步外(只是同步两边的当前状态),如果还要保证业务逻辑的全局不变性约束,防止数据重复等,需要进行补偿事务,有最后写入者赢;人工介入;Saga分布式事务机制等三种。)

原文:CAP Twelve Years Later: How the "Rules" Have Chang