CAP 理论

CAP定律(Consistency,Availability,Partition Tolerance theorem),说的是在一个分布式计算机系统中,一致性,可用性和分区容错性这三种保证无法同时得到满足,最多满足两个。该定律作为猜想在2000年提出,2002年被证实。其中,一致性说的是分布式系统中,所有节点在同一时刻看到同一个值。可用性说的是每个请求都会收到一个应答,无论该应答是成功还是失败。分区容错性指的是无论任何消息丢失,系统都可用。

Partition Tolerance的意思是,在网络中断,消息丢失的情况下,系统照样能够工作。

根据一些专家的分析,CAP并不是一个严谨的定律,并不是牺牲了Consistency,就一定能同时获得Availability和Partition Tolerance。还有一个很重要的因素是Latency,在CAP中并没有体现。在现在NoSQL以及其他一些大规模设计时,A和P并不是牺牲C或部分牺牲C的借口,因为即使牺牲了C,也不一定A和P,并且C不一定必须要牺牲。

-----

淘宝一天就处理了1亿零580万,而12306一天处理的交易仅仅166万条 ,如果从并发性上来说,淘宝的并发量远比12306大,但天猫的商品信息,促销数据都可以做缓存,做CDN,而12306的“商品”是一个个座位,这些座位必须通过后端数据库即时查询出来,状态的一致性要求很高。

从这点上看,12306的商品信息很难利用到缓存,因此12306查看“商品”的代价是比较大的,涉及到一系列的后端数据库操作,从这个角度讲,12306的复杂度是高于天猫的。

淘宝的商品相对独立,而12306商品之间的关联性很大,由于CAP定律限制,如果其商品的一致性要求过高,必然对可用性和分区容错性造成影响。

因此,业务设计上,如果找到一条降低一致性要求时,还能保证业务的正确性的业务分拆之路。举个例子,火车票查询时,不要显示多少张,而是显示“有”或“无”,或者显示>100张,50~100,小于50等,这样就可以减小状态的更新频率,充分使用缓存数据。

-----------

CAP 理论在搞分布式的程序员中已经是路人皆知了。但是 CAP 理论就好比是相对论,虽然所有的人都知道,但是却没有多少人真正理解。

要真正理解 CAP 理论必须要读懂它的形式化描述。 形式化描述中最重要的莫过于对 Consistency, Availability, Partition-tolerance 的准确定义。

Consistency (一致性) 实际上等同于系统领域的 before-or-after atomicity 这个术语,或者等同于 linearizable (可串行化) 这个术语。具体来说,系统中对一个数据的读和写虽然包含多个子步骤并且会持续一段时间才能执行完,但是在调用者看来,读操作和写操作都必须是单个的即时完成的操作,不存在重叠。对一个写操作,如果系统返回了成功,那么之后到达的读请求都必须读到这个新的数据;如果系统返回失败,那么所有的读,无论是之后发起的,还是和写同时发起的,都不能读到这个数据。

要说清楚 Availability 和 Partition-tolerance 必须要定义好系统的故障模型。在形式化证明中,系统包含多个节点,每个节点可以接收读和写的请求,返回成功或失败,对读还要返回一个数据。和调用者之间的连接是不会中断的,系统的节点也不会失效,唯一的故障就是报文的丢失。 Partition-tolerance 指系统中会任意的丢失报文(这和“最终会有一个报文会到达”是相对的)。 Availability 是指所有的读和写都必须要能终止。

注: “Availability 是指所有的读和写都必须要能终止” 这句话听上去很奇怪,为什么不是“Availability 是指所有的写和读都必须成功”? 要回答这个问题,我们可以仔细思考下“什么是成功”。“成功”必须要相对于某个参照而言,这里的参照就是 Consistency。

CAP 理论说在一个系统中对某个数据不存在一个算法同时满足 Consistency, Availability, Partition-tolerance 。 注意,这里边最重要和最容易被人忽视的是限定词“对某个数据不存在一个算法”。这就是说在一个系统中,可以对某些数据做到 CP, 对另一些数据做到 AP,就算是对同一个数据,调用者可以指定不同的算法,某些算法可以做到 CP,某些算法可以做到 AP。

要做到 CP, 系统可以把这个数据只放在一个节点上,其他节点收到请求后向这个节点读或写数据,并返回结果。很显然,串行化是保证的。但是如果报文可以任意丢失的话,接受请求的节点就可能永远不返回结果。

要做到 CA, 一个现实的例子就是单点的数据库。你可能会疑惑“数据库也不是 100% 可用的呀?” 要回答这个疑惑,注意上面说的故障模型和 availability 的定义就可以了。

要做到 AP, 系统只要每次对写都返回成功,对读都返回固定的某个值就可以了。

如果我们到这里就觉得已近掌握好 CAP 理论了,那么就相当于刚把橘子剥开,就把它扔了。

CAP 理论更重要的一个结果是, 在 Partial Synchronous System (半同步系统) 中,一个弱化的 CAP 是能达到的:

    * 对所有的数据访问,总返回一个结果
* 如果期间没有报文丢失,那么返回一个满足 consistency 要求的结果。 

这里的半同步系统指每个节点存在一个时钟,这些时钟不需要同步,但是按照相同的速率流逝。更通俗的来说,就是一个能够实现超时机制的系统。

举个例子,系统可以把这个数据只放在一个节点上,其他节点收到请求后向这个节点读或写数据,并设置一个定时器,如果超时前得到结果,那么返回这个结果,否则返回失败。

更进一步的,也是最重要的,实现一个满足最终一致性 (Eventually Consistency) 和 AP 的系统是可行的。 现实中的一个例子是 Cassandra 系统。

 

----------------------------------

CAP原理(CAP THEOREM)

在足球比赛里,一个球员在一场比赛中进三个球,称之为帽子戏法(HAT-TRICK)。在分布式数据系统中,也有一个帽子原理(CAP THEOREM),不过此帽子非彼帽子。CAP原理中,有三个要素:
一致性(CONSISTENCY)可用性(AVAILABILITY)分区容忍性(PARTITION TOLERANCE)CAP原理指的是,这三个要素最多只能同时实现两点,不可能三者兼顾。因此在进行分布式架构设计时,必须做出取舍。而对于分布式数据系统,分区容忍性是基本要求,否则就失去了价值。因此设计分布式数据系统,就是在一致性和可用性之间取一个平衡。对于大多数WEB应用,其实并不需要强一致性,因此牺牲一致性而换取高可用性,是多数分布式数据库产品的方向。
当然,牺牲一致性,并不是完全不管数据的一致性,否则数据是混乱的,那么系统可用性再高分布式再好也没有了价值。牺牲一致性,只是不再要求关系型数据库中的强一致性,而是只要系统能达到最终一致性即可,考虑到客户体验,这个最终一致的时间窗口,要尽可能的对用户透明,也就是需要保障“用户感知到的一致性”。通常是通过数据的多份异步复制来实现系统的高可用和数据的最终一致性的,“用户感知到的一致性”的时间窗口则取决于数据复制到一致状态的时间。
最终一致性(EVENTUALLY CONSISTENT)
对于一致性,可以分为从客户端和服务端两个不同的视角。从客户端来看,一致性主要指的是多并发访问时更新过的数据如何获取的问题。从服务端来看,则是更新如何复制分布到整个系统,以保证数据最终一致。一致性是因为有并发读写才有的问题,因此在理解一致性的问题时,一定要注意结合考虑并发读写的场景。
从客户端角度,多进程并发访问时,更新过的数据在不同进程如何获取的不同策略,决定了不同的一致性。对于关系型数据库,要求更新过的数据能被后续的访问都能看到,这是强一致性。如果能容忍后续的部分或者全部访问不到,则是弱一致性。如果经过一段时间后要求能访问到更新后的数据,则是最终一致性。
最终一致性根据更新数据后各进程访问到数据的时间和方式的不同,又可以区分为:

因果一致性(CAUSAL CONSISTENCY)

如果进程A通知进程B它已更新了一个数据项,那么进程B的后续访问将返回更新后的值,且一次写入将保证取代前一次写入。与进程A无因果关系的进程C的访问遵守一般的最终一致性规则。“读己之所写(READ-YOUR-WRITES)”一致性。当进程A自己更新一个数据项之后,它总是访问到更新过的值,绝不会看到旧值。这是因果一致性模型的一个特例。会话(SESSION)一致性。这是上一个模型的实用版本,它把访问存储系统的进程放到会话的上下文中。只要会话还存在,系统就保证“读己之所写”一致性。如果由于某些失败情形令会话终止,就要建立新的会话,而且系统的保证不会延续到新的会话。单调(MONOTONIC)读一致性。如果进程已经看到过数据对象的某个值,那么任何后续访问都不会返回在那个值之前的值。单调写一致性。系统保证来自同一个进程的写操作顺序执行。要是系统不能保证这种程度的一致性,就非常难以编程了。上述最终一致性的不同方式可以进行组合,例如单调读一致性和读己之所写一致性就可以组合实现。并且从实践的角度来看,这两者的组合,读取自己更新的数据,和一旦读取到最新的版本不会再读取旧版本,对于此架构上的程序开发来说,会少很多额外的烦恼。
从服务端角度,如何尽快将更新后的数据分布到整个系统,降低达到最终一致性的时间窗口,是提高系统的可用度和用户体验非常重要的方面。对于分布式数据系统:
N — 数据复制的份数,W — 更新数据是需要保证写完成的节点数,R — 读取数据的时候需要读取的节点数如果W+R>N,写的节点和读的节点重叠,则是强一致性。例如对于典型的一主一备同步复制的关系型数据库,N=2,W=2,R=1,则不管读的是主库还是备库的数据,都是一致的。
如果W+R<=N,则是弱一致性。例如对于一主一备异步复制的关系型数据库,N=2,W=1,R=1,则如果读的是备库,就可能无法读取主库已经更新过的数据,所以是弱一致性。
对于分布式系统,为了保证高可用性,一般设置N>=3。不同的N,W,R组合,是在可用性和一致性之间取一个平衡,以适应不同的应用场景。
如果N=W,R=1,任何一个写节点失效,都会导致写失败,因此可用性会降低,但是由于数据分布的N个节点是同步写入的,因此可以保证强一致性。如果N=R,W=1,只需要一个节点写入成功即可,写性能和可用性都比较高。但是读取其他节点的进程可能不能获取更新后的数据,因此是弱一致性。这种情况下,如果W<(N+1)/2,并且写入的节点不重叠的话,则会存在写冲突

posted on 2017-07-10 17:14  alex5211314  阅读(134)  评论(0编辑  收藏  举报

导航