【Distributed System】分布式理论 - CAP 理论(CAP Theorem)

Posted by 西维蜀黍 on 2019-01-08, Last Modified on 2023-09-24

背景

2000年7月,加州大学伯克利分校的Eric Brewer教授在ACM PODC会议上提出CAP猜想。2年后,麻省理工学院的Seth Gilbert和Nancy Lynch从理论上证明了CAP。之后,CAP理论正式成为分布式计算领域的公认定理。

概述

CAP理论:一个分布式系统最多只能同时满足一致性(Consistency)、可用性(Availibility)和分区容错性(Partition tolerance)这三项中的两项。

需要注意的的是,CAP理论中的CA和数据库事务中ACID的CA并完全不是同一回事儿。两者之中的C都是都是一致性(Consistency)。然而,CAP中的A指的是可用性(Availability),而ACID中的A指的是原子性(Atomicity),切勿混为一谈。

一致性(Consistency)

**一致性(Consistency)**是指 “all nodes see the same data at the same time”, 即当某个逻辑操作完成后,对于该操作所涉及的所有数据,在同一时间所有节点中存储的数据应该是完整且完全一致的。

在分布式系统中,数据通常不会只有一份,当用户对数据进行了一定的修改操作(增、删、改)后,为了保证数据的一致性,应该对所有操作进行相同的操作并且这些操作应该是同时成功或者同时失败的。如果一个存储系统是保证一致性的,那么用户读到的数据就一定是最新的(而不会发生两个不同的用户,在同一时间在不同的存储节点中,读取得到不同副本的的情况)

可用性(Availability)

**可用性(Availability)**是指“Reads and writes always succeed”,即用户在访问数据时可以得到及时的响应,而无论当前是否存在操作冲突,或者软硬件升级。

  • 可用性并不意味着数据的一致性,比如读取到的数据是过期数据或脏数据,这时对用户来说,是有及时响应的,因此仍然认为是(该系统)具有可用性;
  • 可用性意味着时效性,因为可用性要求“及时”的响应。因为对于大部分应用而言,超过一定相应时间的服务是没有意义的。

对于一个可用性的分布式系统,每一个非故障的节点必须对每一个请求作出响应。所以,一般我们在衡量一个系统的可用性的时候,都是通过停机时间来计算的

可用性分类 可用水平(%) 年可容忍停机时间
容错可用性 99.9999 <1 min
极高可用性 99.999 <5 min
具有故障自动恢复能力的可用性 99.99 <53 min
高可用性 99.9 <8.8h
商品可用性 99 <43.8 min

通常我们描述一个系统的可用性时,我们说淘宝的系统可用性可以达到5个9,意思就是说他的可用水平是99.999%,即全年停机时间不超过 (1-0.99999)*365*24*60 = 5.256 min,这是一个极高的要求。

分区容错性(Partition Tolerance)

**分区容错性(Partition Tolerance)**是指“The system continues to operate despite an arbitrary number of messages being dropped (or delayed) by the network , or between nodes”,即分布式系统在遇到在网络传输过程中数据丢失(或延迟),或部分节点无法工作时,仍能够对外提供服务。

换句话说,我们在设计分布式系统时,必须考虑到可能出现在网络传输过程中数据丢失(或延迟),或者部分节点无法工作的情况。通常的做法,是将一个数据记录(data records)复制多份到多个节点中。

When a network partition failure happens should we decide to

  • Cancel the operation and thus decrease the availability but ensure consistency
  • Proceed with the operation and thus provide availability but risk inconsistency

我们必须容忍(tolerate)在分布式系统中出现的网络传输数据丢失或延迟。如果我们选择不容忍这种数据丢失或延迟,则意味着我们必须要通过某种机制避免网络传输数据丢失或延迟,显然,这是不可能实现的。

Analysis

值得说明的是,CAP 理论是在分布式系统的上下文讨论的,而分布式系统必须依赖于网络传输。只要有网络传输,就一定会出现数据丢失或延迟的情况。换句话说,数据丢失或延迟是必须要忍受的(因为我们无法避免它的出现)。因而,对于一个分布式系统,分区容错性(Partition Tolerance)是一定满足的。

因此,若舍弃掉了 CAP 中的 P(Partition Tolerance),则意味着分区容错性不满足。要保证不出现网络传输数据丢失或延迟,唯一的方法就是不使用网络传输,这时分布式系统也就没有意义了(因为分布式系统必须基于网络构建)。

因此,CAP 三者地位并不平等:P(Partition Tolerance) 是基础(或者说 P 是一定满足的),在 C (Consistency) 和 A (Availability) 之间需要进行权衡。

证明

我们以一个小例子来证明, C (Consistency) 和 A (Availability) 之间不能同时满足。

最初,一个客户端请求 G1 节点 将变量 v 更新为 v1(写请求),这个变量的初始值为 v0。由于系统是可用的,G1 会做出回应(修改变量值,并告知客户端操作完成)。由于系统是可以忍受分区(满足 P )的,因而因为网络传输原因, G1 节点无法正常复制数据到 G2 节点上。

接下来,客户端向 G2 节点发出一个读请求。由于系统是可用的,G2 会做出回应(返回 v 变量的值,且值为 v0),因为由于网络传输原因, G1 节点无法正常复制数据到 G2 节点上。

**一致性(Consistency)**要求,当某个逻辑操作完成后,对于该操作所涉及的所有数据,在同一时间所有节点中存储的数据应该是完整且完全一致的

在这个例子中,客户端向 G1 节点写入了新值 v1,而却在 G2节点中获取了旧的值 v0。因此,这个系统并不满足一致性(Consistency)


若这个系统选择保证一致性(Consistency),当网络传输问题发生时,则无法实现 G1 和 G2 节点之间的数据同步操作。最终,当客户端请求 G2 节点时,G2 节点因为无法完成数据同步,因而无法向客户端提供最新的值,最终不会向客户端做出回应,因此系统并不满足可用性(Availability)。

CAP的权衡

通过CAP理论及前面的证明,我们知道一个分布式系统是无法同时满足一致性、可用性和分区容错性这三个特性,那要舍弃哪个呢?

我们分三种情况来讨论。

CA without P - 放弃分区容错性(Partition Tolerance)

这种情况在分布式系统中是不存在的。

上文也提到了,在分布式环境下,在网络传输过程中数据的丢失和延迟是无法被完成避免的,因而网络分区(network partition)是必须要被容忍的(即 CAP 中的 P 必须被满足)。

所以如果舍弃P,意味着要舍弃分布式系统。那也就没有必要再讨论CAP理论了。

比如我们熟知的关系型数据库,如 MySQL 和 Oracle 就是保证了可用性和数据一致性,但是它只是个单机系统,并不是个分布式系统。一旦关系型数据库要考虑主备同步、集群部署等,就必须要把P也考虑进来。

总结来说,对于一个分布式系统来说。P是一个基本要求,CAP三者中,只能在CA两者之间做权衡,并且要想尽办法提升P

在《Spanner, TrueTime and the CAP Theorem》中,Eric提到,从Google的经验中可以得到的结论是,无法通过降低CA来提升P。要想提升系统的分区容错性,需要通过提升基础设施的稳定性来保障,而不是通过降低C和A来获得的。

CP without A - 强一致系统(High Consistency)

**强一致性系统(High Consistency)**意味着在任何时间向任何节点(nodes)获取数据都会得到相同的结果。换句话说,向任何一个节点执行一个读操作都会返回相同的值,而且是最新写入的那个值。

如果一个分布式系统不要求强的可用性,即容许系统停机或者长时间无响应的话,就可以在CAP三者中保障CP而舍弃A。

一个保证了CP而一个舍弃了A的分布式系统,一旦发生网络故障或者消息丢失等情况,就要牺牲用户的体验,等待所有数据全部一致了之后再让用户访问系统。

例子

  • 最典型的,就是分布式的关系型数据库,即使在发生极端情况时,仍然优先保证数据的强一致性,代价就是舍弃系统的可用性。
  • 严格的法定数协议(Paxos、Raft、ZAB)或者2PC协议
  • 分布式系统中常用的Zookeeper

AP wihtout C - 高可用系统(High Availability)

要高可用并允许分区,则需放弃一致性。一旦网络问题发生,节点之间可能会失去联系。为了保证高可用,需要在用户访问时可以马上得到返回,则每个节点只能用本地数据提供服务,而这样会导致全局数据的不一致性。

例子

DNS

  • 发布一张网页到 CDN,多个服务器有这张网页的副本。后来发现一个错误,需要更新网页,这时只能每个服务器都更新一遍
  • 一般来说,网页的更新不是特别强调一致性。短时期内,一些用户拿到老版本,另一些用户拿到新版本,问题不会特别大。当然,所有人最终都会看到新版本。所以,这个场合就是可用性高于一致性

12306,淘宝

很多系统在可用性方面会做很多事情来保证系统的全年可用性可以达到N个9,所以,对于很多业务系统来说,比如淘宝的购物,12306的买票。都是在可用性和一致性之间舍弃了一致性而选择可用性。

你在12306买票的时候肯定遇到过这种场景,当你购买的时候提示你是有票的(但是可能实际已经没票了),你也正常的去输入验证码,下单了。但是过了一会系统提示你下单失败,余票不足。这其实就是先在可用性方面保证系统可以正常的服务。

  • 值得注意的是,这在数据的一致性方面做了些牺牲(这意味着用户看到的并不一定是正确的结果),因而会影响一些用户体验,但是也不至于造成用户流程的严重阻塞。

当进行最后下单的时候,还是会保证强一致性(通过牺牲可用性,可体现为等待时间较长,甚至出现失败)。

其他

  • 电子邮件、Amazon S3,Google搜索引擎

例子

场景

如上图,是我们证明CAP的基本场景,网络中有两个节点N1和N2,可以简单的理解N1和N2分别是两台计算机,N1和N2组成了一个完整的系统,他们的功能是相同的(functionally equlvalent),他们之间网络可以连通。

N1中有一个应用程序A,和一个数据库V,N2也有一个应用程序B2和一个数据库V。现在,A和B是分布式系统的两个部分,V是分布式系统的数据存储的两个子数据库。

说明

在满足一致性时(注意,这里暗指强一致性),N1和N2中的数据是一样的,V0=V0。

在满足可用性时,用户不管是请求N1或者N2,都会得到立即响应。

在满足分区容错性时,N1和N2有任何一方宕机,或者网络不通的时候,都不会影响N1和N2彼此之间的正常运作。


作为一个分布式系统,它和单机系统的最大区别,就在于网络,现在假设一种极端情况,N1和N2之间的网络断开了,我们要支持这种网络异常,相当于要满足分区容错性,这时能不能同时也满足一致性和响应性呢?

假设在N1和N2之间网络断开的时候,有用户向N1发送数据更新请求,那N1中的数据V0将被更新为V1,由于网络是断开的,所以分布式系统同步操作M不能成功被执行,所以N2中的数据依旧是V0。

这个时候,有用户向N2发送数据读取请求,由于数据还没有进行同步,应用程序没办法立即给用户返回最新的数据V1,怎么办呢?

有二种选择:

  • 情况1:牺牲数据一致性,保证可用性。N2直接响应旧的数据V0给用户;

  • 情况2:牺牲可用性,保证数据一致性。阻塞等待,直到网络连接恢复,分布式系统同步操作M完成之后,N2再给用户响应最新的数据V1。

这个过程,证明了要满足分区容错性的分布式系统,只能在一致性和可用性两者中,选择其中一个。

Reference