整理笔记 - CAP理论和分布式系统设计

Posted DataFlow范式

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了整理笔记 - CAP理论和分布式系统设计相关的知识,希望对你有一定的参考价值。

引子

关于CAP理论和分布式系统这个话题,被人谈论的太多了,甚至有一些还牵扯到是非非,恩恩怨怨,爱恨情仇,真真...,停停停,打住。其实这方面还是有深度的。笔者不会从什么前世今生胡扯一通,而是静下心来参考已有大神的理解,并与自己观点一致的方面进行总结,便于需要时再回来查阅。

其实也不是无缘无故提起这个话题,而且最近一段时间,笔者在刷 Spring 2018 课程,https://pdos.csail.mit.edu/6.824/schedule.html ,关于分布式系统工程的课程,这里就不聊这个课程了,以后再慢慢扩展。

为了引入分布式系统和CAP理论,我们先来看一下高可用。

高可用性

先来一个标准(xx百科,自由的百科全书):

高可用性(high availability,HA),指系统无中断地执行其功能的能力,代表系统的可用性程度,是进行系统设计时的准则之一。高可用性系统与构成该系统的各个组件相比可以更长时间运行。

高可用性通常通过提高系统的容错能力来实现。定义一个系统怎样才算具有高可用性往往需要根据每一个案例的具体情况来具体分析。

其度量方式,是根据系统损害、无法使用的时间,以及由无法运作恢复到可运作状况的时间,与系统总运作时间的比较。

计算公式为: 

A(可用性),MTBF(平均故障间隔),MDT(平均修复时间) 在线系统和执行关键任务的系统通常要求其可用性要达到5个9标准(99.999%)。

可用性 年故障时间
99.9999% 32秒
99.999% 5分15秒
99.99% 52分34秒
99.9% 8小时46分
99% 3天15小时36分

理论的东西,我们理解即可,上面数字我们不看,管他几个9,反正国内大厂大多是4个9、5个9甚至更多9,光缆被挖断忽略。

既然要求高可用,那如何设计呢?上面提到一个关键字容错能力,也就是即使发生错误,系统也不能中断正常服务。下面我们从分布式系统设计着手分析。

全球规模分布式系统

在现代分布式系统中,节点数目是巨大的。在CAP理论的范围内,Michael Stonebraker(这老爷子是数据库专家,获得2014年图灵奖)断言分区必然会发生,并且系统内发生节点失败的机会随着节点数的增加而呈指数级增加。

整理笔记 - CAP理论和分布式系统设计

分布式系统是指联网的计算机通过消息传递来协调行为的系统。在这样的系统中机器之间并发执行,独立故障,并且没有全局状态和全局锁。

为了提高可用性和响应速度,以及满足容灾等需求,这些系统都采用了复制技术。这就带来了服务和数据状态的全球范围内的数据复制和一致性问题。

类似这样的系统有:Dynamo,PNUTS,Cassandra,Megastore,Mesa,Walter,COPS,Spanner,Gemini等。

分布式系统设计的两大原则

分布式系统设计的原则有很多,这里介绍两个基础性的原则,也是比较重要的。

1. 通过复制来提高可用性

通过复制来提高可用性,这是分布式系统设计的首要原则。

从复制类型来看,复制可以分为主动复制和被动复制。

主动复制

整理笔记 - CAP理论和分布式系统设计

在主动复制中,每个客户端请求都由所有服务器处理。

主动复制首先由Leslie Lamport以状态机复制命名。这要求由服务器托管的进程是确定性的。确定性意味着,给定相同的初始状态和请求序列,所有过程将产生相同的响应序列,并且最终处于相同的最终状态。为了使所有的服务器接收到相同的操作序列,一般都使用原子广播协议。

原子广播协议保证所有服务器都接收到消息或没有接收到消息。如果接收到消息,则它们都以相同的顺序接收消息。

主动复制的主要缺点是实际上大多数真实世界的服务器都是非确定性的。

Paxos算法是莱斯利·兰伯特(Leslie Lamport)1990年提出的一种基于消息传递的一致性算法。Paxos算法解决的问题是一个分布式系统如何就某个值(决议)达成一致。

被动复制

整理笔记 - CAP理论和分布式系统设计

在被动复制中,只有一个服务器(称为主服务器)处理客户机请求。处理请求后,主服务器更新其他(备份)服务器上的状态,并将响应发送回客户端。如果主服务器发生故障,则其中一台备份服务器就会接管它。被动复制可以用于非确定性过程。

被动复制与主动复制相比的主要缺点是在失败的情况下,客户端的响应会被延迟。

从进程与系统交互角度来看,复制分为同步复制和异步复制。

同步复制

同步复制是指通过原子写入操作来保证零数据丢失,即完全写入。在本地和远程副本存储的确认之前,写入不被认为是完整的。

整理笔记 - CAP理论和分布式系统设计

异步复制

异步复制是指本地存储确认后,写入即被认为是完整的。远程存储已更新,但可能滞后很小。系统性能会因异步复制而大大提高。但是在丢失本地存储的情况下,远程存储不能保证具有当前的数据副本,并且最近的数据可能会丢失。

整理笔记 - CAP理论和分布式系统设计

关于复制副本的数量,通常我们讨论的都是3个副本,已经满足容灾和高可用的需要。但是在Chubby(Google Chubby,底层一致性实现是以Paxos为基础)、F1(Google的分布式关系型数据库,基于Spanner)和Aurora(Amazon Aurora ,一种与mysql和PostgreSQL兼容的分布式关系数据库)中为了更高的可用性,都采用了5或更多副本。结合同步复制和异步复制,以及链式复制,可以实现混合复制类型的系统,即5个副本中部分是实时同步,其他副本可以采用链式复制的方式,或者paxos多数原则的方式,实现异步复制。异步复制的副本可以作为快照读取的副本和OLAP的副本。

2. 使用CAP理论指导分布式系统设计

来自某百科全书:

在理论计算机科学中,CAP定理(CAP theorem),又被称作布鲁尔定理(Brewer's theorem),它指出对于一个分布式计算系统来说,不可能同时满足以下三点:

  1. 一致性(Consistency) (等同于所有节点访问同一份最新的数据副本)

  2. 可用性(Availability)(每次请求都能获取到非错的响应——但是不保证获取的数据为最新数据)

  3. 分区容错性(Partition tolerance)(以实际效果而言,分区相当于对通信的时限要求。系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在C和A之间做出选择)

复制技术是产生一致性问题的根源。由此带来了分布式系统设计的第二个原则。

整理笔记 - CAP理论和分布式系统设计

对于Internet这样的全球规模的分布式系统,一直以来讨论最多的是AP和CP系统。

整理笔记 - CAP理论和分布式系统设计

CP系统是牺牲可用性的系统。复制同步的协议一般使用严格的法定数协议 (paxos、raft和zab)或者2PC协议。CP类型的系统有MongoDB、HBase和Zookeeper等。

AP系统是牺牲一致性的系统。复制同步的协议一般使用非严格的法定数协议。AP类型的系统有CouchDB、Cassandra和Amazon Dynamo等。

以前笔者对CA系统也没有过多的关注,跟随大部分人思路认为CAP理论不可兼得,没有细究更深缘由。后来参考网上资料,发现理解还是有偏差的,或者不全面。下面参考资料整理一下。

那么究竟有没有CA系统?如果有,如何实现CA系统?下面进行阐述。

重新理解CAP

1. CAP三者并不对等:P是基础,CA之间权衡

在全球规模的分布式系统环境下,网络分区是一个自然的事实。在这样的情况下,就会存在两种理解观点:

  • 因为分区是必然的,系统设计时,只能实现AP和CP系统,CA系统是不可能的。

  • 从技术上来说,分区确实会出现,但从效果来说,或者从概率来说,分区很少出现,可以认为系统不会发生分区。由于分区很少发生,那么在系统不存在分区的情况下没什么理由牺牲C或A。

从更广阔的分布式计算理论背景下审视CAP理论,可以发现C,A,P三者并不对等。

CAP之父在《Spanner, TrueTime and the CAP Theorem》一文中写道:

如果说Spanner真有什么特别之处,那就是谷歌的广域网。Google通过建立私有网络以及强大的网络工程能力来保证P,在多年运营改进的基础上,在生产环境中可以最大程度的减少分区发生,从而实现高可用性。

从Google的经验中可以得到的结论是,CAP理论可能影响我们判断方向,CAP三者之间其实并不对称,C和A不是权衡P的原因。提高一个系统的容错能力,或者说提高P(分区容忍能力)是通过提高基础设施的稳定性来获得的,而不是通过降低C和A来获得的。也就说牺牲C和A也不能提高P。

还有一种说法是,放弃C不是为了获得A,而是为了低延迟。PNUTS为了降低WAN上的消息事务的延迟(几百毫秒,对于像亚马逊和雅虎这样的企业需要实施的许多Web应用程序来说,成本太高),采用放弃一致性的做法。

而CA是系统的刚性强需求,但是CA两者也不对等。系统无论如何要保证一致性(无论事先还是事后,这是系统设计的最大不变性约束,后文会详述),在这个前提下,可以谈谈可用性的程度。Google的Spanner就是这样的思路。

总结:P是一个自然的事实,CA是强需求,三者并不对等。

2. 保证不发生分区,CA也不容易兼得

在分布式系统中,安全性(Safety),活性(Liveness)是本质需求,并且广泛的研究结果是分布式系统中一直存在一个广泛意义的trade-off:

在不可靠的分布式系统中无法同时实现安全性和活性。分布式系统的设计中充满了安全性和活性的trade-off,FLA著名的论文《Impossibility of Distributed Consensus with One Faulty Process》就是说我们不可能设计一个算法既可以绝对保证一致性(安全性)又无需时间等待的实现一致性(活性)。

CAP就是这个trade-off的的集中体现。分别对应于:

  • Safety:非正式的说,一个算法没有任何坏的事情发生,那么该算法就是安全的。CAP中的C就是典型的safety属性:所有对客户的响应都是正确的。

  • Liveness:相反,一个算法最终有有一些好的事情发生,那么该算法就是活性的。CAP中的A就是典型的liveness属性:所有的客户最终都会收到回应。

FLA中的故障是指:Unreliable:有很多种方式可以让一个系统不可靠,CAP中的P,以及其他故障(系统崩溃、消息丢失、恶意攻击和拜占庭故障等)。

所以,CAP理论可以说是FLA不可能性的不同表达方式。P只是Unreliable的一种极端形式而已。在Google的Chubby文章中,指出Paxos协议维护系统的safety,引入时钟来保证liveness,由此克服FLA的不可能性。实际上,基本的Paxos协议可以保证值一旦被选出后就一定不会改变,但不能保证一定会选出值来。换句话说,这个投票算法不一定收敛。所以在系统设计时,Paxos算法的使用是有条件的。

在数据库领域,CAP也正是ACID和BASE长期博弈权衡的结果。ACID伴随数据库的诞生定义了系统基本设计思路,所谓先入为主。2000年左右,随着互联网的发展,高可用的话题被摆上桌面,所以提出了BASE。从此C和A的取舍消长此起彼伏,其结晶就是CAP理论。

整理笔记 - CAP理论和分布式系统设计

从ACID和BASE来说,ACID是为了保证一致性而诞生,因而侧重一致性;BASE是为了高可用系统的设计而诞生,因而侧重可用性。在分解C和A的情况时,肯定要涉及P,所以CAP理论统一了这一切。

我们在分布式系统设计的两大原则中讨论过保持一致性的手段:同步复制和异步复制,结合复制协议的各种模式,请参考业界流行的下表。例如简单满足了C,但延迟升高了,吞吐量下来了,还有什么可用性?我觉得延迟是包含在可用性的范围内的,不可用就是延迟的极大极限值。

 
   
   
 
  1. Backups:

  2. Make a copy


  3. M/S:

  4. Master/Slave replication: Usually asynchronous


  5. MM:

  6. Multi-master replication: concurrent writes,Usually asynchronous,eventual consistency


  7. 2PC:

  8. Two Phase Commit: Centralized consensus protocol


  9. Paxos:

  10. Protocol similar to 2PC/3PC: Decentralized, distributed consensus protocol

有个新的理论叫做PACELC,大体意思如下:

如果有分区partition (P),系统就必须在availability 和consistency (A and C)之间取得平衡; 否则else (E) 当系统运行在无分区情况下,系统需要在 latency (L) 和 consistency (C)之间取得平衡。

可用性并不是简单的网络连通,服务可以访问,数据可以读取就是可用性,对于互联网业务,可用性是完整的用户体验,甚至会延伸到用户现实生活中(补偿)。有的系统必须容忍大规模可靠分布式系统中的数据不一致,其中原因就是为了在高并发条件下提高读写性能。

必须容忍大规模可靠分布式系统中的数据不一致,有两个原因:在高并发条件下提高读写性能,并要区分物理上导致的不一致和协议规定的不一致:

  • 节点已经宕机,副本无法访问(物理)

  • 法定数模型会使部分系统不可用的分区情况,即使节点已启动并运行(paxos协议)

  • 网络断开,节点孤立(物理)

所以,CA系统才是真正的难点。宣称是CA系统的,目前有两家:一家是Google的Spanner(Spanner宣称是CA系统存在的真正问题是用户对可用性的态度,即用户是否会对可用性吹毛求疵),一家是Alibaba的OceanBase。

3. 发生分区时,也不要随意牺牲CA

虽然架构师仍然需要在分区的前提下对数据一致性和可用性做取舍,但具体如何处理分区和恢复一致性,这里面有不计其数的变通方案和灵活度。

当发生分区时,系统设计人员不应该盲目地牺牲一致性或可用性。当分区存在或可感知其影响的情况下,就要预备一种策略去探知分区并显式处理其影响。这样的策略应分为三个步骤:探知分区发生,进入显式的分区模式以限制某些操作,启动恢复过程以恢复数据一致性并补偿分区期间发生的错误。

这一切都需要在系统设计之初考虑到,并在测试时模拟各种故障保证覆盖到你的测试点。

构建高度稳健的基础设施永远是第一要务,所以我不认为网络分区与CA属性是对等的。

分布式系统的难题

分布式并发读写事务

如果下图所示,进程A和B是地理上分布的两个进程,A进程对系统发起写操作,B进程同时并发的读取。

>>> 1. 首先第一个难题,是否允许任意节点并发可写 在Google的F1、蚂蚁的OceanBase和亚马逊的Aurora中,都是指定一个写节点或者更新节点的(OceanBase各个节点之间完全对等)。


>>> 2. 第二个难题,是否支持读写并发 这里涉及到读写一致性的问题。

比如上图,当用户A在写入系统的时候,用户B的读取情况是什么样子的?是读取数据的上一个快照,还是读取A写入的最新数据?用户A和用户B在读取的过程中如何加锁?特别跨越广域网的不同的数据中心的时候。这里tricky的地方在于是否要对整个数据加读写锁。目前我看Google的主要方法是目前A进程在写的时候采用多版本数据存储,并保证分布式事务。B进程可以实现无锁的快照读取。基于中心节点的机制,如果读写冲突或者写写冲突,会被锁机制拒绝,用户操作失败。

这个问题还涉及到分布式事务的隔离性问题。传统数据库ANSI SQL标准定义了四个隔离等级:

  • 未提交读

    一个事务可以读任何已提交或未提交的数据。这可以通过读操作不需要请求任何锁来实现。

  • 已提交读

    一个事务可以读任何已提交的数据。对于同一个对象的重复读可能导致读到不同版本的数据。实现方式是,读数据前必须首先获得一个读操作锁,一旦数据读取之后该锁被立即释放。

  • 可重复读

    一个事务只能读取一个已提交数据的一个版本。一旦该事务读取了一个对象,那么它将只能读取该对象的同一个版本。实现方式是,事务在请求读数据之前必须获得一个锁,并且保持该锁直到事务结束。

  • 可串行化

    保证完全的可串行化。

分布式数据库如何满足,是设计分布式系统的首要问题。

>>> 3. 第三个难题,元数据如何保存 用户A或B在读取或者写入系统的时候,如何获得数据的版本和时间戳?

如果元数据在异地数据中心,获得元数据将会有一个广域网延迟到时间开销。我认为Google的truetime是用了物理时间代替经典的逻辑时钟,并且作为副本的时间戳,也就是版本号,这样基于truetime的精巧API设计,让版本号和物理时间线性对齐,无需去查询副本的元数据信息。相反的例子是Google Chubby提到的:在一个已经初步成熟的复杂应用程序的每次操作中引入版本号的开销是非常大的。所以后来退一步求其次,Chubby采用了仅仅在所有使用锁的操作中引入版本号。

>>> 4. 第四个难题,在读写事务期间,节点故障 注意,这里是指任意节点故障,包括一次事务中的leader节点,参与节点,以及用户节点。特别是用户所在的节点故障要求系统必须有加锁租约等自恢复机制。

关于锁的设计,在CAP的范围内,需要满足三点:

  • 锁对象信息的要写入多副本以应对故障

  • 不同对象的锁信息需要分布化和负载均衡

  • 锁信息写入持久化存储系统

注意,这里锁的概念和Google Chubby中锁的概念是不同的。这里是一种粗粒度的锁(leader选举),其作者也不建议把Chubby应用在细粒度锁(事务更新)的场景中。这两种锁在CAP的范围内使用时值得非常细心的研究和讨论,特别是在分布式数据库领域内。

>>> 5. 第五个难题,嵌套事务或者链式事务 嵌套事务和链式事务都是事务中的一种,事务还包括分布式事务,扁平事务等。这里笔者给大家解释一下嵌套和链式事务。

嵌套事务:

  • 嵌套事务是一个层次结构框架,由一个顶层事务控制着各个层次的事务,顶层事务之下嵌套的事务被称为子事务

  • 嵌套事务是由若干事务组成的一棵树,子树既可以是嵌套事务也可以是扁平事务

  • 处在叶节点的事务是扁平事务,但是每个事务从根到叶节点的距离可以是不同的

  • 子事务既可以提交也可以回滚。但是它的提交操作并不马上生效。除非其父事务已经提交。因此可以推论出,任何子事务都在顶层事务提交后才真正的提交

  • 树中的任意事务回滚会引起它的所有子事务一同回滚,故子事务仅保留 ACI 特性而不具有 D 特性

  • 实际的工作是交由叶子节点完成,即只有叶子节点的事务才能才能访问数据库、发送信息、获取其他类型的资源,而高层的事务仅负责逻辑控制

链式事务:

  • 链事务的思想是:在提交一个事务时,释放不需要的数据对象,将必要的处理上下文隐式地传给下一个要开始的事务

  • 提交事务操作和开始下一个事务操作 将合并为一个原子操作,这意味着下一个事务将看到上一个事务的结果,就好像一个事务中进行的一样

  • 链事务与带有保存点的扁平事务不同的是,带有保存点的扁平事务能回滚到任意正确的保存点,而链事务中的回滚仅限当前事务,即只能恢复到最近的一个保存点

  • 对于锁的处理,两者也不相同,锁事务在执行 COMMIT 后即释放了当前所持有的锁,而带有保存点的扁平事务不影响迄今为止所持有的锁

由于这部分的事务类型,还是比较复杂的,感兴趣的朋友可以查阅资料,深入学习。

总结

笔者通过资料整理,涉及高可用系统、分布式系统设计原则、CAP理论重新理解等方面进行总结,一些内容在原材料基础上进行删减或修订,部分内容保留。

最后再整理一下:

  1. CAP中的三个因素并不对等,P是基础,CA之间需要权衡。系统设计不是三选二的取舍。

  2. 延迟作为可用性的指标和体现,系统设计通常需要在C和延迟之间权衡。

  3. CAP真正的权衡在CA之间,系统设计需要细心分解C和A(这个部分后续再做细讲,本篇文章不涉及),不同的系统有不同的需求。本文在对CAP分解的基础上,提供了系统设计的一些思考方法。未来系统的设计必然是要满足多种一致性模型和多种可用性需求(例如Azure Cosmos DB支持多种一致性模型)。

  4. 针对分区设计数据不变性,记录所有的分区历史,这让分区合并之后的补偿有据可依。

  5. 在第3点的基础上,未来分布式系统需要从整体上考虑,即需要考虑IT基础设施,也要考虑应用的适应和配合,以及人类社会中的法律和补偿。

  6. 本文讨论了在CAP范围内,分布式系统设计的一些难点。

注意本文的分区和数据库的分片(分区)不是一个概念。

参考文献

  • 喔家ArchiSelf<<cap理论与分布式系统设计 style="box-sizing: border-box;">></cap理论与分布式系统设计>

  • Immutability Changes Everything

  • Transactions Across Datacenters

  • https://snarfed.org/transactionsacrossdatacenters_io.html

  • From the Ground Up: Reasoning AboutDistributed Systems in the Real World

  • http://bravenewgeek.com/from-the-ground-up-reasoning-about-distributed-systems-in-the-real-world/

  • How Google Serves Data From MultipleDatacenters

  • http://highscalability.com/blog/2009/8/24/how-google-serves-data-from-multiple-datacenters.html

  • Everything You Know About Latency Is Wrong

  • http://bravenewgeek.com/everything-you-know-about-latency-is-wrong/

  • You Can’t Sacrifice Partition Tolerance

  • https://codahale.com/you-cant-sacrifice-partition-tolerance/

  • Distributed systems for fun and profit

  • http://book.mixu.net/distsys/single-page.html

  • Distributed Systems and the CAP Theorem

  • http://ternarysearch.blogspot.fi/2014/03/distributed-systems-and-cap-theorem.html

  • CAP Twelve Years Later: How the"Rules" Have Changed

  • http://www.infoq.com/cn/articles/cap-twelve-years-later-how-the-rules-have-changed

  • 《How to beat the CAPtheorem》

  • A Conversation with Bruce Lindsay

  • http://nathanmarz.com/blog/how-to-beat-the-cap-theorem.html

  • http://ternarysearch.blogspot.fi/2014/03/distributed-systems-and-cap-theorem.html

  • Rethinking Eventual Consistency: Can we dobetter?

  • Rethinking Eventual Consistency

  • Perspectives on the CAP Theorem

  • Spanner, TrueTime & The CAP Theorem

  • Eventual Consistent Databases: State of theArt

  • Incremental Consistency Guarantees forReplicated Objects

  • http://martin.kleppmann.com/2015/05/11/please-stop-calling-databases-cp-or-ap.html

  • https://github.com/aphyr/partitions-post

  • https://ying-zhang.github.io/cloud/2017/spanner-truetime-cap/index.html


以上是关于整理笔记 - CAP理论和分布式系统设计的主要内容,如果未能解决你的问题,请参考以下文章

架构设计中的 CAP 和 BASE 理论

cap理论与分布式事务的解决方案

分布式系统必备:CAP理论

面试准备 - 分布式系统 CAP 理论

图文详解分布式架构设计基础理论CAP

大数据开发者应该知道的分布式系统 CAP 理论