大白话,一文搞懂分布式中的CAP理论


前言

看了很多关于CAP的文章,充斥着很多晦涩难懂的词汇,甚至东拼西凑复制粘贴,越看越迷糊。
我总结了一下,有2个问题始终没有说的很清楚。

  1. 到底什么是C、A、P,三者有什么区别?
  2. 为啥三个中只能同时满足2个?

搞懂这2个问题,对CAP的理解就水到渠成了。我尝试用比较直白的语言结合图片进行说明,希望对大家有帮助。


一、什么是CAP?

我们用转账的例子进行说明
转账
完成整个转账操作,总共需要两步:

  • 步骤一:用户A余额-100
  • 步骤二:用户B余额+100

一致性(Consistency)

一致性强调数据是最新且正确的

在上述的例子中,当步骤一执行完成,但步骤二未完成时,如果我去查询A和B最新的余额,会得到什么呢?A余额=900,B余额=1000,这明显是不正确的。也就是不满足一致性的。
得到不一致的数据

那怎样才算是满足一致性呢?很简单,程序只要等待步骤一和步骤二全部执行完成后,返回最新且正确的结果即可。

满足一致性

注意这里说的一致性,指的是强一致性。弱一致性和最终一致性实际上可以接受一定时间内的数据不正确,不在本文讨论范围内。

可用性(Availability)

可用性强调接口必须在可接受的时间内返回结果

接口出现长时间等待、超时、报错、甚至崩溃,都是接口不可用。所谓的“高可用”就是尽量减少这些情况的发生。

所以我们要尽量减少等待,上面的例子,只需要这样改,就达到高可用了。

高可用

但是这就出现问题了,接口返回的数据不正确了,不满足一致性了。要满足高可用,就一定会导致数据不一致吗?答案是:不一定

可用性是一个相对的概念,不同的系统有不同的可用性要求。某些系统里,等待超过5s则不满足可用性,有的系统可能超过1s就不满足了。

如果你要求1s内返回数据就是高可用。而整个转账操作只需要10ms,那么即使等待整个操作完成再返回,你仍然觉得这个接口是高可用的。

既一致又高可用

分区容错性(Partition tolerance)

强调系统的一部分发生故障时,其他部分还可以正常运行

说白了,就是要把系统部署在多台服务器上,也就是分布式部署。如果程序都在一个单体服务中,那出现部分故障,整个服务就挂掉了。分布式系统,通常都要满足分区容错性(不然就叫单体服务了)。

假设我们已经满足分区容错性,把步骤一和步骤二在不同的服务部署。

转账过程中,步骤一执行完成,但是步骤二挂了。此时我仍然可以查询A的余额,但是应该返回什么呢呢?900还是1000?

在这里插入图片描述

如果我还要满足一致性,那我必须给你返回正确的数据

但是B服务都挂了,我们根本不知道他到底收到100元没有,不知道900还是1000才是正确的。只能等待B服务恢复再询问它,如果它收到了100,那么用户A的余额就是900,否则是1000。

但是“等待B服务恢复”,这个时间是很长的、不可接受的,也就是说不是高可用的。

相反,如果我要满足高可用。那么我就会返回一个余额(900或1000),但是我不能保证这个数据是正确的。


二、为什么只能同时满足2个

其实我们把三个概念都理解之后,自然就清楚为什么只能满足2个了,这里进行一个简单的总结:

如果我要满足一致性,并且高可用(CA)

表示我必须在短的时间内,得到正确的结果。这就不能允许任何部分有故障(分区容错),因为如果有部分故障,那么其他部分就会:

  • 要么接口等很久才得到数据,不满足可用性
  • 要么会得到错误的数据,不满足一致性

如果我要满足一致性,并且分区容错(CP)

那表示我的服务部署在多个节点上,还要总是返回最新且正确的数据。那我在部分节点故障时,其他部分就只有等待或请求失败,也就是说我的系统不是高可用的。

如果我要满足可用性,并且分区容错(CP)

也就是我的服务部署在多个节点,并且当部分故障时其他节点仍能快速响应。那么这个响应的结果,就不能保证是最新且正确的。也就是可能会不一致。

三、常见的中间件使用哪种模式

综上可知,一个分布式系统,必须要满足分区容错性(P),然后再在一致性(C)和可用性(A)之间选一个。

AP模式

这是被广泛采用的模式
在这里插入图片描述

比如redis集群:

  • 集群中的任意节点故障,其他节点仍然可以使用,所以它是分区容错的(P)
  • 即使有单点故障,导致数据没有正确广播而产生不一致(C),但仍然能够快速读写数据(A)

在这里插入图片描述

eureka和nacos的服务注册与发现

  • 可以集群部署,所以是分区容错的
  • 服务列表不总是正确的。服务下线后,你仍然会在注册列表中看到他一段时间。此时的数据就是不一致的(C),但可以保证任何时候,你都可以获取到一份注册表(A)

CP模式

在这里插入图片描述
这种模式的其实比较少。如ZooKeeper,它具有这样的特点:

  • 可以多节点部署,单个节点故障不会导致整个系统崩溃,所以它是分区容错的(P)
  • 当领导者(Leader)挂掉后,整个系统进入恢复模式,此时是不可用的,直到新的Leader被选举出来。所以ZooKeeper的数据总是一致的(C),但他不能保证可用性(A)。

CA模式

没有分区容错性的模式,大多数单体服务都满足这个要求。如单机redis。
在这里插入图片描述

比较典型的还是单机的关系型数据库:mysql、oracle等。具有以下特点:

  • 因为有事务,所以你查询到的数据,要么是事务开始前,要么是事务提交/回滚后。总是获取到正确的数据,而不存在中间状态,这就是一致性(C)
  • 因为是单体服务,不用担心单点故障问题,这就是可用性(A)

当然,当你把mysql和oracle做成多节点之后,你具备了分区容错性(P),那你将必须在AP或CP模式中选一个。


总结

可以看到分布式系统其实最优先考虑的是分区容错(P),其次是可用性(A),而能够忍受一定程度的数据不一致。这对我们自己搭建分布式系统也有很大的指导意义。


作者注:我希望用更简单的语言帮助到更多开发者,文章都是原创且亲自测试过的。希望大家不吝点赞支持。另外因为是原创文章,难免有错误或疏漏,欢迎评论指正。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java分布式事务是指在分布式系统,多个独立的服务或应用之间进行数据操作时,保证数据的一致性和可靠性的一种机制。简单来说,就是多个服务或应用在进行数据操作时,要么全部成功,要么全部失败,不会出现部分成功部分失败的情况。 在分布式系统,每个服务或应用都有自己的数据库,它们之间需要进行数据的读取和写入。当多个服务或应用同时进行数据操作时,可能会出现以下问题: 1. 数据不一致:由于网络延迟或其他原因,某个服务或应用的数据操作成功了,但其他服务或应用的数据操作失败了,导致数据不一致。 2. 并发冲突:多个服务或应用同时对同一份数据进行读写操作,可能会导致数据冲突和错误。 为了解决这些问题,Java分布式事务引入了一些机制和技术,例如: 1. 两阶段提交(Two-Phase Commit):在分布式事务,引入一个协调者(Coordinator)来协调各个参与者(Participant)的数据操作。在第一阶段,协调者询问各个参与者是否可以提交事务;在第二阶段,如果所有参与者都同意提交,则协调者通知各个参与者提交事务;如果有任何一个参与者不同意提交,则协调者通知各个参与者回滚事务。 2. 分布式事务消息:使用消息队列来实现分布式事务,将数据操作和消息发送放在同一个事务,保证数据和消息的一致性。 3. 分布式锁:通过分布式锁来控制对共享资源的访问,保证在同一时间只有一个服务或应用可以对资源进行操作,避免并发冲突。 以上是对Java分布式事务的简单介绍,希望能帮到你。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值