引言
本文为稀土掘金技术社区首发签约文章,30天内禁止转载,30天后未获授权禁止转载,侵权必究!
在《系统架构演进篇》中,我们站在全景角度出发,以十多年前国内互联网时代的兴起为起点,一步步讲述了后端领域技术架构的演进过程,从起初的单体式架构,再到如今的分布式、微服务、云原生,技术发展始终不曾停下前进的脚步。
科技进步离不开理论支撑,而当下大行其道的分布式架构,透过繁荣昌盛表象,底层同样离不开诸多分布式理论撑持。当然,相信诸位在学习分布式相关技术时,必然学到过两个分布式领域中的基础理论,即:CAP与BASE理论。
不过我先问大家一个问题:什么系统需要实践CAP理论?
很多人下意识会说出:分布式系统,这个答案对吗?并不尽然,其实大家所能接触的大部分"分布式系统",并不需要实践CAP
理论。
我为何给出上面这句结论?因为大多数讲分布式的资料、课程,虽然在一开始就会先讲述CAP
理论,但大家仔细想想,你在做分布式项目时,落地过这个基础理论吗?相信包括我在内,以及90%
以上的开发者,没有 ,至于为何,本文来从不一样的角度好好唠唠CAP
,以及另一个著名的BASE
理论~
当然,BASE
会广泛运用于分布式系统中,但绝大多数人并没有真正理解CAP
与BASE
理论之间的关系,注意,我这里说的"绝大多数人"覆盖面可能达到90%
,为何这么说?本文会一点点抽丝剥茧,"掰正"大家的固有认知。
PS:个人编写的《技术人求职指南》小册已完结,其中从技术总结开始,到制定期望、技术突击、简历优化、面试准备、面试技巧、谈薪技巧、面试复盘、选
Offer
方法、新人入职、进阶提升、职业规划、技术管理、涨薪跳槽、仲裁赔偿、副业兼职......,为大家打造了一套"从求职到跳槽"的一条龙服务,同时也为诸位准备了七折优惠码:3DoleNaE
,感兴趣的小伙伴可以点击:s.juejin.cn/ds/USoa2R3/了解详情!
一、分布式基础 - CAP理论
为了知识完整性,我们先来聊聊最基本的CAP
理论,先上张图:
当一个从逻辑上被视为整体的系统,拆散到多个节点部署时,则能称之为分布式系统,分布式领域中的CAP
理论,即是图中三个单词的首字母缩写组合,CAP
由三个指标组成:
C
:Consistency
(一致性);A
:Availability
(可用性);P
:Partition tolerance
(分区容错性)。
2.1、Consistency一致性
分布式和以往集中式部署的单体架构不同,它可以部署在多个节点上,以Redis
为例,如果只部署一个节点,则属于单机版本,采用主从、分片等集群模式部署,称之为分布式版本。所谓的一致性,即是在同一个分布式系统中,同一时刻的所有节点,能看到的数据完全相同。
比如2024-03-10 11:11:11
这个时间点,两个读取相同Key
的请求,一个落入到主节点A
,另一个落入到从节点B
,读到的数据必须完全相同,这保证了数据的完整性和准确性。反之,如果这两个请求拿到的结果不同,意味着Redis
主从集群这个分布式系统中,并没有实现数据一致性。
PS:同时,一致性还要求:一旦某个数据在分布式系统中的某个节点上被更新(如修改成新值),那么后续对该数据的读取操作,无论发生在哪个节点上,都应该返回更新后的值,即:所有节点在同一时间内,都能看到相同、且最新写入的值。
基于上述结论来问个问题:在MySQL
主从模式下,一个新值写入后还未提交,此时在从节点上读取到了老值,满足CAP
的一致性要求吗?
思考一分钟......
答案是满足,只要保证事务提交前,所有节点读到的数据都为老值,就满足一致性要求!因为带事务性质的场景中,写入动作的完成时机,并不是数据落到某个节点,而是整个事务被提交时,只有当事务被提交,才代表真正完成了数据写入。
2.2、Availability可用性
分布式系统因为部署在多个节点,虽然避免了单点故障问题,但也会增加整个系统的风险出错率,还是上面Redis
的例子,A、B、C
三个节点都有可能出现故障。而CAP
里的可用性,即是指分布式系统中,能够始终对请求做出响应,好比B
节点发生错误,整个Redis
服务依旧要能够正常处理并响应外部的请求,不能由于某一个或部分节点的损坏,导致整个系统陷入不可用状态。
可用性强调的是系统对外部请求的响应能力,具体来说,它要求系统能够在一定的时间内,对任何非失败的外部请求做出响应。这意味着,无论系统内部发生什么情况,只要外部用户发出请求,系统都应该尽快做出响应,即使回应的是拒绝服务或错误消息。因此,可用性关注的是系统对外部请求的响应速度和可靠性。
2.3、Partition tolerance分区容错性
组成分布式系统的多个节点,可以部署在任意节点上,这意味着我们可以用物理位置不同的服务器来部署系统,不同节点间的协调与数据同步,都依靠网络来完成。当组成分布式系统的多个节点,其中某个节点出现网络故障时(如网络中断、抖动、网络设备损坏等),对系统内其余正常的节点来说,无法通过网络感知到该节点,它成为无法通信的孤立区域,从而造成了网络分区。
PS:
CAP
里的网络分区,并非是网络编程里的局域网概念,而是指分布式系统内部,网络出现故障时,系统中的节点可能被分割成多个独立的子网络,这些子网络之间的通信被阻断,导致数据无法在它们之间自由流动。
CAP
里的P
分区容错性,就要求系统在这样的情况下仍然能够正常运行,即使系统内出现了多个分区,分布式系统依旧能正常运行,并对外提供基本的服务(至少一部分服务可用)。当然,某个节点掉线会造成孤立区域出现,系统内动态加入节点,同样也会导致分区问题。
透过表象看本质,因为新节点加入和老节点离开,都可以视为系统内部的网络分区,CAP
里的分区容错性,本质就是分布式系统对节点动态加入和离开的处理能力!
2.4、CAP三者选其二问题
上面对CAP
理论做了展开,讲清了其中的三个指标,简单总结下:
- 一致性要求所有节点在同一时间看到相同的数据;
- 可用性则要求系统能够始终对请求做出响应;
- 分区容错性则是指系统在遇到网络分区时,仍然能够保持一定的可用性和一致性。
虽然其中的A、P
在某些方面看起来相似,但它们关注的焦点并不相同,可用性侧重于系统对用户请求的响应能力,而分区容错性则更侧重于系统在出现网络分区时的表现,这里请大家一定要区分清楚。
好了,相信大家一定听过一句定理:CAP三个指标不可能同时做到,三者只能选其二 ,意味着只能有CA、AP、CP
三个组合,但大家仔细想想,你听说过市面上有保证CA
的分布式组件吗?肯定没有,Why
?
换个角度来看待上面提出的问题,假设我们自己要研发一款分布式组件,如果要保证CA
(一致性与可用性),该怎么实现?
可用性可以理解成高可用,高可用的前提是解决单点故障,因此我们可以考虑集群设计方案,使用多个节点来组成整个系统,当系统部分节点出现故障时,外部请求能自动转移到其他健康的节点上处理。通过这种方案,我们可以保证系统在节点故障时的可用性,不过这里又有个问题,如何感知节点是否健康?
设计健康检查机制!而最主流的方案则是心跳机制,就好比一个正常的人,一定会有心跳,换到程序设计中,一个正常的节点,必然也具备发送心跳包的能力。反之,如果一个节点发送不了心跳包,或者系统内其他节点收不到某个节点的心跳包,说明该节点已经处于故障状态,后续不用将请求转发到该节点。
保障了可用性后,接着来看看一致性,因为此时有多个节点,多个节点的数据一致该怎么实现?选择2PC、3PC
这类强一致方案,当外部往某个节点写入数据时,该节点触发数据同步机制,将写入的数据同步给所有节点,当所有都同步完成后,再给客户端返回写入成功,这样就能保证所有节点数据完全一致。
通过上述步骤,就设计出了一个简易版的CA
分布式组件,存在什么问题吗?问题很大,节点间的心跳检测、数据同步,需要依靠网络进行通信,先来看看心跳检测的问题:
①某个节点其实很健康,但发出的心跳包,因为网络抖动造成丢包,其他节点没收到就认为它故障了,这合理吗?不合理。
②某个节点发出的心跳包,部分节点收到了,部分节点没收到,一部分节点认为健康,一部分节点认为故障,从而造成了分区,怎么办?
再来看看保证一致性的数据同步方案,假设某个节点故障,又或者同步数据时的包丢失,导致系统内多个节点数据不一致,系统为了达成"数据一致态",会不断触发重试机制,造成外部请求阻塞,一直无法成功写入......
综上,最开始的设计思路,只是我们最理想的状态,但网络其实是个不可控因素,总会由于各种各样的原因造成故障出现。因此,在设计分布式系统时,网络故障带来的分区问题,一定要率先考虑,如果对分区问题没有容错性,代表系统内一个节点出现问题时,会造成整个系统无法正常运行,这也是为什么只有保证AP、CP
的分布式组件,没有保证CA
的原因。
PS:有没有能保证
CA
的组件呢?答案是有,就是单机版本,毕竟只有一个节点,数据写入成功后,就能保证多个外部请求看到的数据都相同;同样,只要这一个节点活着,系统就肯定可用,也是一种"狭义上的可用"。
综上,分布式肯定要保证P
,无法保证P
的分布式组件,只能被称为"部署在多个节点上的单体系统",为此,对于CAP
那幅图,正确的画法应该是这样的:
虽然很多人在聊CAP
时,说到三选二,可是分布式系统中,实际只能在A、C
里选,不存在CA
这个组合! 好了,回过头,再来看为什么CAP
不能一起实现呢?
分布式系统中的通信离不开网络,而恰恰网络出现故障是常事,在出现分区问题时,节点间的通信会受到严重阻碍,来看个例子:
如上图所示,该系统由A、B、C
三个节点组成,其中由于C
节点故障导致分区问题出现。如果要完全满足CAP
里的一致性要求,意味着当外部写入数据时,A
节点必须等到C
节点同步完成,才能给客户端返回写入成功,可此时C
节点已经挂了,注定着数据写不进去......
假设此时出现读取该数据的请求怎么办?此时只有两种办法:
- ①放弃可用性:等待所有节点的数据都达到一致状态,保证任意节点返回的数据都相同,可这时系统必然无法及时响应;
- ②放弃一致性:给客户端返回已经写入进
A、B
的新数据,但后续C
节点恢复,请求去到C
时,会出现读取到的数据不一致;
通过这个例子,相信大家一定明白了C、A
之间为何只能选一个,保证可用性(AP
),虽然可以快速响应外部请求,但无法做到任意时间点、所有节点数据的一致;保证一致性(CP
),就需要等到所有节点数据达到一致,从而造成系统无法及时响应外部请求,可用性降低。
2.5、为何90%分布式系统无需实践CAP理论?
在一开始我就给了一个定论:90%以上的分布式系统,不需要实践CAP理论,下面展开聊聊。
前面给过分布式系统的定义:当一个从逻辑上被视为整体的系统,拆散到多个节点部署时,则能称之为分布式系统 ,而分布式系统也可以分为两类,一类是带存储性质的,另一类则是非存储性质的(类似于Kubernetes
里的无状态Pod
),而90%
以上的开发者,负责开发的分布式系统都属于后者,啥意思?
包括我在内的大部分人,平时都在做业务开发,而一个业务性质的分布式系统,需要实践CAP
理论吗?并不需要,因为业务系统本身只有逻辑处理,没有数据存储,自然不存在所谓的数据一致性(数据最终是存在各种组件里,如数据库)。真正需要实践CAP
理论的分布式系统,主要是带存储性质的分布式系统,如Redis、MQ
、数据库、注册中心......
以CAP
理论应用最广的"注册中心"领域为例,正是因为它需要存储服务注册的信息,所以才需要实践CAP
理论,也只有在研发这类分布式组件时,才需要在一致性、可用性间做取舍 。作为业务开发者,我们只需要站在已有组件上做考量,想清楚到底用CP
,还是AP
就足够了。
2.6、在业务中应用CAP思想
除开研发分布式存储组件要实践CAP
理论外,做业务类型的分布式系统,哪些场景会用到CAP
思想呢?下面举些例子说明。
通过传统的数据库,搭建多库架构(主从、多主)时,选择同步复制,说明更关注数据一致性(CP);选择异步复制,则代表更关注响应速度(AP),具体原因可参考《MySQL主从原理篇》。
使用分布式锁时,在Redis、Zookeeper
都为主从集群模式的情况下,如果选择Redis
来实现分布式锁,代表更关注响应速度(AP);如果选用ZK
来实现,则代表更关注一致性(CP
)。
微服务项目,选择服务注册中心时,......
通常而言,如果更关注性能,可以选择AP
类型的组件 ,毕竟AP
舍弃了数据一致性,只需等数据写入主节点,就可以向客户端返回写入成功,这无疑会大幅度提升请求的响应时间。但AP
组件存在数据丢失的风险,比如数据刚写进主节点,还没来得及同步给从机,主节点就宕机了,此时切换上来的新主,就会丢失这部分数据。
反之,如果更关注数据安全性,则可选用CP
类型的组件 ,CP
放弃了可用性,优先保证数据一致性,当数据写进主节点后,往往需要等到从节点也写入完成,最后才能给客户端返回写入成功,这显然会拖慢响应速度,因为从机未写入成功,请求则需要一直阻塞等待。不过这种模式有个好处,就是当主节点宕机时,无论是哪台从机成为新主,都能保证拥有最完整的数据,不会存在数据丢失现象。
最后,技术没有绝对的好坏,实际项目中究竟选什么类型的组件,不能随心而定,而是得先深度了解业务场景,再进行技术选型,不同的业务关注面并不一样。
二、分布式核心 - BASE理论
前面提到分布式系统不可完全满足CAP
的三个指标,但完全舍弃一致性,或可用性也绝对不行,怎么办?
有些熟悉分布式理论的小伙伴,下意识会喊道:BASE理论 ,这个答案对吗?不一定对,虽然很多讲解CAP
的资料,一提到CAP
,自然而然就会扯上BASE
理论,美名其曰:BASE理论可以让我们不必在A、C中做抉择,而是可以实现部分的A和C ;又或者说:BASE理论是在CAP基础上,满足AP后对C方面的拓展与延申。
你觉得上面这类说法正确吗?很多人都会觉得对,因为大部分人当初就是这么学的......
如果你对BASE
理论也是上述两种理解,那......,或许你一直都未曾真正搞懂CAP
与BASE
这两个分布式理论!
当然,为了更好的"掰正大家的观念",以及照顾分布式知识薄弱的小伙伴,我们先简单讲讲
BASE
理论。
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
(最终一致性);
为了后面更好的讲述CAP
与BASE
理论之间的关系,我们先快速认识/回顾一下上面这三个概念。
2.1、Basically Available基本可用
基本可用这个概念很好理解,比如咱们买了一台"船新版的遥遥领先",因为是新机,所以功能、硬件、系统都没问题,属于"完全可用状态"。
众所周知,"遥遥领先"不算便宜,小竹这个人呢,比较好面子,也想要一台,可是买不起怎么办?正巧此时小竹的好朋友:大熊猫,昨天刚到的全新版,不小心从十八楼摔下去了,于是想换台新的。小竹听闻此事后,眼珠一转、双手一拍,怒斥千元巨资!全款!不分期!将其成功拿下!
拿到手后,虽说这屏幕有点稀碎、按键有点松、机身有点歪......,但好歹开机还能亮啊!毕竟有句话说得好:
好了,到此时,小竹手里这台"战损版-遥遥领先",尽管卖相不咋滴,但功能方面基本没问题,凑合凑合也成,这就属于"基本可用状态"。
换成分布式领域亦是同理,当系统出现故障或意外情况时,允许降低部分可用性,保证系统基本可用,而这类情景十分常见,比如大促活动期间:
- 熔断/限流:当系统负载达到指定阈值时,新到来的请求会被拒绝(返回配置好的提示语);
- 关闭非核心业务:如禁止大数据报表导出、停掉部分非关键性的任务,给核心业务腾出资源;
- 服务降级......
基本可用的核心思想就是:当系统出现故障或意外情况时,允许放弃掉部分可用性,保证核心功能可用,或能响应外部请求(返回自定义的错误提示也可以)即可。
PS:基本可用不是"不可用",就好比一台遥遥领先,已经摔成了十八瓣,还能算基本可用吗?当然不行......
2.2、Soft state软状态
软状态在有些地方也叫弱状态,是指允许系统存在中间状态,并且该中间状态不会影响系统整体的可用性。
这就好比传统关系型数据库里的事务机制,假设没有事务机制,写入数据就只有成功、失败两种状态:
- 成功:数据成功保存到数据库,当出现读取请求时,能正常读到写入(或更新)后的值;
- 失败:数据未保存到数据库,当出现读取请求时,只能看到之前的值,或看不到值;
而有了事务机制后,在成功与失败之间,多了一个中间态,即:数据写入成功,事务暂未提交,这个中间态的存在,也不会影响数据库整体的可用性。
2.3、Eventually consistent最终一致性
在BASE
理论中,最终一致性是和软状态绑定的,两者需要结合在一起理解。前面说到,软状态允许系统内存在中间态,但要记住:如果这个中间态,一直不变成终态,而是卡在那里的话,就会影响系统整体的可用性。
啥意思?结合上面给出的数据库事务例子来说,如果一个事务处于中间态(写入成功,但一直不提交);当其他请求继续对该事务操作的数据行进行更新时,就会被阻塞,从而影响了系统的可用性,这就违背了"软状态"的定义。
软状态除开强调允许存在中间态外,还声明了该中间态不会影响系统整体的可用性,意味着这个中间态,注定只能短暂存在,在一定时间后,肯定会变成终态!
BASE
理论里的最终一致性,就是为了填补"卡在中间态不变化"这个逻辑漏洞,代表系统内出现中间态时,经过一定时间推移后,最终肯定能达到一致的状态(终态)。当然,这样讲或许有点难理解,来套入一个常见的分布式场景:
分布式系统中,有个落地页渲染所需的数据比较复杂(如数据看板),得调用多个服务的接口进行组装才能返回,这个过程非常耗时,可能需要几分钟之久。
面对这种情况一般会怎么做?通常会提前聚合好数据,而后放入到Redis
或ES
中进行缓存,以便后续能快速拿到渲染所需的数据。
问题来了,现在参与聚合的一部分数据更新了,那提前聚合出的结果就会不一致,怎么办?先来看看现在的终态:
- ①参与聚合的基础数据更新成功,聚合结果实时更新;
- ②参与聚合的基础数据更新失败,聚合结果保持不变。
之前仅有这两个终态,如果要进行满足状态,既耗费资源,又会响应缓慢,为此就可以允许中间态存在,保证最终一致性即可(如开启定时任务,定时聚合并刷新缓存中的数据)。
总之,只要参与聚合的基础数据不再更新,随着时间推移,定时任务肯定会把缓存中的结果,刷新成"最新的基础数据聚合后的结果",让系统内的数据达到一致状态,这就是最终一致性!
三、固有认知的拨乱反正
上节简单讲了下BASE
理论的定义,接下来回到一开始的问题:为什么我说BASE
理论和CAP
理论没太大关系?
想要讲明白这个问题,我们得重新认识下CAP
理论,CAP
理论更偏向于分布式存储这个领域,而且并非所有分布式存储领域都会用到CAP
,只有哪些存在数据副本的分布式存储场景,如所谓的主从集群、多主集群、镜像集群、复制集群、副本集群......等场景时,才会用到CAP
,因为这些情况下才会涉及到数据同步,才会涉及到数据一致性问题!
而分库分表、Redis-Cluster、MongoDB-Cluster......
这类分片式存储的场景中,尽管也都是分布式存储场景,可由于每个节点存储的数据本来就不同,自然不存在所谓的数据一致性问题,也就自然不存在所谓的CAP
实践,这点希望大家一定要弄清楚,否则说明你并未真正理解CAP
思想。
PS:
CAP
理论最早是在1998
年由Eric Brewer
提出,这是26
年前的事了,当时分布式领域最前沿的技术,就是主从集群这一块,因此该理论更多针对的是这个方向。
回到一开始讲的CAP
一致性,其定义是:所有节点在同一时间内,都能看到相同、且最新写入的值 ,注意看"所有节点看到相同值"这句话,什么情况下,所有节点的数据才需要相同?即上面提到的"数据副本"场景。也就是说,CAP
理论里的一致性,关注点是:副本节点数据同步间的一致性。
再回头来看BASE
理论里面的最终一致性,这里的一致性和CAP
里的一致性,两者是一回事吗?并不是,BASE
里的一致性维度更高,关注点是:分布式系统里的状态一致性,即中间态最终会演变成终态(不理解没关系,后续会举例展开)。
既然BASE
并非CAP
的延申理论,那它究竟是个啥?我相信绝大多数小伙伴,并没有认真看过ACM
平台上那篇BASE
论文,我带大家来仔细看,其中有一段是这样的:
看图中圈出来的这段话,段落标题为An ACID Alternative
,英语好的小伙伴可以翻译一下,啥意思?ACID的替代品 ,想要理解论文这段话,我们先来说说ACID
理论(在论文中也有前置说明)。
3.1、ACID理论
在之前《全解MySQL专栏》的《事务篇》中,曾对传统关系型数据库的ACID
理论做了详细讲述,ACID
对应着四个特性。
Atomicity
原子性:将组成事务的多个操作,视为一个不可分割的整体,要么全部成功,要么全部失败,不能只生效一部分;Consistency
一致性:事务执行前后,数据库只能从一个一致状态转为另一个一致状态,即事务执行对整体数据产生的变化是一致的;Isolation
隔离性:多事务并发执行时,各事务之间不能被其他事务干扰,就类如每个事务都在独立的沙箱中执行;Durability
持久性:一旦事务提交,无论发生什么状况(如故障、宕机等),该事务对数据的变更都会永久保存。
这里重点把一致性拉出来聊聊,ACID
中的一致性定义为:一个事务执行前后,数据库只能从一个一致状态转变为另一个一致状态,这句话有点绕对吧?其实在之前的事务篇中举例解释过,现在再以"转账"这个典型的事务场景案例来说:
需求:小竹向熊猫账户里转
1000W
元。数据库现状:小竹账户余额为
1001W
元,熊猫账户余额为888W
元。
面对上述这个需求,对应的转账事务为:小竹账户先减1000W
,熊猫账户再加1000W
。所谓的一致性,就是这个事务执行前后,数据库里的整体数据变化一致,对其进行拆解:
- 初始态:小竹账户余额为
1001W
元,熊猫账户余额为888W
元。 - 结果态:小竹账户余额为
1W
元,熊猫账户余额为1888W
元。
如果该事务执行失败,数据库应该回到初始态,这对数据库来说状态没有改变;而当该事务执行成功时,数据库应该来到结果态,此时小竹+熊猫的账户余额,合计还是1889W
,对数据库整体状态来说,也并未改变,这就是ACID
里所谓的一致性:事务执行前后只会从一个一致状态转为另一个一致状态。
当然,上面这段拆解对有些小伙伴来说,或许还是有点绕,那再举反例说明,即聊聊"不一致"的场景:
- ①小竹账户减
1000W
,余额1W
,但熊猫账户没有加,还是888W
,合计889W
。 - ②熊猫账户加
1000W
,余额1888W
,但小竹账户没有减,还是1001W
,合计2889W
。
上述则是两种没有保证一致性的场景,事务执行前后,整体数据的变化并不一致,造成数据库陷入了一种不正确的状态。
其他事务场景也是同理,下单时,[库存数减一,订单数加一]、[库存数不变,订单数不变],这属于一致状态,出现这两种情景之外的现象,则说明数据库未保证一致性。
PS:其实有点类似于能量守恒定律,能量在转化或转移的过程中,总量保持不变......
上面的转账事务例子,如果刚执行完减钱动作,接着数据库发生故障/宕机,恢复后,又会导致数据不一致。所以完全满足ACID
里的一致性,也得考虑数据库故障/宕机对一致性的影响(这里不做展开,感兴趣可参考《MySQL事务恢复机制》)。
3.2、重新理解BASE理论
OK,简单回顾了一下ACID
理论,现在再来看BASE
论文里的那段:ACID的替代品。
来简单翻译下这段话:
如果
ACID
为分布式数据库提供了一致性选择,那如何在分布式数据库里保证可用性呢?一个答案就是BASE
理论。
BASE
理论与ACID
完全相反,ACID
是悲观思想,在每次操作结束时强制要求保持一致性(即事务结束必须落入终态)。而BASE
是乐观思想,可以接受数据库一致性处于不断变化的状态。虽然听起来......
翻译了前两句话,从这两句话里不难看出,BASE
理论的最终一致性,并不是用来对标CAP
的一致性,而是与ACID
里的一致性对等,允许事务执行后出现"中间态"。
来个分布式事务的经典例子:
商品库里有个"黄金竹子",库存数为8888
,现在有个对应的下单请求(暂时抛开实际下单场景中的复杂流程),对应的事务则为[库存减一、创建一条订单],如果遵循ACID
里的一致性,此时只能有两种状态:
- 初始态:黄金竹子库存为
8888
,没有对应的订单。 - 结果态:黄金竹子库存为
8887
,有一条对应的订单。
在分布式场景中,因为商品服务和订单服务是分开部署的,所以要通过网络调用"创建订单"的接口,可网速再怎么快,就算是内网环境,扣完库存到创建订单之间也会有时间差,这就导致分布式系统中会出现短暂的"不一致":
- 中间态:黄金竹子库存为
8887
,没有对应的订单记录。
如果在这个中间态期间,出现一个读取"黄金竹子库存数"的请求,就会看到8887
这个库存数。因此,正是因为这个中间态的存在,就违背了ACID
原则,ACID
定义的一致性不接受这种现象。
再回头看,BASE
理论中,软状态的定义允许存在中间态,最终一致性的定义接受延迟性,所以这也是为什么BASE
理论的论文中说:BASE是分布式场景中ACID理论代替品的本质原因!
好了,再把BASE
理论中的最终一致性,代入到上面的案例中,如果下单事务执行结束,流向会有两个:
- 新增订单成功,分布式事务可以提交,中间态流转到结果态。
- 新增订单失败,分布式事务触发回滚,中间态撤回到初始态。
综上所述,不管中间态是流转到结果态,还是撤回到初始态,最终系统内整体数据的变化是一致的,这也就是所谓的"最终一致性"。
PS:分布式事务如何保证最终一致性,后续会有单独的分布式事务篇章进行讲述,本文不再展开。
3.3、为什么很多人聊CAP会扯到BASE?
上阶段证实了一开始提出的观点,BASE
理论的初衷,是为了成为分布式系统中ACID
理论的替代者,跟CAP
理论没什么太大关系。可为什么一抓一大把的资料,都在潜意识的表达"BASE
理论是对CAP
理论的补充"这种观点呢?
因为CAP
理论只能在A、C
中,二者选其一,选了A
不能选C
,要了C
不能要A
。可是有些人又想要A
,又想要C
,于是就把BASE
理论抬出来做解释,声称:BASE
理论可以让我们不必在A、C
中做抉择,而是可以实现部分的A
和C
!
奔着三人成虎的原则,你这样喊,他也这样喊,我还这样喊......,越来越多的人就都认为BASE
是CAP
的补充版......。其实不然,就连CAP、BASE
两个理论中的一致性定义都不同,CAP的一致性,关注的是数据一致性;BASE的一致性,关注的是状态一致性,真正的对应关系如下:
CAP
一致性 ≠BASE
一致性、BASE
一致性 =ACID
一致性。
同时,两个理论除开一致性的定义不同外,甚至连"可用性"的定义也不一样。CAP
中的可用性,是指主从这类集群,某个节点出现故障时,不会影响系统整体的运行,如:
上图这个MySQL
多主集群,两个节点互为对方的从机,业务应用任意连接某个节点,都可以读写完整数据。当某个节点出现故障时,业务应用都可以连接另一个节点,正常读写数据,这是CAP
关注的可用性(响应速度也是CAP
可用性的一种表现)。
反观BASE
理论,其中定义的"基本可用",更多针对于分片式领域,来看论文中这段话:
翻译过来的意思就是:如果用户数据跨五个数据库服务器存储,根据BASE
理论,一台数据库出现故障,仅仅只会影响到20%
的用户 。用户数据跨五个数据库存储,意味着什么?看过之前《MySQL分库分表篇》的小伙伴应该清楚,这代表着用户数据做了水平拆分,所有用户数据会根据特定的规则,分发到五个不同的节点中分开存储!
正因如此,才会给出"一个节点故障只会影响20%
用户"这个结论,毕竟存储其他80%
数据的四个节点此时正常。所以,BASE
理论中的基本可用,并不是指主从这类"全量式复制"的集群,而是针对"分片式"的集群。
PS:
BASE
论文中还提到按功能拆分的思想,其中称为"功能组",就类似如今的按业务分库,感兴趣可去阅读原文。
好了,既然CAP、BASE
的定义都不同,为什么这么多人会把这两个扯上关系呢?究其根本原因是:BASE理论中定义的三个概念,几乎能覆盖大多数分布式场景!
BASE
理论的最终一致性,虽然是指状态一致性,可是也能套入CAP
的数据一致性,比如主从集群数据复制,如果选同步-复制,太影响性能;如果选异步-复制,可以最快程度上响应请求,同时数据也会同步给从机,虽然有时间差,但只要集群内节点正常运转,所有节点的数据,最终都会保持一致。
异步-复制模式中,如果数据刚落入主节点,而后主节点就宕机,尽管选上来的新主会丢失一部分数据,但也不是不能用对吧?为此,
BASE
定义的基本可用,也能套上CAP
的可用性~
BASE
理论比CAP
晚诞生十年,两者所处的时代背景不同,BASE
定义的维度比CAP
要高,所以在一定程度上,BASE
能向下兼容CAP
,这也就让许多人觉得BASE
是CAP
理论的拓展。
四、总结
通篇看完,大家会发现CAP
理论并没有那么强大,它的作用更多局限于分布式存储领域,同时也并非整个分布式存储领域都会用到CAP
思想,如今主流的分片式存储架构,就摒弃了传统的CAP
模型。
而BASE
理论,则广泛运用于各类分布式系统,其中定义的三个概念,覆盖了分布式系统的大多数场景,比如微服务中的服务降级、熔断、限流机制,就是"基本可用"的一种体现。
最后,在之前的文章中,我一直强调大家不要听风就是雨,学习技术时,务必保持审慎怀疑的态度!比如本文讨论的BASE
理论,90%
资料都在告诉你,它是CAP
的拓展理论,可事实却并非如此。为此,大家学习时要多思考、做验证,这样才能真正的把技术学透彻。
PS:后续所有文章会陆续同步到个人公众号:竹子爱熊猫,想在微信上便捷阅读的小伙伴可搜索关注~