undefined

CAP理论

Consistency、Availability、Partition Tolerance

Robert Greiner(http://robertgreiner.com/about/)的文章作为基础,其中有两版解释:

第一版:对于一个分布式计算系统,不可能同时满足一致性(Consistence)、可用性(Availability)、分区容错性(Partition Tolerance)三个设计约束。

第二版:在一个分布式系统(指互相连接并共享数据的节点的集合)中,当涉及读写操作时,只能保证一致性(Consistence)、可用性(Availability)、分区容错性(Partition Tolerance)三者中的两个,另外一个必须被牺牲。这里的共享数据的节点集合指的是同一份数据在多个节点都有,但并不一定在所有节点都有。

对比两个版本的定义,有几个关键的差异点:

  • 第二版定义了什么才是 CAP 理论讨论的分布式系统。互连( interconnected )和 共享数据( share data)。因为分布式系统并不一定会互联和共享数据,比如 Memcache 集群,相互之间就没有连接和共享数据,所以不符合 CAP 理论讨论的对象
  • 第二版强调了 write/read pair,CAP 关注的是对数据的读写操作,而不是分布式系统的所有功能。例如:Zookeeper 的选举机制就不是 CAP 讨论的对象

一、名词解释

1. 一致性:

第一版:所有节点在同一时刻都能看到相同的数据。
第二版:对某个指定的客户端来说,读操作保证能够返回最新的写操作结果。

在事务执行过程中,系统其实处于一个不一致的状态,不同的节点的数据并不完全一致,第一版解释不严谨,第二版强调客户端读操作能够获取最新的写结果就没有问题,事务在执行过程中,客户端无法读取到未提交/未写入的数据。

2. 可用性

第一版:每个请求都能得到成功或者失败的响应
第二版:非故障的节点在合理的时间内返回合理的响应(不是错误和超时的响应)

第一版定义太广泛,超时、错误、异常、结果不准确都算失败。第二版解释明确了不能超时、不能出错、结果是合理的。合理不一定是正确

3. 分区容忍性

第一版:出现消息丢失或者分区错误时系统能够继续运行。
第二版:当出现网络分区后,系统能够继续“履行职责”。

网络分区指的是:两个节点无法进行交互,形成了两个分区。也可以说丢失一个节点发给另一个节点的任意多的消息

主要差异在第一版强调运行,只要系统不宕机,都可以说系统在运行,但返回错误、拒绝服务都算是在运行。而第二版强调发挥作用、履行职责,这点和可用性一脉相承,只有返回合理的结果才是工作。第一版说丢包导致的分区,定义狭隘,因为丢包只是网络故障的一种,第二版说明现象是发生了分区现象,不管什么原因,可能是丢包、也可能是连接中断,还可能是拥塞,只要导致了网络分区,都算。

二、CAP 应用

放到分布式环境下来思考,我们会发现必须选择 P(分区容忍)要素,因为网络本身无法做到 100% 可靠,有可能出故障,所以分区是一个必然的现象。如果我们选择了 CA 而放弃了 P,那么当发生分区现象时,为了保证 C,系统需要禁止写入,当有写入请求时,系统返回 error(例如,当前系统不允许写入),这又和 A 冲突了,因为 A 要求返回 no error 和 no timeout。因此,分布式系统理论上不可能选择 CA 架构,只能选择 CP 或者 AP 架构。

1. CP 架构

为了保证一致性,当发生分区时,node1 节点的数据已经更新了,客户端可以从 node1 上获取更新数据。由于node1 和 node2 之间的复制通道中断,node2 的数据未更新,那么客户端访问 node2 的时候应该返回 error,提示客户端系统发生了问题。保证了一致性,但违背了可用性要求

2. AP 架构

和 CP 架构一样的场景,node1 和 node2 的数据即使不一致,也会返回给客户端。不满足一致性要求,虽然 node2 返回不准确的结果,但是是一个合理的结果,满足可用性。

三、CAP关键细节(重点理解)

  • CAP关注的粒度是数据,而不是整个系统

在实际的设计过程中,每个系统不可能只处理一种数据,而是包含多种类型的数据,有的数据必须选择 CP,有的数据必须选择 AP。比如:用户管理系统包含账号数据(用户ID、密码)、用户信息数据(昵称、兴趣、自我介绍等)。通常,用户账号数据会选择 CP,而用户信息会选择 AP。

因此做设计时,不能从整个系统去选择 CP 还是 AP。我们需要将系统内的数据按照不同的应用场景和要求进行分类,每类数据选择不同的策略(CP 还是 AP),而不是直接限定整个系统所有数据都是同一策略

  • CAP是忽略网络延迟的

布鲁尔在定义一致性时,并没有将延迟考虑进去。也就是说,希望事务提交时,数据能够瞬间复制到所有节点。但是从 node1 复制数据到 node2 总要花费一定时间,这也就意味着 CAP 理论中的 C 在实践中不可能完美实现,在数据复制的过程中,节点之间的数据并不一致。

在技术无法做到完全的一致性,而业务上必须要求一致性。因此比如账号数据,理论上要求选择 CP 而实际上 CP 都做不到,只能选择 CA。
也就是说,只能单点写入,其他节点做备份,无法做到分布式情况下多点写入。注意:这并不意味着系统无法应用分布式架构,只是说账号密码这类数据无法做分布式,但是系统整体还是可以应用分布式架构。

那么单点写入读取,解决了一致性,可用性以及性能无法保证。因此我们一般可以将用户 id 为 0-100 的数据存在 node1,将用户 id 为 100-200 的数据存储在 node2,client 根据用户 id 来决定访问那个 node。这样缓解了性能问题,但是还是没有解决某个节点故障时,这个节点上的用户无法读写的问题。但是站在整体上来看,这种设计可以降低节点故障时受影响的用户的数量和范围。

  • 正常运行情况下,不存在 CP 和 AP 的选择,可以同时满足 CA

CAP 理论告诉我们分布式系统智能选择 CP 或者 AP,但这里的前提是系统发生了分区现象。如果系统没有发生分区现象,也就是节点间的网络连接一切正常,我们应该 C 和 A 都保证。在做架构设计时既要考虑分区发生时选择 CP 还是 AP,也要考虑分区没有发生时如何保证 CA

  • 放弃不等于什么都不做,需要为分区恢复后做准备

CAP 理论告诉我们三者之能取两个,需要“牺牲”另外一个。但时牺牲不等于什么都不做,是希望可以达到 99.999% 或其他。我们可以在分区期间进行一些操作,从而让分区故障解决后,系统能够重新达到 CA 的状态

最典型的就是分区期间记录一些日志,当分区故障解决后,系统根据日志进行数据恢复,使得重新达到 CA 状态。例如对于用户账户数据,假设我们选择了 CP,当分区选择了 CP,则分区发生后,node1 可以注册新用户,node2 不可以(不满足可用性),此时 node1 就可以将新注册但未同步到 node2 的用户记录到日志中。当分区恢复后,node1 读取日志中的记录,同步给 node2,当同步完成后,node1 和 node2 就达到了同时满足 CA 的状态。

四、对比 ACID 和 BASE

ACID 是数据库管理系统为了保证事务的正确性而提出的一个理论

  • Atomicity(原子性)一个事务中的所有操作,要么全部完成、要么全部不完成
  • Consistency(一致性)在事务开始之前和事务结束以后,数据库的完整性没有被破坏
  • Isolation(隔离性)数据库允许多个并发事务同时对数据进行读写和修改的能力。隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)
  • Durability(持久性)事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。

BASE 是指基本可用(Basically Available)、软状态( Soft State)、最终一致性( Eventual Consistency),核心思想是即使无法做到强一致性(CAP 的一致性就是强一致性),但应用可以采用适合的方式达到最终一致性。

  • 基本可用:分布式系统在出现故障时,允许损失部分可用性,即保证核心可用。关键词是 “部分” 和 “核心”,需要具体选择哪些作为可以损失的业务,哪些是必须保证的业务
  • 软状态:允许系统存在中间状态,而该中间状态不会影响系统整体可用性。这里的中间状态就是 CAP 理论中的数据不一致
  • 最终一致性:系统中的所有数据副本经过一定时间后,最终能够达到一致的状态。关键词是 “一定时间” 和 “最终”,“一定时间”和数据的特性是强关联的,不同的数据能够容忍的不一致时间是不同的。“最终”的含义就是不管多长时间,最终还是要达到一致性的状态。

BASE 理论本质上是对 CAP 的延伸和补充,如下:

  • CAP 理论忽略延时,实际应用中延时无法避免。意味着完美的 CP 场景不存在,因此 CP 方案实际上是最终一致性,只是一定时间是指几毫秒而已
  • AP 方案中牺牲一致性只是指分区期间,而不是永远放弃一致性。分区期间牺牲一致性,但分区故障恢复后,系统应该达到最终一致性。