CAP 理论,相信不少人都听过,它是指:程序员
一个分布式系统最多只能同时知足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance)这三项中的两项。面试
为何要理解 CAP 理论?我能说出不少理由来。若是是在职场上,也许最合适的理由是,当领导给出的任务不靠谱时,咱们能够依据 CAP 去否决它。服务器
好比,有这么一个任务,给你定了三大目标:网络
看到“既要、又要、还要”,是否是想到了阿里……多线程
OK,若是你深入理解了 CAP,你会发现完成这个任务是不可能的。可是,若是你不理解 CAP,而后又拍了胸脯保证完成任务,很差意思,职场是不须要眼泪和后悔的。架构
有点跑题了,书归正传。CAP 理论是分布式设计中最基础最重要的理论,不懂它,你可能连分析一套分布式系统的核心设计理念都作不到。异步
关于 CAP 为什么你读了那么多文章都仍是搞不明白呢?由于 CAP 理论来自学术界,而解读 CAP 理论的人尝试用工程师的方式去阐述它,这自己就有了问题。分布式
CAP 自己基于状态,基于瞬态,是一个描述性的理论,它并不解决工程问题。可是,不少工程师却老是尝试为 CAP 作过多解读。好比,非要说 CAP 理论只能适合某某场景,非要说 CAP 理论里的一致性是很是强的一致性,把其和事务的一致性混为一谈。性能
因为 CAP 是学术理论,并非工程理论,它会舍弃不少现实世界的现实问题。好比网络的时长,好比节点内部的处理速度不一致,好比节点间存储方式和速度的不一致。它说的一致性就是客户端是否能拿到最新数据,它说的可用性就是容许客户端拿不到最新数据。而这些东西被工程师们的过多脑补,致使了文章和文章说法不同,解析不同,阐述背景不同。学习
在今天这篇文章中,咱们只解释和说明,不脑补,不过多从工程角度解读,只说本质,只指核心,但愿能真正说清楚、讲明白 CAP 理论。望本文能达到这个目的。
接下来你看到文字,我前先后后写了 10 天,已是这篇文章的第三版了,前两版写了一半都被我推翻重写了,由于我本身看了都不满意。
一方面是对本身知识掌握程度不满意,本觉得本身明白 CAP 了,直到写的时候,发现有些仍是拿不许。
另外一方面是不满意本身的写的太晦涩、太教科书,能把知识讲的通俗易懂,才是我但愿的。
给你们看看文章上辈子的模样。
要理解 CAP,首先咱们要清楚,为什么会有人提出 CAP?他提出 CAP 是为了解决什么问题?
时间回到 1985 年,彼时,后来证实了 CAP 理论的 Lynch 教授此时给当时的 IT 界来了一记惊雷:
她经过不可辩驳的证实告诉业界的工程师们,若是在一个不稳定(消息要么乱序要么丢了)的网络环境里(分布式异步模型),想始终保持数据一致是不可能的。
这是个什么概念呢?就是她打破了那些既想提供超高质量服务,又想提供超高性能服务的技术人员的幻想。
这本质是在告诉你们,在分布式系统里,须要妥协。
可是,如何妥协?分布式系统里到底应该怎么权衡这种 trade-off?
咱们能够想象一下,在 CAP 定理提出以前,没有这些方向性的指引,在设计和实施分布式系统时该有多么混乱。一套分布式系统是由多个模块组成的,这些模块自己可能由不一样的开发人员去完成。然而,对于这些人,在公共层面,居然没有一个原则去指导他们该怎么完成这套功能。
好比,咱们在同步两个节点的数据时,若是发生了错误,到底咱们应该怎么作呢?若是没有统一的标准和方向,那极可能在一套分布式系统中的不一样模块,会出现不一样的处理状况。
假设一套系统,由 A、B 两个模块构成。
A 模块的设计理念是:节点间出现了问题,它可能会选择不断的重试,一直等到节点通讯恢复。
而 B 的设计理念是:节点间出现了问题,它断开就是了,可能最多就记录下状态,等之后处理。
但是,当 A、B 之间出现了通讯怎么办?那会出现 A 往 B 发请求,出问题会不断重试。而 B 往 A 发请求,出问题则直接断开的状况。
固然,在后面咱们会说明,CAP 的理念在实际工程中,会容许这种不一致。但是,那种不一致是提早设计好和规划好的,是根据实际数据的重要性和业务需求作的妥协,而不是这种混乱的妥协。
因此,IT 界的人们就一直在摸索,试图找到一些纲领去指导分布式系统的设计,这一找就找了 15 年。
2000 年时,Eric Brewer 教授在 PODC 会议上提出了 CAP 理论,可是因为没有被证实过,因此,当时只能被称为 CAP 猜测。这个猜测引发了巨大的反响,由于 CAP 很符合人们对设计纲领的预期。
在 2002 年后,通过 Seth Gilbert 和 Nancy Lynch 从理论上证实了 CAP 猜测后,CAP 理论正式成为了分布式系统理论的基石之一。
CAP 定理表达了一个分布式系统里不可能同时知足如下的三个特性:
什么是数据一致性?咋一看真的很让人糊涂,一致性是什么?是指数据能一块儿变化,是能让数据整齐划一。
那么问题又来了,数据什么时候会变化?数据怎么才能被称为一块儿变化?咱们如今来回答这些问题,当咱们搞清楚了这些问题,那么对数据一致性就会有了清晰的理解。
首先第一个问题,数据什么时候会一块儿变化?
答案是:仅且仅当包含数据的服务,收到数据更新请求的时候,数据才会发生变化。而数据更新请求则仅包括数据的增、删、改这三种请求,而这三种请求又被统称为写请求。因此,数据只有在写请求的时候才会发生变化。
那咱们来回答第二个问题,数据要怎么样才能被称为一块儿变化了?即谁来判断数据是最终变化了?是服务器对写请求的返回结果吗?告诉写请求成功,数据就必定发生一致性变化了?
NO,数据发生变化是否一致是须要通过读请求来作检验的。那么读请求判断的依据是什么呢?
假设,咱们的分布式存储系统有两个节点,每一个节点都包含了一部分须要被变化的数据。若是通过一次写请求后,两个节点都发生了数据变化。而后,读请求把这些变化后的数据都读取到了,咱们就把此次数据修改称为数据发生了一致性变化。
可是,这还不是完整的一致性。由于系统不可能永久的正常运行下去。
若是系统内部发生了问题从而致使系统的节点没法发生一致性变化会怎么样呢?当咱们这样作的时候,就意味着想看到最新数据的读请求们,极可能会看到旧数据,或者说获取到不一样版本的数据。此时,为了保证分布式系统对外的数据一致性,因而选择不返回任何数据。
这里须要注意一下,CAP 定理是在说在某种状态下的选择,和实际工程的理论是有差异的。上面描述的一致性和 ACID 事务中的一致性是两回事。事务中的一致性包含了实际工程对状态的后续处理。可是 CAP 定理并不涉及到状态的后续处理,对于这些问题,后续出现了 BASE 理论等工程结论去处理,目前,只须要明白 CAP 定理主要描述的是状态。
奥维德曾经说过:“行动被人们遗忘,结果却将永存”。
这句话说明告终果的重要性,而可用性在 CAP 里就是对结果的要求。它要求系统内的节点们接收到了不管是写请求仍是读请求,都要能处理并给回响应结果。只是它有两点必须知足的条件:
条件 1:返回结果必须在合理的时间之内,这个合理的时间是根据业务来定的。业务说必须 100 毫秒内返回,合理的时间就是 100 毫秒,须要 1 秒内返回,那就是 1 秒,若是业务定的 100 毫秒,结果却在 1 秒才返回,那么这个系统就不知足可用性。
条件 2:须要系统内能正常接收请求的全部节点都返回结果。这包含了两重含义:
若是节点不能正常接收请求了,好比宕机了,系统崩溃了,而其余节点依然能正常接收请求,那么,咱们说系统依然是可用的,也就是说,部分宕机没事儿,不影响可用性指标。
若是节点能正常接收请求,可是发现节点内部数据有问题,那么也必须返回结果,哪怕返回的结果是有问题的。好比,系统有两个节点,其中有一个节点数据是三天前的,另外一个节点是两分钟前的,若是,一个读请求跑到了包含了三天前数据的那个节点上,抱歉,这个节点不能拒绝,必须返回这个三天前的数据,即便它可能不太合理。
分布式的存储系统会有不少的节点,这些节点都是经过网络进行通讯。而网络是不可靠的,当节点和节点之间的通讯出现了问题,此时,就称当前的分布式存储系统出现了分区。可是,值得一提的是,分区并不必定是由网络故障引发的,也多是由于机器故障。
好比,咱们的分布式存储系统有 A、B 两个节点。那么,当 A、B 之间因为可能路由器、交换机等底层网络设备出现了故障,A 和 B 通讯出现了问题,可是 A、B 依然都在运行,都在对外提供服务。这时候,就说 A 和 B 发生了分区。
还有一种状况也会发生分区,当 A 出现了宕机,A 和 B 节点之间通讯也是出现了问题,那么咱们也称 A 和 B 发生了分区。
综上,咱们能够知道,只要在分布式系统中,节点通讯出现了问题,那么就出现了分区。
那么,分区容忍性是指什么? 它是说,若是出现了分区问题,咱们的分布式存储系统还须要继续运行。不能由于出现了分区问题,整个分布式节点所有就熄火了,罢工了,不作事情了。
咱们上面已经知道了,在设计分布式系统时,架构师们在 C、A、P 这三种特性里,只能选择两种。
可是,这道 CAP 的选择题,就像别人在问你“小明的父亲有三个孩子,老大叫大朗,老二叫二郎,请问老三叫什么”同样。在以分布式存系统为限定条件的 CAP 世界里,P 是早已经肯定的答案,P 是必须的。
由于,在分布式系统内,P 是必然的发生的,不选 P,一旦发生分区错误,整个分布式系统就彻底没法使用了,这是不符合实际须要的。因此,对于分布式系统,咱们只能能考虑当发生分区错误时,如何选择一致性和可用性。
而根据一致性和可用性的选择不一样,开源的分布式系统每每又被分为 CP 系统和 AP 系统。
当一套系统在发生分区故障后,客户端的任何请求都被卡死或者超时,可是,系统的每一个节点老是会返回一致的数据,则这套系统就是 CP 系统,经典的好比 Zookeeper。
若是一套系统发生分区故障后,客户端依然能够访问系统,可是获取的数据有的是新的数据,有的仍是老数据,那么这套系统就是 AP 系统,经典的好比 Eureka。
说了这么多,其实 CAP 定理本质很简单,它就是一种分布式系统设计的不一样理念归纳,包括它说的一致性,可用性和分区容错性。这就相似一个大学的校训,是极度概念化的东西。
因此,大白话来形容下 CAP 吧,CAP 就是告诉程序员们当分布式系统出现内部问题了,你要作两种选择:
迁就外部服务就是咱们不能由于咱们本身的问题让外部服务的业务运行受到影响,因此要优先可用性。而让外部服务迁就咱们,就要优先一致性。
不少人在没有对 CAP 作深刻了解的状况下,听到不少人说分布式系统必须在 CAP 三个特性里选择两个,就以为一套分布式系统确定要么只有可用性要么只有一致性,不存在完整的可用性和一致性功能。
这种理解是大有问题的。由于,P 这种问题发生的几率很是低,因此:
当没有出现分区问题的时候,系统就应该有完美的数据一致性和可用性。
你何时见过一个系统,当内部没有问题的时候,会常常让外部请求卡一下的?要么就冷不丁的提供陈旧的老数据?那还能叫系统吗?
这个理解也是不对的。当分区发生的时候,其实对一致性和可用性的抉择是局部性的,而不是针对整个系统的。
多是在一些子系统作一些抉择,甚至极可能只须要对某个事件或者数据,作一致性和可用性的抉择而已。
好比,当咱们作一套支付系统的时候,会员的财务相关像帐户余额,帐务流水是必须强一致性的。这时候,你就要考虑选 C。可是,会员的名字,会员的支付设置就没必要考虑强一致性,能够选择可用性 A。
一套分布式系统的运行,就像人生同样,就是一次又一次的选择。在不一样阶段,不一样的时刻有不一样的事件发生的时候,又怎么可能会有彻底同样的选择呢?
这种二元性的理解更是极其误导人。
CAP 理论的三种特性不是 Boolean 类型的,不是一致和不一致,可用和不可用,分区和没分区的这类二选一的选项。而是这三种特性都是范围类型。
拿可用性来讲,就像我从银行取钱。当我目的是派发压岁钱的时候,我极可能就想全要新票子,可是,新票子极可能就还得多一个步骤,就是须要拿旧票子去换一些新票,此时,我能够多等会儿,能拿到新票子就好。而当个人目的就是作生活花销的时候,票子是新是旧,我根本不那么关心,快点拿到钱就行。这就是可用性的范围需求之一,对时延性的要求。
再好比,分区容错则因为探测机制的问题,可能还得各节点搞投票去协商分区是否存在,当某一台机器出现了问题,可能不影响业务的话,就会被机器投票认为分区不存在。而后一直等到多数机器出现了问题,才会投票确认出现了分区问题。这就好像新冠疫情,还会分低、中、高风险区呢,不是一出现通讯故障就都被逻辑认定为分区问题。
首先,在 CAP 定理中,关于一致性会有多种说法,可是总的来讲,都是在描述数据最新版本的可见性。而这些可见性每每表明的是读请求返回的数据的可见性。
那么问题来了,当咱们要求读数据的可见性的时候,对写数据有什么要求吗?
好比,咱们系统有三个节点,一个客户端给这个系统发了一个写请求,要求系统写入一个值为 20 的数据。那么,若是要知足 CAP 定理中的一致性,就须要在写完 20 这个数据以后,当其余客户端请求读取这个值为 20 的数据以后,不管请求被转发到系统中任何节点都能返回这个值。
这就要求写入这个值为 20 的写请求必须成功写到三个节点上,此时,系统就知足了写一致性的。因此,咱们能够说对于读一致性的要求是同时约束了写一致性的。
其次,在 CAP 定理中,可用性自己要求对读、写请求都要处理。若是咱们以可用性做为标准的时候,在发生分区错误时,因为咱们对读请求并无强行要求返回彻底准确的数据,因此,可能在本次读请求以前的最近一次写请求多是部分失败的。
一样的例子,咱们的分布式系统由三个节点组成,最近一次写请求想把值为 20 的数据写到三个节点上。可是,因为发生了分区问题,有一个节点通讯故障,写请求写不过去,所以只有两个节点包含了值为 20 的数据。
此时,写请求会返回给客户端一个结果,可能会告诉客户端写入成功了,也可能告诉客户端写入部分红功。
这时候,当后续的读请求恰巧被发送到有通讯故障的那个节点,系统可能只能返回一个空的结果。可是,因为系统处理和返回了读写请求,因此,系统是知足了 CAP 中的可用性的。
咱们知道,在一套大规模的分布式系统里,必定是既须要把海量数据作切分,存储到不一样的机器上,也须要对这些存储了数据的机器作副本备份的。
那么,若是,一个分布式系统里只有数据分片存储或者只有数据副本存储,他们都会遵照 CAP 定理吗?
答案是当数据分片时,也是要遵照 CAP 定理,可是,是种很是特殊的遵照。
当在一套分布式系统只有分片存储的时候,CAP 理论会表现成什么样?
好比,咱们有个分布式系统,由三个节点 a、b、c 组成。其中节点 a 存放了 A 表的数据,b 存放了 B 表的数据,c 存放了 C 表的数据。
若是有一个业务,它的意图是想往 A 表插入一条新数据,在 B 表删除一条已有数据,在 C 表更新一条老数据,这个分布式系统该怎么处理这种业务?
技术上咱们对这种一个意图想作多件事的状况每每会包装成一个事务。当咱们包装成一个事务之后,咱们可能会经过先在 a 节点执行,而后去 b 节点执行,最后去 c 节点执行,等到都成功了,才会返回成功。
可是,发生了分区之后怎么办?当在 a、b 节点都成功了,到 c 发现发生了通讯故障?
此时,根据 CAP 定理,你有两个选择,要么就直接返回一个部分红功的结果给客户端,要么直接卡死等客户端超时或者返回失败给客户端。当返回部分红功的时候,这就是选择了可用性(A),当卡死或者返回失败给客户端的时候,就是选择了一致性(C)。
但是,咱们将请求包装成了事务,而事务是要求要么都成功,要么都失败……为了遵照这种要求,对于分布式只有分片的状况,迫于客观条件,只能选择C。因此分片的分布式系统,每每都是 CP 的系统。
可选择,可是没法选择是分布式系统只有分片数据存储的状况时,遵照 CAP 定理的特殊表现。
而当分布式系统是多个节点,每一个节点存储了完整的一套数据,别的节点只是完整数据的备份的时候,即便事务只在一台机器上成功,当发生分区故障的时候,咱们也是能够有充分的余地选择是单机事务的回退 or 就此认为写成功的。
单机事务的回退,就能够对外表现为选择了一致性。
就此认为写成功,则能够认为选择了可用性。
由于,就像咱们前面讲过的,因为 AP 或者 CP 的选择,可能仅局限为整套系统的局部,甚至某些特殊的数据上,而咱们又是用这种局部的特性去描述了整套系统,因此就致使了区分的困难。而这自己其实也日渐成为了 CAP 的一个大问题,从而被人诟病。
CAP 定理自己是没有考虑网络延迟的问题的,它认为一致性是当即生效的,可是,要保持一致性,是须要时间成本的,这就致使每每分布式系统多选择 AP 方式
因为时代的演变,CAP 定理在针对全部分布式系统的时候,出现了一些力不从心的状况,致使不少时候它本身会把之前很严谨的数学定义改为了比较松弛的业务定义,相似于咱们看到,CAP 定理把一致性、可用性、分区容错都变成了一个范围属性,而这和 CAP 定理自己这种数学定理般的称呼是有冲突的,出现了不符合数学严谨定义的问题。
在实践中以及后来 CAP 定理的提出者也认可,一致性和可用性并不只仅是二选一的问题,只是一些重要性的区别,当强调一致性的时候,并不表示可用性是彻底不可用的状态。好比,Zookeeper 只是在 master 出现问题的时候,才可能出现几十秒的不可用状态,而别的时候,都会以各类方式保证系统的可用性。而强调可用性的时候,也每每会采用一些技术手段,去保证数据最终是一致的。CAP 定理并无给出这些状况的具体描述。
CAP 理论从工程角度来看只是一种状态的描述,它告诉你们当有错的时候,分布式系统可能处在什么状态。可是,状态是可能变化的。状态间如何转换,如何修补,如何恢复是没有提供方向的。
正由于 CAP 以上的种种不足,epay 的架构师 Dan Pritchett 根据他自身在大规模分布式系统的实践经验,总结出了 BASE 理论。BASE 理论是对 CAP 理论的延伸,核心思想是即便没法作到强一致性(Strong Consistency),但应用能够采用适合的方式达到最终一致性(Eventual Consitency)。
BASE 理论是实践工程的理论,它弥补了CAP 理论过于抽象的问题,也同时解决了 AP 系统的整体工程实践思想,是分布式系统的核心理论之一,咱们将在下一篇文章里,详细的讲解此套理论。
在文章最后,来几道大厂关于 CAP 的面试真题,检验一下你的学习效果,hiahiahia
什么是 CAP 理论?
CAP 中的 P 是什么意思?
为何说分布式系统,只能在 C、A 中二选一?
结合实际应用,CP、AP 该怎么选择?
我准备了一些纯手打的高质量PDF:
深刻浅出Java多线程、HTTP超全汇总、Java基础核心总结、程序员必知的硬核知识大全、简历面试谈薪的超全干货。
别看数量很少,但篇篇都是干货,看完的都说很肝。
领取方式:扫码关注后,在公众号后台回复:666