(二)漫谈分布式之理论篇:用刁钻的手法掰正你那学歪的CAP与BASE理论!

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
简介: 大多数讲分布式的资料、课程,虽然在一开始就会先讲述CAP理论,但大家仔细想想,你在做分布式项目时,落地过这个基础理论吗?相信包括我在内,以及90%以上的开发者,没有,至于为何,本文来从不一样的角度好好唠唠CAP,以及另一个著名的BASE理论~

引言

在《系统架构演进篇》中,我们站在全景角度出发,以十多年前国内互联网时代的兴起为起点,一步步讲述了后端领域技术架构的演进过程,从起初的单体式架构,再到如今的分布式、微服务、云原生,技术发展始终不曾停下前进的脚步。

科技进步离不开理论支撑,而当下大行其道的分布式架构,透过繁荣昌盛表象,底层同样离不开诸多分布式理论撑持。当然,相信诸位在学习分布式相关技术时,必然学到过两个分布式领域中的基础理论,即:CAP与BASE理论

不过我先问大家一个问题:什么系统需要实践CAP理论

很多人下意识会说出:分布式系统,这个答案对吗?并不尽然,其实大家所能接触的大部分“分布式系统”,并不需要实践CAP理论

我为何给出上面这句结论?因为大多数讲分布式的资料、课程,虽然在一开始就会先讲述CAP理论,但大家仔细想想,你在做分布式项目时,落地过这个基础理论吗?相信包括我在内,以及90%以上的开发者,没有,至于为何,本文来从不一样的角度好好唠唠CAP,以及另一个著名的BASE理论~

当然,BASE会广泛运用于分布式系统中,但绝大多数人并没有真正理解CAPBASE理论之间的关系,注意,我这里说的“绝大多数人”覆盖面可能达到90%,为何这么说?本文会一点点抽丝剥茧,“掰正”大家的固有认知。

一、分布式基础 - CAP理论

为了知识完整性,我们先来聊聊最基本的CAP理论,先上张图:

001.png

当一个从逻辑上被视为整体的系统,拆散到多个节点部署时,则能称之为分布式系统,分布式领域中的CAP理论,即是图中三个单词的首字母缩写组合,CAP由三个指标组成:

  • CConsistency(一致性);
  • AAvailability(可用性);
  • PPartition tolerance(分区容错性)。

1.1、Consistency一致性

分布式和以往集中式部署的单体架构不同,它可以部署在多个节点上,以Redis为例,如果只部署一个节点,则属于单机版本,采用主从、分片等集群模式部署,称之为分布式版本。所谓的一致性,即是在同一个分布式系统中,同一时刻的所有节点,能看到的数据完全相同

002.png

比如2024-03-10 11:11:11这个时间点,两个读取相同Key的请求,一个落入到主节点A,另一个落入到从节点B,读到的数据必须完全相同,这保证了数据的完整性和准确性。反之,如果这两个请求拿到的结果不同,意味着Redis主从集群这个分布式系统中,并没有实现数据一致性。

PS:同时,一致性还要求:一旦某个数据在分布式系统中的某个节点上被更新(如修改成新值),那么后续对该数据的读取操作,无论发生在哪个节点上,都应该返回更新后的值,即:所有节点在同一时间内,都能看到相同、且最新写入的值

基于上述结论来问个问题:在MySQL主从模式下,一个新值写入后还未提交,此时在从节点上读取到了老值,满足CAP的一致性要求吗?

思考一分钟……

答案是满足,只要保证事务提交前,所有节点读到的数据都为老值,就满足一致性要求!因为带事务性质的场景中,写入动作的完成时机,并不是数据落到某个节点,而是整个事务被提交时,只有当事务被提交,才代表真正完成了数据写入。

1.2、Availability可用性

分布式系统因为部署在多个节点,虽然避免了单点故障问题,但也会增加整个系统的风险出错率,还是上面Redis的例子,A、B、C三个节点都有可能出现故障。而CAP里的可用性,即是指分布式系统中,能够始终对请求做出响应,好比B节点发生错误,整个Redis服务依旧要能够正常处理并响应外部的请求,不能由于某一个或部分节点的损坏,导致整个系统陷入不可用状态。

可用性强调的是系统对外部请求的响应能力,具体来说,它要求系统能够在一定的时间内,对任何非失败的外部请求做出响应。这意味着,无论系统内部发生什么情况,只要外部用户发出请求,系统都应该尽快做出响应,即使回应的是拒绝服务或错误消息。因此,可用性关注的是系统对外部请求的响应速度和可靠性。

1.3、Partition tolerance分区容错性

组成分布式系统的多个节点,可以部署在任意节点上,这意味着我们可以用物理位置不同的服务器来部署系统,不同节点间的协调与数据同步,都依靠网络来完成。当组成分布式系统的多个节点,其中某个节点出现网络故障时(如网络中断、抖动、网络设备损坏等),对系统内其余正常的节点来说,无法通过网络感知到该节点,它成为无法通信的孤立区域,从而造成了网络分区。

PS:CAP里的网络分区,并非是网络编程里的局域网概念,而是指分布式系统内部,网络出现故障时,系统中的节点可能被分割成多个独立的子网络,这些子网络之间的通信被阻断,导致数据无法在它们之间自由流动。

CAP里的P分区容错性,就要求系统在这样的情况下仍然能够正常运行,即使系统内出现了多个分区,分布式系统依旧能正常运行,并对外提供基本的服务(至少一部分服务可用)。当然,某个节点掉线会造成孤立区域出现,系统内动态加入节点,同样也会导致分区问题。

透过表象看本质,因为新节点加入和老节点离开,都可以视为系统内部的网络分区,CAP里的分区容错性,本质就是分布式系统对节点动态加入和离开的处理能力!

1.4、CAP三者选其二问题

上面对CAP理论做了展开,讲清了其中的三个指标,简单总结下:

  • 一致性要求所有节点在同一时间看到相同的数据;
  • 可用性则要求系统能够始终对请求做出响应;
  • 分区容错性则是指系统在遇到网络分区时,仍然能够保持一定的可用性和一致性。

虽然其中的A、P在某些方面看起来相似,但它们关注的焦点并不相同,可用性侧重于系统对用户请求的响应能力,而分区容错性则更侧重于系统在出现网络分区时的表现,这里请大家一定要区分清楚。

好了,相信大家一定听过一句定理:CAP三个指标不可能同时做到,三者只能选其二,意味着只能有CA、AP、CP三个组合,但大家仔细想想,你听说过市面上有保证CA的分布式组件吗?肯定没有,Why

换个角度来看待上面提出的问题,假设我们自己要研发一款分布式组件,如果要保证CA(一致性与可用性),该怎么实现?

可用性可以理解成高可用,高可用的前提是解决单点故障,因此我们可以考虑集群设计方案,使用多个节点来组成整个系统,当系统部分节点出现故障时,外部请求能自动转移到其他健康的节点上处理。通过这种方案,我们可以保证系统在节点故障时的可用性,不过这里又有个问题,如何感知节点是否健康?

设计健康检查机制!而最主流的方案则是心跳机制,就好比一个正常的人,一定会有心跳,换到程序设计中,一个正常的节点,必然也具备发送心跳包的能力。反之,如果一个节点发送不了心跳包,或者系统内其他节点收不到某个节点的心跳包,说明该节点已经处于故障状态,后续不用将请求转发到该节点。

保障了可用性后,接着来看看一致性,因为此时有多个节点,多个节点的数据一致该怎么实现?选择2PC、3PC这类强一致方案,当外部往某个节点写入数据时,该节点触发数据同步机制,将写入的数据同步给所有节点,当所有都同步完成后,再给客户端返回写入成功,这样就能保证所有节点数据完全一致。

通过上述步骤,就设计出了一个简易版的CA分布式组件,存在什么问题吗?问题很大,节点间的心跳检测、数据同步,需要依靠网络进行通信,先来看看心跳检测的问题:

①某个节点其实很健康,但发出的心跳包,因为网络抖动造成丢包,其他节点没收到就认为它故障了,这合理吗?不合理。\
②某个节点发出的心跳包,部分节点收到了,部分节点没收到,一部分节点认为健康,一部分节点认为故障,从而造成了分区,怎么办?

再来看看保证一致性的数据同步方案,假设某个节点故障,又或者同步数据时的包丢失,导致系统内多个节点数据不一致,系统为了达成“数据一致态”,会不断触发重试机制,造成外部请求阻塞,一直无法成功写入……

综上,最开始的设计思路,只是我们最理想的状态,但网络其实是个不可控因素,总会由于各种各样的原因造成故障出现。因此,在设计分布式系统时,网络故障带来的分区问题,一定要率先考虑,如果对分区问题没有容错性,代表系统内一个节点出现问题时,会造成整个系统无法正常运行,这也是为什么只有保证AP、CP的分布式组件,没有保证CA的原因。

PS:有没有能保证CA的组件呢?答案是有,就是单机版本,毕竟只有一个节点,数据写入成功后,就能保证多个外部请求看到的数据都相同;同样,只要这一个节点活着,系统就肯定可用,也是一种“狭义上的可用”。

综上,分布式肯定要保证P,无法保证P的分布式组件,只能被称为“部署在多个节点上的单体系统”,为此,对于CAP那幅图,正确的画法应该是这样的:

003.png

虽然很多人在聊CAP时,说到三选二,可是分布式系统中,实际只能在A、C里选,不存在CA这个组合! 好了,回过头,再来看为什么CAP不能一起实现呢?

分布式系统中的通信离不开网络,而恰恰网络出现故障是常事,在出现分区问题时,节点间的通信会受到严重阻碍,来看个例子:

004.png

如上图所示,该系统由A、B、C三个节点组成,其中由于C节点故障导致分区问题出现。如果要完全满足CAP里的一致性要求,意味着当外部写入数据时,A节点必须等到C节点同步完成,才能给客户端返回写入成功,可此时C节点已经挂了,注定着数据写不进去……

假设此时出现读取该数据的请求怎么办?此时只有两种办法:

  • ①放弃可用性:等待所有节点的数据都达到一致状态,保证任意节点返回的数据都相同,可这时系统必然无法及时响应;
  • ②放弃一致性:给客户端返回已经写入进A、B的新数据,但后续C节点恢复,请求去到C时,会出现读取到的数据不一致;

通过这个例子,相信大家一定明白了C、A之间为何只能选一个,保证可用性(AP),虽然可以快速响应外部请求,但无法做到任意时间点、所有节点数据的一致;保证一致性(CP),就需要等到所有节点数据达到一致,从而造成系统无法及时响应外部请求,可用性降低。

1.5、为何90%分布式系统无需实践CAP理论?

在一开始我就给了一个定论:90%以上的分布式系统,不需要实践CAP理论,下面展开聊聊。

前面给过分布式系统的定义:当一个从逻辑上被视为整体的系统,拆散到多个节点部署时,则能称之为分布式系统,而分布式系统也可以分为两类,一类是带存储性质的,另一类则是非存储性质的(类似于Kubernetes里的无状态Pod),而90%以上的开发者,负责开发的分布式系统都属于后者,啥意思?

包括我在内的大部分人,平时都在做业务开发,而一个业务性质的分布式系统,需要实践CAP理论吗?并不需要,因为业务系统本身只有逻辑处理,没有数据存储,自然不存在所谓的数据一致性(数据最终是存在各种组件里,如数据库)。真正需要实践CAP理论的分布式系统,主要是带存储性质的分布式系统,如Redis、MQ、数据库、注册中心……

CAP理论应用最广的“注册中心”领域为例,正是因为它需要存储服务注册的信息,所以才需要实践CAP理论,也只有在研发这类分布式组件时,才需要在一致性、可用性间做取舍。作为业务开发者,我们只需要站在已有组件上做考量,想清楚到底用CP,还是AP就足够了。

1.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理论也是上述两种理解,那……,或许你一直都未曾真正搞懂CAPBASE这两个分布式理论

当然,为了更好的“掰正大家的观念”,以及照顾分布式知识薄弱的小伙伴,我们先简单讲讲BASE理论。

005.png

Basically Available、Soft state、Eventually consistent简称为BASE理论,该理论最早源于ACM《Base: An Acid Alternative》这篇论文,由eBay系统架构师Dan Pritchett2008年发布,该理论提出了三个概念:

  • BABasically Available(基本可用);
  • SSoft state(软状态);
  • EEventually consistent(最终一致性);

为了后面更好的讲述CAPBASE理论之间的关系,我们先快速认识/回顾一下上面这三个概念。

2.1、Basically Available基本可用

基本可用这个概念很好理解,比如咱们买了一台“船新版的遥遥领先”,因为是新机,所以功能、硬件、系统都没问题,属于“完全可用状态”。

众所周知,“遥遥领先”不算便宜,小竹这个人呢,比较好面子,也想要一台,可是买不起怎么办?正巧此时小竹的好朋友:大熊猫,昨天刚到的全新版,不小心从十八楼摔下去了,于是想换台新的。小竹听闻此事后,眼珠一转、双手一拍,怒斥千元巨资!全款!不分期!将其成功拿下!

拿到手后,虽说这屏幕有点稀碎、按键有点松、机身有点歪……,但好歹开机还能亮啊!毕竟有句话说得好:

006.png

好了,到此时,小竹手里这台“战损版-遥遥领先”,尽管卖相不咋滴,但功能方面基本没问题,凑合凑合也成,这就属于“基本可用状态”。

换成分布式领域亦是同理,当系统出现故障或意外情况时,允许降低部分可用性,保证系统基本可用,而这类情景十分常见,比如大促活动期间:

  • 熔断/限流:当系统负载达到指定阈值时,新到来的请求会被拒绝(返回配置好的提示语);
  • 关闭非核心业务:如禁止大数据报表导出、停掉部分非关键性的任务,给核心业务腾出资源;
  • 服务降级……

基本可用的核心思想就是:当系统出现故障或意外情况时,允许放弃掉部分可用性,保证核心功能可用,或能响应外部请求(返回自定义的错误提示也可以)即可

PS:基本可用不是“不可用”,就好比一台遥遥领先,已经摔成了十八瓣,还能算基本可用吗?当然不行……

2.2、Soft state软状态

软状态在有些地方也叫弱状态,是指允许系统存在中间状态,并且该中间状态不会影响系统整体的可用性

这就好比传统关系型数据库里的事务机制,假设没有事务机制,写入数据就只有成功、失败两种状态:

  • 成功:数据成功保存到数据库,当出现读取请求时,能正常读到写入(或更新)后的值;
  • 失败:数据未保存到数据库,当出现读取请求时,只能看到之前的值,或看不到值;

而有了事务机制后,在成功与失败之间,多了一个中间态,即:数据写入成功,事务暂未提交,这个中间态的存在,也不会影响数据库整体的可用性。

2.3、Eventually consistent最终一致性

BASE理论中,最终一致性是和软状态绑定的,两者需要结合在一起理解。前面说到,软状态允许系统内存在中间态,但要记住:如果这个中间态,一直不变成终态,而是卡在那里的话,就会影响系统整体的可用性

啥意思?结合上面给出的数据库事务例子来说,如果一个事务处于中间态(写入成功,但一直不提交);当其他请求继续对该事务操作的数据行进行更新时,就会被阻塞,从而影响了系统的可用性,这就违背了“软状态”的定义。

软状态除开强调允许存在中间态外,还声明了该中间态不会影响系统整体的可用性,意味着这个中间态,注定只能短暂存在,在一定时间后,肯定会变成终态

BASE理论里的最终一致性,就是为了填补“卡在中间态不变化”这个逻辑漏洞,代表系统内出现中间态时,经过一定时间推移后,最终肯定能达到一致的状态(终态)。当然,这样讲或许有点难理解,来套入一个常见的分布式场景:

分布式系统中,有个落地页渲染所需的数据比较复杂(如数据看板),得调用多个服务的接口进行组装才能返回,这个过程非常耗时,可能需要几分钟之久。

面对这种情况一般会怎么做?通常会提前聚合好数据,而后放入到RedisES中进行缓存,以便后续能快速拿到渲染所需的数据。

问题来了,现在参与聚合的一部分数据更新了,那提前聚合出的结果就会不一致,怎么办?先来看看现在的终态:

  • ①参与聚合的基础数据更新成功,聚合结果实时更新;
  • ②参与聚合的基础数据更新失败,聚合结果保持不变。

之前仅有这两个终态,如果要进行满足状态,既耗费资源,又会响应缓慢,为此就可以允许中间态存在,保证最终一致性即可(如开启定时任务,定时聚合并刷新缓存中的数据)。

总之,只要参与聚合的基础数据不再更新,随着时间推移,定时任务肯定会把缓存中的结果,刷新成“最新的基础数据聚合后的结果”,让系统内的数据达到一致状态,这就是最终一致性!

三、固有认知的拨乱反正

上节简单讲了下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论文,我带大家来仔细看,其中有一段是这样的:

007.png

看图中圈出来的这段话,段落标题为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的替代品

008.png

来简单翻译下这段话:

如果ACID为分布式数据库提供了一致性选择,那如何在分布式数据库里保证可用性呢?一个答案就是BASE理论。

BASE理论与ACID完全相反,ACID是悲观思想,在每次操作结束时强制要求保持一致性(即事务结束必须落入终态)。而BASE是乐观思想,可以接受数据库一致性处于不断变化的状态。虽然听起来……

翻译了前两句话,从这两句话里不难看出,BASE理论的最终一致性,并不是用来对标CAP的一致性,而是与ACID里的一致性对等,允许事务执行后出现“中间态”

来个分布式事务的经典例子:

009.png

商品库里有个“黄金竹子”,库存数为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中做抉择,而是可以实现部分的AC

奔着三人成虎的原则,你这样喊,他也这样喊,我还这样喊……,越来越多的人就都认为BASECAP的补充版……。其实不然,就连CAP、BASE两个理论中的一致性定义都不同,CAP的一致性,关注的是数据一致性;BASE的一致性,关注的是状态一致性,真正的对应关系如下:

CAP一致性 ≠ BASE一致性、BASE一致性 = ACID一致性。

同时,两个理论除开一致性的定义不同外,甚至连“可用性”的定义也不一样。CAP中的可用性,是指主从这类集群,某个节点出现故障时,不会影响系统整体的运行,如:

010.png

上图这个MySQL多主集群,两个节点互为对方的从机,业务应用任意连接某个节点,都可以读写完整数据。当某个节点出现故障时,业务应用都可以连接另一个节点,正常读写数据,这是CAP关注的可用性(响应速度也是CAP可用性的一种表现)。

反观BASE理论,其中定义的“基本可用”,更多针对于分片式领域,来看论文中这段话:

011.png

翻译过来的意思就是:如果用户数据跨五个数据库服务器存储,根据BASE理论,一台数据库出现故障,仅仅只会影响到20%的用户。用户数据跨五个数据库存储,意味着什么?看过之前《MySQL分库分表篇》的小伙伴应该清楚,这代表着用户数据做了水平拆分,所有用户数据会根据特定的规则,分发到五个不同的节点中分开存储!

正因如此,才会给出“一个节点故障只会影响20%用户”这个结论,毕竟存储其他80%数据的四个节点此时正常。所以,BASE理论中的基本可用,并不是指主从这类“全量式复制”的集群,而是针对“分片式”的集群。

PS:BASE论文中还提到按功能拆分的思想,其中称为“功能组”,就类似如今的按业务分库,感兴趣可去阅读原文。

好了,既然CAP、BASE的定义都不同,为什么这么多人会把这两个扯上关系呢?究其根本原因是:BASE理论中定义的三个概念,几乎能覆盖大多数分布式场景

BASE理论的最终一致性,虽然是指状态一致性,可是也能套入CAP的数据一致性,比如主从集群数据复制,如果选同步-复制,太影响性能;如果选异步-复制,可以最快程度上响应请求,同时数据也会同步给从机,虽然有时间差,但只要集群内节点正常运转,所有节点的数据,最终都会保持一致。

异步-复制模式中,如果数据刚落入主节点,而后主节点就宕机,尽管选上来的新主会丢失一部分数据,但也不是不能用对吧?为此,BASE定义的基本可用,也能套上CAP的可用性~

BASE理论比CAP晚诞生十年,两者所处的时代背景不同,BASE定义的维度比CAP要高,所以在一定程度上,BASE能向下兼容CAP,这也就让许多人觉得BASECAP理论的拓展。

四、总结

通篇看完,大家会发现CAP理论并没有那么强大,它的作用更多局限于分布式存储领域,同时也并非整个分布式存储领域都会用到CAP思想,如今主流的分片式存储架构,就摒弃了传统的CAP模型。

BASE理论,则广泛运用于各类分布式系统,其中定义的三个概念,覆盖了分布式系统的大多数场景,比如微服务中的服务降级、熔断、限流机制,就是“基本可用”的一种体现。

最后,在之前的文章中,我一直强调大家不要听风就是雨,学习技术时,务必保持审慎怀疑的态度!比如本文讨论的BASE理论,90%资料都在告诉你,它是CAP的拓展理论,可事实却并非如此。为此,大家学习时要多思考、做验证,这样才能真正的把技术学透彻。

PS:后续所有文章会陆续同步到个人公众号:竹子爱熊猫,想在微信上便捷阅读的小伙伴可搜索关注~

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
2月前
|
缓存 Java 数据库
JAVA分布式CAP原则
JAVA分布式CAP原则
65 0
|
5月前
|
算法 前端开发
|
5月前
|
缓存 搜索推荐 Java
Java面试题:简述CAP理论及其在分布式系统设计中的应用。请提供一个具体的例子,说明在系统设计中如何取舍一致性和可用性
Java面试题:简述CAP理论及其在分布式系统设计中的应用。请提供一个具体的例子,说明在系统设计中如何取舍一致性和可用性
61 0
|
6月前
|
缓存 NoSQL 数据库
分布式系统面试全集通第一篇(dubbo+redis+zookeeper----分布式+CAP+BASE+分布式事务+分布式锁)
分布式系统面试全集通第一篇(dubbo+redis+zookeeper----分布式+CAP+BASE+分布式事务+分布式锁)
107 0
|
7月前
|
消息中间件 缓存 算法
从ACID到BASE:分布式系统CAP理论深度解析
**CAP理论**是分布式系统设计的基础,指出一致性(Consistency)、可用性(Availability)和分区容忍性(Partition Tolerance)无法兼得。一致性确保所有节点数据相同,如ACID原则;可用性保证系统始终响应用户请求,常见优化包括BASE理论和多级缓存;分区容忍性则确保网络分区时仍能服务。设计时需根据业务需求权衡这三者。
130 4
|
6月前
|
存储 架构师 关系型数据库
分布式系统详解--基础知识(CAP)
分布式系统详解--基础知识(CAP)
87 0
|
7月前
|
Nacos
分布式理论:CAP理论 BASE理论
分布式理论:CAP理论 BASE理论
46 2
|
2月前
|
NoSQL Java Redis
太惨痛: Redis 分布式锁 5个大坑,又大又深, 如何才能 避开 ?
Redis分布式锁在高并发场景下是重要的技术手段,但其实现过程中常遇到五大深坑:**原子性问题**、**连接耗尽问题**、**锁过期问题**、**锁失效问题**以及**锁分段问题**。这些问题不仅影响系统的稳定性和性能,还可能导致数据不一致。尼恩在实际项目中总结了这些坑,并提供了详细的解决方案,包括使用Lua脚本保证原子性、设置合理的锁过期时间和使用看门狗机制、以及通过锁分段提升性能。这些经验和技巧对面试和实际开发都有很大帮助,值得深入学习和实践。
太惨痛: Redis 分布式锁 5个大坑,又大又深, 如何才能 避开 ?
|
4月前
|
NoSQL Redis
基于Redis的高可用分布式锁——RedLock
这篇文章介绍了基于Redis的高可用分布式锁RedLock的概念、工作流程、获取和释放锁的方法,以及RedLock相比单机锁在高可用性上的优势,同时指出了其在某些特殊场景下的不足,并提到了ZooKeeper作为另一种实现分布式锁的方案。
116 2
基于Redis的高可用分布式锁——RedLock
|
19天前
|
NoSQL Redis
Redis分布式锁如何实现 ?
Redis分布式锁通过SETNX指令实现,确保仅在键不存在时设置值。此机制用于控制多个线程对共享资源的访问,避免并发冲突。然而,实际应用中需解决死锁、锁超时、归一化、可重入及阻塞等问题,以确保系统的稳定性和可靠性。解决方案包括设置锁超时、引入Watch Dog机制、使用ThreadLocal绑定加解锁操作、实现计数器支持可重入锁以及采用自旋锁思想处理阻塞请求。
53 16