CAP与BASE理论
文章目录
一:分布式基础 - CAP理论
当一个从逻辑上被视为整体的系统,拆散到多个节点部署时,则能称之为分布式系统
分布式领域中的CAP
理论,即是图中三个单词的首字母缩写组合,CAP
由三个指标组成:
C
:Consistency
(一致性);A
:Availability
(可用性);P
:Partition tolerance
(分区容错性)。
1:一致性
分布式和以往集中式部署的单体架构不同,它可以部署在多个节点上
以Redis
为例,如果只部署一个节点,则属于单机版本,采用主从、分片等集群模式部署,称之为分布式版本。
所谓的一致性,即是在同一个分布式系统中,同一时刻的所有节点,能看到的数据完全相同。
比如2024-03-10 11:11:11
这个时间点,两个读取相同Key
的请求,一个落入到主节点A
,另一个落入到从节点B
,读到的数据必须完全相同,这保证了数据的完整性和准确性。
反之,如果这两个请求拿到的结果不同,意味着Redis
主从集群这个分布式系统中,并没有实现数据一致性。
同时,一致性还要求:一旦某个数据在分布式系统中的某个节点上被更新(如修改成新值)
那么后续对该数据的读取操作,无论发生在哪个节点上,都应该返回更新后的值,即:所有节点在同一时间内,都能看到相同、且最新写入的值。
基于上述结论来问个问题:在
MySQL
主从模式下,一个新值写入后还未提交,此时在从节点上读取到了老值,满足CAP
的一致性要求吗?答案是满足,只要保证事务提交前,所有节点读到的数据都为老值,就满足一致性要求!
因为带事务性质的场景中,写入动作的完成时机,并不是数据落到某个节点,而是整个事务被提交时,只有当事务被提交,才代表真正完成了数据写入。
2:可用性
分布式系统因为部署在多个节点,虽然避免了单点故障问题,但也会增加整个系统的风险出错率
还是上面Redis
的例子,A、B、C
三个节点都有可能出现故障。
而CAP
里的可用性,即是指分布式系统中,能够始终对请求做出响应,好比B
节点发生错误,整个Redis
服务依旧要能够正常处理并响应外部的请求,不能由于某一个或部分节点的损坏,导致整个系统陷入不可用状态。
可用性强调的是系统对外部请求的响应能力,具体来说,它要求系统能够在一定的时间内,对任何非失败的外部请求做出响应。
这意味着,无论系统内部发生什么情况,只要外部用户发出请求,系统都应该尽快做出响应,即使回应的是拒绝服务或错误消息。
因此,可用性关注的是系统对外部请求的响应速度和可靠性。
3:分区容错性
组成分布式系统的多个节点,可以部署在任意节点上,这意味着我们可以用物理位置不同的服务器来部署系统,不同节点间的协调与数据同步,都依靠网络来完成。
当组成分布式系统的多个节点,其中某个节点出现网络故障时(如网络中断、抖动、网络设备损坏等),对系统内其余正常的节点来说,无法通过网络感知到该节点,它成为无法通信的孤立区域,从而造成了网络分区。
CAP
里的网络分区,并非是网络编程里的局域网概念,而是指分布式系统内部网络出现故障时,系统中的节点可能被分割成多个独立的子网络,这些子网络之间的通信被阻断,导致数据无法在它们之间自由流动。
CAP
里的P
分区容错性,就要求系统在这样的情况下仍然能够正常运行,即使系统内出现了多个分区,分布式系统依旧能正常运行,并对外提供基本的服务(至少一部分服务可用)。
当然,某个节点掉线会造成孤立区域出现,系统内动态加入节点,同样也会导致分区问题。
因为新节点加入和老节点离开,都可以视为系统内部的网络分区,CAP
里的分区容错性,本质就是分布式系统对节点动态加入和离开的处理能力
4:CAP -> AP & CP
上面对CAP
理论做了展开,讲清了其中的三个指标,简单总结下:
- C:一致性要求所有节点在同一时间看到相同的数据;
- A:可用性则要求系统能够始终对请求做出响应;
- P:分区容错性则是指系统在遇到网络分区时,仍然能够保持一定的可用性和一致性。
虽然其中的A、P
在某些方面看起来相似,但它们关注的焦点并不相同
可用性侧重于系统对用户请求的响应能力,而分区容错性则更侧重于系统在出现网络分区时的表现
CAP定理:CAP三个指标不可能同时做到,三者只能选其二,意味着只能有CA、AP、CP
三个组合,但从来没听说过市面上有保证CA
的分布式组件
假设我们自己要研发一款分布式组件,如果要保证
CA
(一致性与可用性),该怎么实现?
可用性可以理解成高可用,高可用的前提是解决单点故障,因此我们可以考虑集群设计方案,使用多个节点来组成整个系统
当系统部分节点出现故障时,外部请求能自动转移到其他健康的节点上处理
通过这种方案,我们可以保证系统在节点故障时的可用性,不过这里又有个问题,如何感知节点是否健康?
设计健康检查机制!而最主流的方案则是心跳机制,如果一个节点发送不了心跳包,或者系统内其他节点收不到某个节点的心跳包,说明该节点已经处于故障状态,后续不用将请求转发到该节点。
保障了可用性后,接着来看看一致性,因为此时有多个节点,多个节点的数据一致该怎么实现?
选择2PC、3PC
这类强一致方案,当外部往某个节点写入数据时,该节点触发数据同步机制,将写入的数据同步给所有节点,当所有都同步完成后,再给客户端返回写入成功,这样就能保证所有节点数据完全一致。
通过上述步骤,就设计出了一个简易版的CA
分布式组件,存在什么问题吗?
问题很大,节点间的心跳检测、数据同步,需要依靠网络进行通信,先来看看心跳检测的问题:
- 某个节点其实很健康,但发出的心跳包,因为网络抖动造成丢包,其他节点没收到就认为它故障了,这合理吗?不合理。
- 某个节点发出的心跳包,部分节点收到了,部分节点没收到,一部分节点认为健康,一部分节点认为故障,从而造成了分区,怎么办?
再来看看保证一致性的数据同步方案,假设某个节点故障,又或者同步数据时的包丢失,导致系统内多个节点数据不一致
系统为了达成“数据一致态”,会不断触发重试机制,造成外部请求阻塞,一直无法成功写入……
综上,最开始的设计思路,只是我们最理想的状态,但网络其实是个不可控因素,总会由于各种各样的原因造成故障出现。
因此,在设计分布式系统时,网络故障带来的分区问题,一定要率先考虑
如果对分区问题没有容错性,代表系统内一个节点出现问题时,会造成整个系统无法正常运行
这也是为什么只有保证AP、CP
的分布式组件,没有保证CA
的原因。
有没有能保证
CA
的组件呢?答案是有,就是单机版本,毕竟只有一个节点,数据写入成功后,就能保证多个外部请求看到的数据都相同;
同样,只要这一个节点活着,系统就肯定可用,也是一种“狭义上的可用”。
综上,分布式肯定要保证P
,无法保证P
的分布式组件,只能被称为“部署在多个节点上的单体系统”
为此,对于CAP
那幅图,正确的画法应该是这样的:
为什么三者不能同时存在?
分布式系统中的通信离不开网络,而恰恰网络出现故障是常事,在出现分区问题时,节点间的通信会受到严重阻碍,来看个例子:
如上图所示,该系统由A、B、C
三个节点组成,其中由于C
节点故障导致分区问题出现。
如果要完全满足CAP
里的一致性要求,意味着当外部写入数据时,A
节点必须等到C
节点同步完成,才能给客户端返回写入成功
可此时C
节点已经挂了,注定着数据写不进去……
假设此时出现读取该数据的请求怎么办?此时只有两种办法:
- 放弃可用性:等待所有节点的数据都达到一致状态,保证任意节点返回的数据都相同,可这时系统必然无法及时响应;
- 放弃一致性:给客户端返回已经写入进
A、B
的新数据,但后续C
节点恢复,请求去到C
时,会出现读取到的数据不一致;
通过这个例子,相信大家一定明白了C、A
之间为何只能选一个
- 保证可用性(
AP
),虽然可以快速响应外部请求,但无法做到任意时间点、所有节点数据的一致; - 保证一致性(
CP
),就需要等到所有节点数据达到一致,从而造成系统无法及时响应外部请求,可用性降低。
5:为何90%系统无需实现CAP
前面给过分布式系统的定义:当一个从逻辑上被视为整体的系统,拆散到多个节点部署时,则能称之为分布式系统
而分布式系统也可以分为两类,一类是带存储性质的,另一类则是非存储性质的(类似于Kubernetes
里的无状态Pod
)
而90%
以上的开发者,负责开发的分布式系统都属于后者,啥意思?
包括我在内的大部分人,平时都在做业务开发,而一个业务性质的分布式系统,需要实践CAP
理论吗?
并不需要,因为业务系统本身只有逻辑处理,没有数据存储,自然不存在所谓的数据一致性(数据最终是存在各种组件里,如数据库)。
真正需要实践CAP
理论的分布式系统,主要是带存储性质的分布式系统,如Redis、MQ
、数据库、注册中心……
以CAP
理论应用最广的“注册中心”领域为例,正是因为它需要存储服务注册的信息,所以才需要实践CAP
理论
也只有在研发这类分布式组件时,才需要在一致性、可用性间做取舍。
作为业务开发者,我们只需要站在已有组件上做考量,想清楚到底用CP
,还是AP
就足够了。
6:业务中的CAP思想
除开研发分布式存储组件要实践CAP
理论外,做业务类型的分布式系统,哪些场景会用到CAP
思想呢?下面举些例子说明。
- 通过传统的数据库,搭建多库架构(主从、多主)时
- 选择同步复制,说明更关注数据一致性(CP)
- 选择异步复制,则代表更关注响应速度(AP)
- 使用分布式锁时,在
Redis、Zookeeper
都为主从集群模式的情况下- 如果选择
Redis
来实现分布式锁,代表更关注响应速度(AP) - 如果选用
ZK
来实现,则代表更关注一致性(CP)
- 如果选择
- …
二:分布式核心:BASE理论
- BASE理论可以让我们不必在A、C中做抉择,而是可以实现部分的A和C;
- BASE理论是在CAP基础上,满足AP后对C方面的拓展与延伸。
上面这两种说法都是错误的!!!
Basically Available、Soft state、Eventually consistent
简称为BASE
理论
该理论最早源于ACM
上《Base: An Acid Alternative》这篇论文,由eBay
系统架构师Dan Pritchett
于2008
年发布,该理论提出了三个概念:
BA
:Basically Available
(基本可用);S
:Soft state
(软状态);E
:Eventually consistent
(状态最终一致性);
1:基本可用
当系统出现故障或意外情况时,允许降低部分可用性,保证系统基本可用,而这类情景十分常见,比如大促活动期间:
- 熔断/限流:当系统负载达到指定阈值时,新到来的请求会被拒绝(返回配置好的提示语);
- 关闭非核心业务:如禁止大数据报表导出、停掉部分非关键性的任务,给核心业务腾出资源;
- 服务降级……
当系统出现故障或意外情况时,允许放弃掉部分可用性,保证核心功能可用,或能响应外部请求(返回自定义的错误提示也可以)即可
2:软状态
软状态在有些地方也叫弱状态,是指允许系统存在中间状态,并且该中间状态不会影响系统整体的可用性
这就好比传统关系型数据库里的事务机制,假设没有事务机制,写入数据就只有成功、失败两种状态:
- 成功:数据成功保存到数据库,当出现读取请求时,能正常读到写入(或更新)后的值;
- 失败:数据未保存到数据库,当出现读取请求时,只能看到之前的值,或看不到值;
而有了事务机制后,在成功与失败之间,多了一个中间态,即:数据写入成功,事务暂未提交,这个中间态的存在,也不会影响数据库整体的可用性。
3:最终一致性
在BASE
理论中,最终一致性是和软状态绑定的,两者需要结合在一起理解。
前面说到,软状态允许系统内存在中间态,但要记住:如果这个中间态,一直不变成终态,而是卡在那里的话,就会影响系统整体的可用性。
啥意思?结合上面给出的数据库事务例子来说,如果一个事务处于中间态(写入成功,但一直不提交);当其他请求继续对该事务操作的数据行进行更新时,就会被阻塞,从而影响了系统的可用性,这就违背了“软状态”的定义。
软状态除开强调允许存在中间态外,还声明了该中间态不会影响系统整体的可用性
意味着这个中间态,注定只能短暂存在,在一定时间后,肯定会变成终态!
BASE
理论里的最终一致性,就是为了填补“卡在中间态不变化”这个逻辑漏洞
代表系统内出现中间态时,经过一定时间推移后,最终肯定能达到一致的状态(终态)。
4:CAP & BASE的关系(重要)
BASE
理论和CAP
理论没太大关系
4.1:再谈CAP
CAP
理论更偏向于分布式存储这个领域,而且并非所有分布式存储领域都会用到CAP
,只有哪些存在数据副本的分布式存储场景,如所谓的主从集群、多主集群、镜像集群、复制集群、副本集群……等场景时,才会用到CAP
,因为这些情况下才会涉及到数据同步,才会涉及到数据一致性问题!
而分库分表、Redis-Cluster、MongoDB-Cluster……
这类分片式存储的场景中,尽管也都是分布式存储场景,可由于每个节点存储的数据本来就不同,自然不存在所谓的数据一致性问题,也就自然不存在所谓的CAP
实践,这点一定要弄清楚,否则说明你并未真正理解CAP
思想。
CAP
理论最早是在1998
年由Eric Brewer
提出,这是26
年前的事了
当时分布式领域最前沿的技术,就是主从集群这一块,因此该理论更多针对的是这个方向。
4.2:再谈Base & ACID
再回头来看BASE
理论里面的最终一致性,这里的一致性和CAP
里的一致性,两者是一回事吗?
并不是,BASE
里的一致性维度更高,关注点是:分布式系统里的状态一致性,即中间态最终会演变成终态。
既然BASE
并非CAP
的延申理论,那它究竟是个啥?回到论文
可以发现标题是《ACID的替代品》
说到ACID这里只说一下C:ACID中的C是说:事务执行前后,数据库只能从一个一致状态转为另一个一致状态,即事务执行对整体数据产生的变化是一致的
需求:小竹向熊猫账户里转
1000W
元。
数据库现状:小竹账户余额为1001W
元,熊猫账户余额为888W
元。
面对上述这个需求,对应的转账事务为:小竹账户先减1000W
,熊猫账户再加1000W
。
所谓的一致性,就是这个事务执行前后,数据库里的整体数据变化一致,对其进行拆解:
- 初始态:小竹账户余额为
1001W
元,熊猫账户余额为888W
元。 - 结果态:小竹账户余额为
1W
元,熊猫账户余额为1888W
元。
如果该事务执行失败,数据库应该回到初始态,这对数据库来说状态没有改变;
而当该事务执行成功时,数据库应该来到结果态,此时小竹+熊猫的账户余额,合计还是1889W
,对数据库整体状态来说,也并未改变
这就是ACID
里所谓的一致性:事务执行前后只会从一个一致状态转为另一个一致状态。
当然,上面这段拆解对有些小伙伴来说,或许还是有点绕,那再举反例说明,即聊聊“不一致”的场景:
- ①小竹账户减
1000W
,余额1W
,但熊猫账户没有加,还是888W
,合计889W
。 - ②熊猫账户加
1000W
,余额1888W
,但小竹账户没有减,还是1001W
,合计2889W
。
上述则是两种没有保证一致性的场景,事务执行前后,整体数据的变化并不一致,造成数据库陷入了一种不正确的状态。
其他事务场景也是同理,下单时,[库存数减一,订单数加一]、[库存数不变,订单数不变],这属于一致状态
出现这两种情景之外的现象,则说明数据库未保证一致性。
4.3:重新理解BASE
再来翻译一下刚才那段话:
如果
ACID
为分布式数据库提供了一致性选择,那如何在分布式数据库里保证可用性呢?一个答案就是BASE
理论。
BASE
理论与ACID
完全相反,ACID
是悲观思想,在每次操作结束时强制要求保持一致性(即事务结束必须落入终态)。而
BASE
是乐观思想,可以接受数据库一致性处于不断变化的状态。虽然听起来……
可见:BASE
理论的最终一致性,并不是用来对标CAP
的一致性,而是与ACID
里的一致性对等,允许事务执行后出现“中间态”
商品库里有个“黄金竹子”,库存数为8888
,现在有个对应的下单请求(暂时抛开实际下单场景中的复杂流程)
对应的事务则为[库存减一、创建一条订单],如果遵循ACID
里的一致性,此时只能有两种状态:
- 初始态:黄金竹子库存为
8888
,没有对应的订单。 - 结果态:黄金竹子库存为
8887
,有一条对应的订单。
在分布式场景中,因为商品服务和订单服务是分开部署的,所以要通过网络调用“创建订单”的接口,可网速再怎么快,就算是内网环境,扣完库存到创建订单之间也会有时间差,这就导致分布式系统中会出现短暂的“不一致”:
- 中间态:黄金竹子库存为
8887
,没有对应的订单记录。
如果在这个中间态期间,出现一个读取“黄金竹子库存数”的请求,就会看到8887
这个库存数。
因此,正是因为这个中间态的存在,就违背了ACID
原则,ACID
定义的一致性不接受这种现象。
再回头看,BASE
理论中,软状态的定义允许存在中间态,最终一致性的定义接受延迟性
所以这也是为什么BASE
理论的论文中说:BASE是分布式场景中ACID理论代替品的本质原因!
好了,再把BASE
理论中的最终一致性,代入到上面的案例中,如果下单事务执行结束,流向会有两个:
- 新增订单成功,分布式事务可以提交,中间态流转到结果态。
- 新增订单失败,分布式事务触发回滚,中间态撤回到初始态。
综上所述,不管中间态是流转到结果态,还是撤回到初始态,最终系统内整体数据的变化是一致的,这也就是所谓的“最终一致性”。
两个理论除开一致性的定义不同外,甚至连“可用性”的定义也不一样。
CAP
中的可用性,是指主从这类集群,某个节点出现故障时,不会影响系统整体的运行
上图这个MySQL
多主集群,两个节点互为对方的从机,业务应用任意连接某个节点,都可以读写完整数据。
当某个节点出现故障时,业务应用都可以连接另一个节点,正常读写数据,这是CAP
关注的可用性(响应速度也是CAP
可用性的一种表现)。
反观BASE
理论,其中定义的“基本可用”,更多针对于分片式领域,来看论文中这段话:
翻译过来的意思就是:如果用户数据跨五个数据库服务器存储,根据BASE
理论,一台数据库出现故障,仅仅只会影响到20%
的用户。
用户数据跨五个数据库存储,意味着什么?这代表着用户数据做了水平拆分,所有用户数据会根据特定的规则,分发到五个不同的节点中分开存储!
正因如此,才会给出“一个节点故障只会影响20%
用户”这个结论,毕竟存储其他80%
数据的四个节点此时正常。
所以,BASE
理论中的基本可用,并不是指主从这类“全量式复制”的集群,而是针对“分片式”的集群。
BASE
论文中还提到按功能拆分的思想,其中称为“功能组”,就类似如今的按业务分库
Redis的集群是什么理论?
Redis主从集群是AP,数据同步用的异步复制,并且不支持通过参数修改,Redis选择了性能,放弃了数据一致性,存在数据丢失风险。
3.x推出的Redis-Cluster集群,属于分片式集群,而且是去中心化的分片集群,这类集群几乎摒弃了CAP思想,走的是分区存储的路子,遵从的是BASE思想,集群内部分节点宕机时,只影响部分数据,保证了基本可用。
4.4:为什么二者喜欢一起说
BASE
理论的最终一致性,虽然是指状态一致性,可是也能套入CAP
的数据一致性
比如主从集群数据复制,如果选同步-复制,太影响性能;如果选异步-复制,可以最快程度上响应请求,同时数据也会同步给从机,虽然有时间差,但只要集群内节点正常运转,所有节点的数据,最终都会保持一致。异步-复制模式中,如果数据刚落入主节点,而后主节点就宕机,尽管选上来的新主会丢失一部分数据,但也不是不能用对吧?为此,BASE
定义的基本可用,也能套上CAP
的可用性
BASE
理论比CAP
晚诞生十年,两者所处的时代背景不同,BASE
定义的维度比CAP
要高
所以在一定程度上,BASE
能向下兼容CAP
,这也就让许多人觉得BASE
是CAP
理论的拓展。
4.5:总结一下
CAP的一致性,关注的是数据一致性;BASE的一致性,关注的是状态一致性
CAP
一致性 ≠ BASE
一致性
BASE
一致性 = ACID
一致性。