引言

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

概述

CAP 理论对分布式系统的特性做了高度抽象,形成了三个指标:

  • 一致性(Consistency)
  • 可用性(Availability)
  • 分区容错性(Partition Tolerance)

CAP定理我们常见的描述是一个分布式系统最多只能同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance)这三项中的两项

但布鲁尔在提出 CAP 猜想的时候,并没有详细定义 Consistency、Availability、Partition Tolerance 三个单词的明确定义。

因此如果初学者去查询 CAP 定义的时候会感到比较困惑,因为不同的资料对 CAP 的详细定义有一些细微的差别。

这里不展开讲述这些细微差别,本文选取Robert Greiner的文章来作为参考。

CAP Theorem: Explained

CAP Theorem: Revisited

需要注意CAP Theorem: Explained这篇文章已经被标明outdated(已过时)。网上说的大多数定义是根据第一篇文章来的。

下面带你逐步理解CAP定理中分布式一致性可用性分区容错性这4个关键词。

分布式

我们都知道CAP定理说的是分布式系统下的定理,但是分布式系统有很多类型,有异构的,比如节点之间是上下游依赖的关系,有同构的,比如分区/分片型的、副本型的(主从、多主)。

那么CAP说的分布式包含以上所有吗,网上很少有提到CAP说的分布式系统是否有具体的类型。

CAP Theorem: Explained文中描述CAP定理用的是Distributed systems,也没有具体指明CAP理论下分布式系统的特征,但是在CAP Theorem: Revisited是这样描述CAP的:

in a distributed system (a collection of interconnected nodes that share data), you can only have two out of the following three guarantees across a write/read pair: Consistency, Availability, and Partition Tolerance - one of them must be sacrificed.

翻译过来就是:在一个分布式系统(指互相连接并共享数据的节点的集合)中,当涉及读写操作时,只能保证一致性(Consistence)、可用性(Availability)、分区容错性(Partition Tolerance)三者中的两个,另外一个必须被牺牲。

我们可以看到有两个很明显的差别:

  • a collection of interconnected nodes that share data: 第二篇文章中提到了CAP关心的是互联且共享数据的分布式系统,所以最简单的例如 Memcache 的集群,节点之间就没有连接和共享数据,因此 Memcache 集群这类分布式系统就不符合 CAP 理论探讨的对象;而 MySQL 集群就是互联和进行数据复制的,因此是 CAP 理论探讨的对象。
  • write/read pair: CAP 关注的是对数据的读写操作,而不是分布式系统的所有功能。例如,ZooKeeper 的选举机制就不是 CAP 探讨的对象。

所以CAP定理的分布式系统说的是副本型的分布式系统。

一致性

一致性这个词在不同的环境下有着不同的含义,被极大的滥用了,导致很难理解:

  1. 多副本的一致性
  2. 一致性hash
  3. CAP理论的一致性
  4. ACID里的一致性

而这几个一致性的含义不同。我们接触一致性最多的还是ACID中的一致性。但这和CAP中的一致性完全不是一个东西,我先告诉你ACID的一致性指的是约束一致性,而CAP中的一致性指的是数据正确。我们先来讨论CAP中的一致,最后再向你解释ACID中的一致性。

在 Robert Greiner 的 CAP Theorem: Explained 文中是这样描述一致性的:

[C] Consistency - All nodes see the same data at the same time.

翻译下就是所有节点同时看到相同的数据。

一想不对啊,多节点之前网络传输肯定有延迟啊,这里又要提到个 CAP 的知识点,就是 CAP 是忽略网络延迟的。布鲁尔在定义一致性时,并没有将延迟考虑进去。也就是说,当事务提交时,数据能够瞬间复制到所有节点。

为了帮你理解一致性,我给你举一个具体的例子。比如,2 个节点的 KV 存储,原始的 KV 记录为“X = 1”。

紧接着,客户端向节点 1 发送写请求“SET X = 2”。

如果节点 1 收到写请求后,只将节点 1 的 X 值更新为 2,然后返回成功给客户端。

那么,此时如果客户端访问节点 2 执行读操作,就无法读到最新写入的 X 值,这就不满足一致性了。

如果节点 1 收到写请求后,通过节点间的通讯,同时将节点 1 和节点 2 的 X 值都更新为 2,然后返回成功给客户端。

那么在完成写请求后,不管客户端访问哪个节点,读取到的都是同一份最新写入的数据,这就叫一致性。

一致性这个指标,描述的是分布式系统非常重要的一个特性,强调的是数据正确。也就是说,对客户端而言,每次读都能读取到最新写入的数据。注意,这里说的所有节点同时看到相同的数据,后者要求所有节点数据一致,前者要求客户端看到的一致。而这也是Robert Greiner 在 CAP Theorem: Revisited中提到的一致性:

Consistency - A read is guaranteed to return the most recent write for a given client.

翻译下就是对某个指定的客户端来说,读操作保证能够返回最新的写操作结果。

第一版解释和第二版解释的主要差异点表现在:

  • 第一版从节点 node 的角度描述,第二版从客户端 client 的角度描述。

相比来说,第二版更加符合我们观察和评估系统的方式,即站在客户端的角度来观察系统的行为和特征。

  • 第一版的关键词是 see,第二版的关键词是 read。

第一版解释中的 see,其实并不确切,因为节点 node 是拥有数据,而不是看到数据,即使要描述也是用 have;

  • 第二版从客户端 client 的读写角度来描述一致性,定义更加精确。

第一版强调同一时刻拥有相同数据(same time + same data),第二版并没有强调这点。这就意味着实际上对于节点来说,可能同一时刻拥有不同数据(same time + different data),这和我们通常理解的一致性是有差异的,为何做这样的改动呢?其实在第一版的详细解释中已经提到了,具体内容如下:

A system has consistency if a transaction starts with the system in a consistent state, and ends with the system in a consistent state. In this model, a system can (and does) shift into an inconsistent state during a transaction, but the entire transaction gets rolled back if there is an error during any stage in the process.

翻译下:

如果事务以系统处于一致状态开始,并以系统处于一致状态结束,则系统具有一致性。 在此模型中,系统可以(并且确实)在事务期间转换为不一致状态,但是如果在过程的任何阶段出现错误,则整个事务都会回滚。

这个很好理解,在数据库事务中我们常常遇到这种情况,事务未提交前是可以读到与已提交数据不同的值的。对于系统执行事务来说,在事务执行过程中,系统其实处于一个不一致的状态,不同的节点的数据并不完全一致,因此第一版的解释“All nodes see the same data at the same time”是不严谨的。而第二版强调 client 读操作能够获取最新的写结果就没有问题,因为事务在执行过程中,client 是无法读取到未提交的数据的,只有等到事务提交后,client 才能读取到事务写入的数据,而如果事务失败则会进行回滚,client 也不会读取到事务中间写入的数据。

ACID中的一致性

前文说过CAP的一致性与ACID的一致性不是一个东西,这里补充讲讲

ACID 是数据库管理系统为了保证事务的正确性而提出来的一个理论,ACID 包含四个约束。

  1. Atomicity(原子性)

一个事务中的所有操作,要么全部完成,要么全部不完成,不会在中间某个环节结束。事务在执行过程中发生错误,会被回滚到事务开始前的状态,就像这个事务从来没有执行过一样。

  1. Consistency(一致性)

在事务开始之前和事务结束以后,数据库的完整性没有被破坏。

3. Isolation(隔离性)

数据库允许多个并发事务同时对数据进行读写和修改的能力。隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。

4. Durability(持久性)

事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。

可以看到,ACID 中的 A(Atomicity)和 CAP 中的 A(Availability)意义完全不同,而 ACID 中的 C 和 CAP 中的 C 名称虽然都是一致性,但含义也完全不一样。ACID 中的 C 是指数据库的数据完整性,而 CAP 中的 C 是指分布式节点中的数据一致性。ACID 的应用场景是数据库事务,CAP 关注的是分布式系统数据读写这个差异点。

可用性

直接来看第一版对可用性的定义

Every request gets a response on success/failure.

简单翻译为:每个请求都能得到成功或者失败的响应。

这个描述太模糊了,比如我们在上文提到了不同节点数据是可能不一致的,比如两个节点之间网络不通,那么此时系统可以选择直接返回错误信息或者旧数据,那么此时该请求这个算成功还是失败呢。所以我们来看第二版对可用性的定义

A non-failing node will return a reasonable response within a reasonable amount of time (no error or timeout).

翻译下:非故障节点将在合理的时间内(无错误或超时)返回合理的响应。

这个就比较清晰了,如果节点宕机了,不在讨论范围内,如果节点错误或超时了,那么就是不可用,至于返回是新数据还是旧数据,只要能返回数据就可以,因为第二版没有强调必须是正确的数据,如果返回的旧数据,肯定是不正确的结果,但可以是一个合理的结果。

我们在来看看上面说的故障是指什么故障,一般情况下,我们讨论的故障是指节点宕机或无法响应,而不是分区故障。

分区故障:以网络分区为例,网络分区是指因为网络故障导致网络不连通,不同节点分布在不同的子网络中,各个子网络内网络正常。其实,你可以这么理解,节点之间的网络通讯出现了消息丢失、高延迟的问题。

所以说可用性说的是任何来自客户端的请求,不管访问哪个非故障节点,都能得到响应数据,但不保证是同一份最新数据。你也可以把可用性看作是分布式系统对访问本系统的客户端的另外一种承诺:我尽力给你返回数据,不会不响应你,但是我不保证每个节点给你的数据都是最新的。

举个例子

假设下图两个节点都是正常工作的,并且可以互相通信,但是这两个节点数据同步是异步的,所以数据不一致,但是如果客户端请求来了两个节点都返回数据,此时,有一个节点数据是旧的,这不是正确的结果,但是是合理的响应,所以这种情况是满足可用性的

再举个例子

来看下面的图,当节点 1 和节点 2 通信出问题的时候,数据无法同步,但是如果两个节点都能响应客户端,我们也说这是满足可用性的。

分区容错性

第一版解释:

System continues to work despite message loss or partial failure.

简单翻译为:出现消息丢失或者分区错误时系统能够继续运行。

第二版解释:

The system will continue to function when network partitions occur.

简单翻译为:当出现网络分区后,系统能够继续“履行职责”。

第一版解释和第二版解释主要差异点表现在:

  • 第一版用的是 work,第二版用的是 function。

work 强调“运行”,只要系统不宕机,我们都可以说系统在 work,返回错误也是 work,拒绝服务也是 work;而 function 强调“发挥作用”“履行职责”,这点和可用性是一脉相承的。也就是说,只有返回 reasonable response 才是 function。相比之下,第二版解释更加明确。

  • 第一版描述分区用的是 message loss or partial failure,第二版直接用 network partitions。

对比两版解释,第一版是直接说原因,即 message loss 造成了分区,但 message loss 的定义有点狭隘,因为通常我们说的 message loss(丢包),只是网络故障中的一种;第二版直接说现象,即发生了分区现象,不管是什么原因,可能是丢包,也可能是连接中断,还可能是拥塞,只要导致了网络分区,就通通算在里面。

不过集群毕竟不是单机,当发生分区故障的时候,有时不能仅仅因为节点间出现了通讯问题,无法响应最新写入的数据,之后在客户端查询数据时,就一直返回给客户端出错信息。

分区容错性说的是,当节点间出现任意数量的消息丢失或高延迟的时候,系统仍然在继续工作。也就是说,分布式系统在告诉访问本系统的客户端:不管我的内部出现什么样的数据同步问题,我会一直运行。这个指标,强调的是集群对分区故障的容错能力。

其实就是上一张图,假设他是发生了网络分区,节点与节点之间无法通信,但节点与客户端之间可以互相通信。在此基础上,如果这节点2返回了数据,而不是拒绝响应或者超时,通过可用性的分析我们知道下图这个系统是满足可用性的,并且是在分区故障的时候满足可用性的,那么我们可以说这个系统又满足了分区容错性,但是很明显不满足一致性,所以这个系统是AP模型。

讲到这里,你可能有点懵,上面说分区容错性指的是在分区故障情况下系统依然工作,看上面的例子,这不必然带着可用性了么,那么CAP的P,说的是发生了分区故障还是发生了分区故障后还能工作?我相信如果将CAP中的P描述为分区故障你更好理解,但实际上并不是,你可以把发生了分区故障后还能工作(分区容错性)看成目标,C和A是手段。当发生分区故障的时候,要么你保证可用性,但是数据肯定不一致;要么你保证数据一致,但是可用性必然无法保证(因为数据无法同步到其他分区节点,其他分区节点不可用)。 这类似于ACID,数据库事务中的ACID中的C(一致性)是目标,AID是手段。而CAP中P是目标,C和A是手段。

下面详细讲讲

可用性与分区容错性,傻傻分不清

一致性大家比较好理解,多个数据节点保持数据一致即可,实现方式上就是更新时所有数据节点都更新成功才返回更新成功。

主要是可用性和分区容错性比较难以理解,下面详细说说。

问题1:分区容错性说分区故障正常工作,什么叫正常工作?这个正常工作是指满足可用性吗?

可能有一部分人被我上面的图(上一张图)误导了,觉得分区容错性和可用性是绑定在一起的,其实并不是,来看下一张图。相比上一张图,这张图的节点2与客户端不通了,当然这不是网络不通,而是客户端被选择的结果,这个选择就是如果节点2出问题了,没有最新的数据,那么就让客户端不访问节点2,而是访问节点1,这样系统是不是就保证数据一致性了,但是很明显不满足可用性了,我节点2没有宕机,没有断网,客户端你可以访问我但是你不访问,你为了拿到最新的数据,保证数据一致性,舍弃了节点2。

此时,该系统是正常工作的吗,是。该系统发生故障了吗?发生了。该系统满足可用性了吗?没有保证。我们可以回答上面的问题了,分区容错性说的是发生分区故障的时候正常工作,这个正常工作,并不是可用性,当我们舍弃可用性,保证数据一致性的时候,数据也是正常工作。

注意,可用性并不是说系统能工作就叫可用性,而是某个分区中的节点和客户端可以正常工作。

问题2:为什么说分区容错性是目标,而CA要舍弃一个,不能设计CA系统,舍弃P吗
  1. 什么叫分布式系统?多个节点,网络通信
  2. 网络通信是不可靠的,tcp也一样,参考两军问题
  3. 分区容错虽然不是时刻发生,但他意味着必然发生,因为网络是不可靠的

所以分区容错性是目标,是分布式系统必须要保证的,不能因为部分节点网络问题导致整个系统不能工作。

因为分布式系统与单机系统不同,它涉及到多节点间的通讯和交互,只要有网络交互就一定会有延迟和数据丢失,而这种状况我们必须接受,还必须保证系统不能挂掉。所以就像我上面提到的,节点间的分区故障是必然发生的。也就是说,分区容错性(P)是前提,是必须要保证的。

假设一个分布式系统我要保证CA,当发生分区容错的时候,为了保证数据一致性,多个节点数据无法同步,那么我更新操作是等待所有节点更新成功才返回更新成功呢,如果这样那么这个更新操作永远不能成功,再假设我不等待所有节点都更新成功就返回,我保证我读的时候去拿最新的数据,如果我发起一个读请求,发现他不是最新的,我要等待最新的数据同步过来,可是已经发生分区故障,我永远等不到最新数据过来,最后只能超时报错。所以说,当发生分区故障的时候,C和A无法同时保证。

问题3:C和A真的无法共存吗

当然不是,前后强调了很多次了,只有当发生分区故障的时候,C和A无法同时保证。不发生分区故障的时候,节点与节点之间,节点与客户端之间都能正常通信,那么这个系统此时肯定是既满足一致性又满足可用性。

至于我们说的CAP中的P,虽然说当发生分区故障的时候什么什么,但是由于网络分区是必然会发生的,同时分区容错性也是必须保证的,所以我们设计系统时必须假定已经发生了分区故障。

当然,如果是单机系统,没有网络分区的可能,那么自然是满足CA的,但这个系统也不叫分布式系统了,没必要讨论。

所以我们要知道,C和A无法共存指的是发生分区故障的时候无法共存,网络正常两个都可以满足的。

是CAP不能共存吗,是的,准确的说,是分区故障导致了CA不能共存

总结

CAP的理论终于讲完了。

这里就不重复CAP的定义了,我觉得理解CAP的关键点有以下几个:

  • CAP探讨的分布式系统是副本型的分布式系统。比如Mysql集群,有了实际例子就很好理解
  • 由于网络不可靠分区故障是必然会发生的,所以在分布式系统中P一定要保证
  • 系统可用(正常工作)和CAP说的可用性不一样,前者站在客户端的角度,后者站在分区节点的角度
  • 分布式系统中C和A可以共存,但无法一直共存,当发生分区故障时必须要牺牲一个,不发生的时候两个都能保证

一点拙见,主要内容还是参考了大佬的文章,这里添加了一些对于新手比较容易产生的问题并尝试解答,希望对你有所帮助

参考

极客时间-从0开始学架构

-李运华

极客时间-分布式协议与算法实战

-韩健

CAP 超详细名词解释的更多相关文章

  1. 图书管理系统(Java实现,十个数据表,含源码、ER图,超详细报告解释,2020.7.11更新)

    图书管理系统数据库设计实验报告 文章目录 更新日志 1.概述 2.需求分析 2.1需要实现的功能 2.2业务流程图 2.2.1学生流程图 2.2.2管理员流程图 2.2.3超级管理员流程图 2.3功能 ...

  2. b2c项目基础架构分析(二)前端框架 以及补漏的第一篇名词解释

    继续上篇,上篇里忘记了也很重要的前端部分,今天的网站基本上是以一个启示页,然后少量的整页切换,大量的浏览器后台调用web服务局部.动态更新页面显示状态这种方式在运作的,从若干年前简单的ajax流行起来 ...

  3. b2c项目基础架构分析(一)b2c 大型站点方案简述 已补充名词解释

    我最近一直在找适合将来用于公司大型bs,b2b b2c的基础架构. 实际情况是要建立一个bs架构b2b.b2c的网站,当然还包括wap站点.手机app站点. 一.现有公司技术人员现状: 1.熟悉asp ...

  4. Java多线程学习(吐血超详细总结)

    Java多线程学习(吐血超详细总结) 林炳文Evankaka原创作品.转载请注明出处http://blog.csdn.net/evankaka 写在前面的话:此文只能说是java多线程的一个入门,其实 ...

  5. Sql常用语法以及名词解释

    Sql常用语法以及名词解释 SQL分类: DDL—数据定义语言(CREATE,ALTER,DROP,DECLARE) DML—数据操纵语言(SELECT,DELETE,UPDATE,INSERT) D ...

  6. 超强、超详细Redis数据库入门教程

    这篇文章主要介绍了超强.超详细Redis入门教程,本文详细介绍了Redis数据库各个方面的知识,需要的朋友可以参考下 [本教程目录] 1.redis是什么2.redis的作者何许人也3.谁在使用red ...

  7. 超详细的Xcode代码格式化教程,可自定义样式。

    超详细的Xcode代码格式化教程,可自定义样式. 为什么要格式化代码 当团队内有多人开发的时候,每个人写的代码格式都有自己的喜好,也可能会忙着写代码而忽略了格式的问题.在之前,我们可能会写完代码后,再 ...

  8. Web Services的相关名词解释:WSDL与SOAP

    在对Web Services进行性能测试时,接触到最多的两个名词就是WSDL和SOAP.利用LoadRunner对Web Services进行调用的时候,也存在两种常用方法,即基于WSDL的[Add ...

  9. 超强、超详细Redis数据库入门教程(转载)

    这篇文章主要介绍了超强.超详细Redis入门教程,本文详细介绍了Redis数据库各个方面的知识,需要的朋友可以参考下   [本教程目录] 1.redis是什么 2.redis的作者何许人也 3.谁在使 ...

随机推荐

  1. 从苏宁电器到卡巴斯基第30篇:难忘的三年硕士时光 VIII

    自给自足 临近毕业答辩,别的导师的学生基本上都完成了各自的论文,也都开始交由第三方进行审核.而我们导师由于情况特殊,还没有机会看我们的论文,所以我们也打算和老师约一个时间,来给我们的论文提点意见,修改 ...

  2. Windows认证体系解读

    目录 Windows认证方式 Windows本地认证 NTLM认证方式(工作组环境中) wiresharek抓包NTLMv2,使用Hashcat爆破 NTLM认证方式(域环境中) Kerberos认证 ...

  3. Windows PE第6章 栈与重定位表

    第六章 栈与重定位表 本章主要介绍栈和代码重定位.站和重定位表两者并没有必然的联系,但都和代码有关.栈描述的是代码运行过程中,操作系统为调度程序之间相互调用关系,或临时存放操作数而设置的一种数据结构. ...

  4. 【Android Jetpack高手日志】DataBinding 从入门到精通

    前言 DataBinding 数据绑定库是 Android Jetpack 的一部分,借助该库可以使用声明性格式(而非程序化地)将布局中的界面组件绑定到应用中的数据源.我个人觉得,使用 DataBin ...

  5. xml数据解析和生成

    java中xml的解析方式有许多,有java自带的DOM.SAX,android中的PULL,其它的还有DOM4J.JDOM等. 本文简要讲述DOM.SAX.PULL三种方式. 1.DOM方法 缺点: ...

  6. idea 2018.3.3版本激活到

        新装的,还是试用版本,下面就是进行激活操作: 先下载 链接: https://pan.baidu.com/s/1o44bsO7tx3WGuO5GgT0ytw 提取码: gbmw 第一步:将bi ...

  7. .NET生成小程序码,并合自定义背景图生成推广小程序二维码

    前言: 对于小程序大家可能都非常熟悉了,随着小程序的不断普及越来越多的公司都开始推广使用起来了.今天接到一个需求就是生成小程序码,并且于运营给的推广图片合并在一起做成一张漂亮美观的推广二维码,扫码这种 ...

  8. TLB和CPU缓存

    TLB 如果每次应用程序访问一个线性地址都需要先解析(查PDT,PTT)那么效率十分低,为了提高执行效率CPU在CPU内部建立了一个TLB表,此表和寄存器一样访问速度极高.其会记录线性地址和物理地址之 ...

  9. 关于文字内容过长,导致文本内容超出html 标签宽度的解决方法之自动换行

    在标签的style 属性中设置 word-break style="word-break:break-all;" 这样就可以实现换行 上截图没设置之前 设置之后 完美解决!!!!! ...

  10. im-chooser重新选择输入法框架在终端执行im-chooser命令

    CentOS 7安装图像桌面.Ibus拼音输入法一.图形桌面安装1.安装额外包yum源(extra package for Enterprise Linux) yum install epel-rel ...