CAP

CAP

CAP 定理又被称作布鲁尔定理,是加州大学的计算机科学家布鲁尔在 2000 年提出的一个猜想;2002 年,麻省理工学院的赛斯·吉尔伯特和南希·林奇发表了布鲁尔猜想的证明,使之成为分布式计算领域公认的一个定理。CAP 定理认为,在分布式系统中,系统的一致性(Consistency)、可用性(Availability)、分区容忍性(Partition tolerance)三者不可能同时兼顾。而由于网络通信的不稳定性,分区容忍性是必须要保证的,因此 CAP 理论在实践中往往指明了在设计应用的时候就需要在一致性和可用性之间权衡选择。互联网应用比企业级应用更加偏向保持可用性,因此通常用最终一致性代替传统事务的 ACID 强一致性。

布鲁尔在提出 CAP 猜想时并没有具体定义 Consistency、Availability、Partition Tolerance 这 3 个词的含义,不同资料的具体定义也有差别:

  • 一致性(Consistency):所有节点都能访问同一份最新的副本数据。

  • 可用性(Availability):每个请求都能接收到一个响应,无论响应成功或失败,而不应该是网络超时、连接断开等非服务程序答复。

  • 分区容忍性(Partition Tolerance):除了整个网络的故障外,其他的故障(集)都不能导致整个系统无法正确响应。

CAP 特性定义

Consistency | 一致性

一致性又称为原子性或者事务性,对某个指定的客户端来说,读操作保证能够返回最新的写操作结果。一致性表示一个事务的操作是不可分割的,要不然这个事务完成,要不然这个事务不完成,不会出现这个事务完成了一半这样的情况,也就不会读取到脏数据。传统的 ACID 数据库是很少存在一致性问题的,因为数据的单点原因,数据的存取又具有良好的事务性,不会出现读写的不一致。

而在分布式系统中,经常出现的一个数据不具有一致性的情况是读写数据时缺乏一致性。比如两个节点数据冗余,第一个节点有一个写操作,数据更新以后没有有效的使得第二个节点更新数据,在读取第二个节点的时候就会出现不一致的问题出现。这里并不是强调同一时刻拥有相同的数据,对于系统执行事务来说,在事务执行过程中,系统其实处于一个不一致的状态,不同的节点的数据并不完全一致。

一致性强调客户端读操作能够获取最新的写操作结果,是因为事务在执行过程中,客户端是无法读取到未提交的数据的。只有等到事务提交后,客户端才能读取到事务写入的数据,而如果事务失败则会进行回滚,客户端也不会读取到事务中间写入的数据。

Availability | 可用性

可用性主要是指系统能够很好的为用户服务,不出现用户操作失败或者访问超时等用户体验不好的情况;所谓非故障的节点在合理的时间内返回合理的响应,而不是错误和超时的响应。这里强调的是合理的响应,不能超时,不能出错。注意并没有说正确的结果,例如,应该返回 Java 但实际上返回了 Go,肯定是不正确的结果,但可以是一个合理的结果。

可用性通常情况下可用性和分布式数据冗余,负载均衡等有着很大的关联。

Partition Tolerance | 分区容忍性

分区容忍性即指当出现网络分区后,系统能够继续履行职责。这里网络分区是指:一个分布式系统里面,节点组成的网络本来应该是连通的。然而可能因为一些故障,譬如节点间网络连接断开、节点宕机等,使得有些节点之间不连通了,整个网络就分成了几块区域,数据就散布在了这些不连通的区域中。好的分区容忍性要求能够使应用虽然是一个分布式系统,而看上去却好像是在一个可以运转正常的整体。比如现在的分布式系统中有某一个或者几个机器宕掉了,其他剩下的机器还能够正常运转满足系统需求,这样就具有好的分区容忍性。

分区容忍性的典型场景,譬如数据库有两个拷贝在两个不同的数据中心,当数据被写到一个数据中心的时候,他也一定要被写到另一个数据中心。那么现在假设网络中断了,这就是我们所说的网络分区的意思:

我们可以有如下的两种策略:

  • 应用还是被允许写到数据库,所以两边的数据库还是完全可用的。但是一旦两个数据库之间的网络中断了,任何一个数据中心的写操作就不会在另一个数据中心出现,这就违反了一致性。

  • 如果你不想失去一致性,你就必须保证你的读写操作都在同一个数据中心,即 Leader 或者 Master 节点。另一个数据中心,因为网络故障不能被更新,就必须停止接收读写操作,直到网络恢复,两边数据库又同步了之后。所以虽然非 Leader 的数据库在正常运行着,但是他却不能处理请求,这就违反了可用性定义。

CAP 模型实践

实际应用中的可用性和 CAP 可用性并不相同。你应用的可用性多数是通过 SLA 来衡量的(比如 99.9%的正确的请求一定要在一秒钟之内返回成功),但是一个系统无论是否满足 CAP 可用性其实都可以满足这样的 SLA。实际操作中,跨多个数据中心的系统经常是通过异步备份(Asynchronous Replication)的,所以不是可线性化的。但是做出这个选择的原因经常是因为远距离网络的延迟,而不是仅仅为了处理数据中心的网络故障。

互联网行业模型。不同的业务类型要求不同的 CAP 模型,CA 适用于支付、交易、票务等强一致性的行业,宁愿业务不可用,也不能容忍脏数据。互联网业务对于强一致性不高,发个帖子要审核,没人看到无所谓。发一个音频要进行编码审核才能看到。

CA 模型

CA 模型即保证了可用性与强一致性,牺牲了分区容忍性。比如 MySQL Cluster 集群,提供两阶段提交事务方式,保证各节点数据强一致性。MySQL 的集群无法忍受脱离集群独立工作,一旦和集群脱离了心跳,节点出问题,导致分布式事务操作到那个节点后,整个就会失败,这是分区容忍性的牺牲。

当发生分区现象时,为了保证一致性,系统需要禁止写入。当有写入请求时,系统返回 Error(例如,当前系统不允许写入),不过这就又和可用性相冲突,因为可用性要求不可以返回错误或超时。因此分布式系统理论上不可能选择 CA 模型。

CP 模型

CP 模型即选择了强一致性与分区容忍性,牺牲了可用性。假设系统中存在节点 Node1 与 Node2,因为 Node1 节点和 Node2 节点连接中断导致分区现象,Node1 节点的数据已经更新到 y,但是 Node1 和 Node2 之间的复制通道中断,数据 y 无法同步到 Node2,Node2 节点上的数据还是旧数据 x。这时客户端 C 访问 Node2 时,Node2 需要返回 error,提示客户端系统现在发生了错误,这种处理方式违背了可用性的要求,因此 CAP 三者只能满足 CP。

CP 模型譬如 Redis 客户端 Hash 和 Twemproxy 集群,各 Redis 节点无共享数据,所以不存在节点间的数据不一致问题。其中节点宕机了,都会影响整个 Redis 集群的工作。当 Redis 某节点失效后,这个节点里的所有数据都无法访问。如果使用 3.0 Redis Cluster,它有中心管理节点负责做数据路由。

AP 模型

AP 模型牺牲了一致性,譬如同样是 Node2 节点上的数据还是旧数据 x,这时客户端 C 访问 Node2 时,Node2 将当前自己拥有的数据 x 返回给客户端了。而实际上当前最新的数据已经是 y 了,这就不满足一致性(Consistency)的要求了,因此 CAP 三者只能满足 AP。

值得一提的是,这里 Node2 节点返回 x,虽然不是一个正确的结果,但是一个合理的结果,因为 x 是旧的数据,并不是一个错乱的值,只是不是最新的数据。

譬如在 Cassandra 集群时,数据可以访问,数据能备份到各个节点之间,其中一个节点失效的话,数据还是可以出来的。而分布式事务的各个节点更新了提交了只是其中一部分节点,底层继续同步,这是 AP 模型。