Day432.BASE理论&分布式事务常见解决方案 -谷粒商城
Posted 阿昌喜欢吃黄桃
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Day432.BASE理论&分布式事务常见解决方案 -谷粒商城相关的知识,希望对你有一定的参考价值。
BASE 理论
一、BASE 简介
BASE 其实就是对 CAP 的延伸。
我们在保证 AP 的情况下,我们永远无法做到强一致,就是我们这个业务状态做不了强一致,
但是我们可以让它弱一致
,也就是我们说的最终一致,强一致弱一致是对立概念
强一致,就是我们以前用的这种本地系统。我们就一个数据库,一台机器,我们一个代码在这操作好多数据。那操作完了要么都成功,要么都失败。这是一个强一致状态。
但我们在分布式以后呢,我们想要保证强一致可能很难。
但是我们可以保证最终一致,这就是我们说的 BASE,BASE 解释起来就是这个样子。
二、基本可用(Basically Available)
也就是说我们这个业务系统基本能用就行了,我们可以让它损失部分的可用性。比如说有两三个机器宕机了,我们可以损失一些部分的功能,或者我们也可以让他损失一些响应时间。
本来呢他不宕机,我查他挺快的,他宕机了,我查他不行了,我多查几个其他机器,查询速度慢一点也行,
所以我们最终都是希望一两个节点的不可用,不会导致我们整个系统不可用。
所以我们肯定想要让我们整个系统都是可用的,我们可以让它响应时间上有损失,多试几次,也可以让它功能上有损失。
比如我们这个网站在高峰期的时候,我们为了保护我们系统的稳定性,我们可能将部分的消费者流量直接引导到一个错误页面,就是我们说的这个降级页面。当前服务排队人太多了,请稍后重试等等。所以这就是我们说的基本可用。我们要保证业务的基本能用。
然后再加上我们的软状态。
三、软状态( Soft State)
软状态就指的是我们系统存在中间状态,不像我们的强一致,要么成要么败,我们也可以有一个中间状态。类似正在同步中这样。
比如我们保存了一个数据,它要么有,要么没有,但是还有一个正在同步中。我们可以有一个中间状态。
然后我们希望最终一致
四、最终一致( Eventual Consistency)
最终一致指的就是我们这个系统里边的这些数据。经过一段时间后,最终达到业务状态是一致的。
比如我们这个下单方法。
我们这个库存扣了,订单回滚了,库存没办法回滚,我们总是有一个东西呢没办法回滚,那没办法回滚怎么办呢?
我们首先不要求强一致了,但是我可以让系统过一段时间对它做一个检查,检查以后发现,刚才扣的这个库存都没人用,那再重新给它加回来,
这样最终导致的就是你如果没有这个订单,我最终就不给你扣这个库存,但是我们不是说立马就不扣的,我们可以让它到达一个最终一致。
我们可以用各种手段来保证最终一致性。
五、总结
-
强一致,
- 就是我们要求这个数据更新以后,立马就能被看到。那我们现在的本地事务,我们现在就是这种强一致状态。
-
弱一致,
- 就是我现在更新完了,可能有一段时间我还看不到,我能容忍部分的时间、或者部分的节点,我这更新成功了,只有这个数据库。
更成功了。那其他库呢可能还没有成功,还正在同步中。我们就可以忍受这些正在同步中的过程。如果能忍受,我们就说我们这个事务
可以做成一个弱一致性的事务。不是非要那么强的。
- 就是我现在更新完了,可能有一段时间我还看不到,我能容忍部分的时间、或者部分的节点,我这更新成功了,只有这个数据库。
-
最终一致,
- 就是指我们这个弱一致,经过一段时间以后,我们肯定得把它原来的数据同步过来。
或者我们这个下单方法,原来扣了的库存,最终都得加上去。这就是我们说的最终一致性。
分布式事务常见的解决方案
一、2PC 模式
2PC 就是我们说的二阶段提交,又叫做 XA Transactions。
mysql 从 5.5 版本开始支持,SQL Server 2005 开始支持,Oracle 7 开始支持。
这个二阶提交协议呢,就是把我们整个分布式事务拆成了两个阶段
。
假设我们这有一个本地资源管理器,一个事务管理器。
本地资源管理器是每一个服务的事务管理器
比如我们有一个订单服务,还有一个库存服务,那现在这是两个服务,这两个服务呢想要做一个分布式事务,两个一起成或者一起败。
接下来得有一个总的事务管理器
总的事务管理器让我们的分布式事务分为两个阶段,
首先我们总事务管理器会询问每一个微服务里的的小事务管理器,都准备好了没有?
然后,这些小事务管理器会检查它们当前的这些数据有没有准备好、连接正不正常、能不能提交数据?
如果一切都准备好,就差提交了。就会告诉我们的总事务管理器,它们都准备好了
接下来总事务管理器只要收到两个小事务都说准备好了,总事务管理器就叫这两个人发命令让 它们提交。这两个服务就会纷纷提交,然后就成功了。
但是如果在询问阶段,有一个小事务说自己不能提交,那事务管理器知道之后,就会要求所有人回滚。
这就是是我们说的这个二阶提交协议。
- 准没准备好数据、能不能提交、连接正不正常
- 提交
1、优点
比较简单,而且数据库是原生支持的,特别是我们商业数据库,它的支持度也非常好。
所以我们使用它的话呢,基本上少量配置就直接能用了,遵循规范标准就行。
2、缺点
性能不理想
,特别不适用于大型互联网场景高并发的情况下。
这种情况下,我们这个 XA 根本就没法工作了,因为它要占用大量的锁定资源。
然后它还有各种不理想的情况。
- 许多 nosql 也没有支持 XA,这让 XA 的应用场景变得非常狭隘。
- 也有 3PC,引入了超时机制(无论协调者还是参与者,在向对方发送请求后,若长时间未收到回应,则做出相应处理)
3、总结
了解即可,使用非常少
二、3PC 模式
也可以了解一下三阶提交协议,了解即可
三阶提交其实将整个预备又化成了两个阶段。
- 能否提交
- 能否准备好数据
- 提交
先问大家,你们这些数据能否提交,然后大家回答能提交,
它就让大家来准备这些数据,我们来开始提交。然后大家就准备这些数据来提交。
然后最后一个阶段,只要大家都告诉 它准备好了, 它就让所有人都去提交。
三、柔性事务 - TCC 事务补偿方案
这二阶提交协议呢,它看起来就像刚性事务。
就像是,我命令你们都提交,那你们都能提交,只要命令过去了,这个状态呢就是一致的。
但是在分布式里边,更多使用的是柔性事务。
1、什么叫柔性事务?
-
刚性事务
- 就是我们说的遵循 ACID 这个原则的事务,
强一致性
的。
- 就是我们说的遵循 ACID 这个原则的事务,
-
柔性事务
- 就是遵循我们之前说的 BASE 理论,实现
最终一致性的
。
- 就是遵循我们之前说的 BASE 理论,实现
2、TCC 事务补偿方案是怎么工作的?
比如呢我们现在有两个服务,两个服务要操作两个数据库。比如订单服务、库存服务。
TCC 模式就是我们开发人员在写每个业务代码的时候,还要写出对应的回调方法,准备让别人回调。
分别是这三个阶段
-
第一个是 Try,尝试阶段,就是我们预准备一些数据。
-
第二阶段就是 Confirm,就是把我们准备的数据提交。
-
第三个阶段呢就是 Cancel,想要回滚我们提交的数据。
比如我们之前给 它加二了,那我们的 Cancel 这一行业务代码写的逻辑就应该是减二。
当然这三块代码都得我们开发人员自己编写
,
编写以后呢,就相当于订单服务、库存服务我们都有这三行代码,
订单呢有它的 Try 逻辑,Confirm 逻辑和 Cancel 逻辑。
库存也有 Try、Confirm、Cancel 逻辑。
接下来我们的这个主服务、业务活动管理器,就先来执行第一阶段。
第一阶段,先命令大家去来预准备自己的数据。
那订单服务和我们的库存服务都会调用各自编写的 Try 模块的这些方法。
比如实现了一个接口,这个接口里边有一个叫tryLockStock()
,尝试锁定这个库存。所以大家都先来执行 Try,
都执行成功,进入第二阶段,大家都来提交。
Confirm 就是将我们 Try 准备的这些数据提交,订单一提交,库存也一提交。
接下来就是关键第三阶段了。只要有任何一个服务失败,订单失败或者库存失败,或者调用订单、库存的这个大业务在最后失败了。
接下来我们这个业务管理器,就会命令我们已经调了的这两个服务,去主动触发 它 它们的 Cancel 逻辑。Cancel 逻辑还是我们自己编写的,相当于我们给 它来做一个补偿。
比如原来我给这个里边加二了,我就给 它减二,原来我增添了这条记录,那我现在就给 它删除这条记录,这都是要我们自己写方法。
那这种模式呢在我们其实商城项目里边,特别是我们互联网项目。用的也非常多,支持 TCC 事务的框架也非常多。我们只需要按照人家规定的接口,把我们一个业务代码拆成三个部分就行了。
然后呢,人家会在合理的时机来触发我们哪个部分的逻辑。
所以我们只需要写好我们的回滚逻辑,最重要的就是这个事务补偿逻辑。
你在这加二了,你最终呢就要减二来进行一个补偿。
你在这儿增添数据了,你最终就要删除数据来做一个补偿。
这就是 TCC 事务补偿方案。
四、柔性事务-最大努力通知方案
最大努力通知和下面的可靠消息都是用来保证最终一致性的。
我们来举一个例子,我们现在有一个订单服务、 还有一个库存服务、还有一个大业务,大业务呢现在调了订单和库存,结果大业务在自己这失败了。然后 它就尽最大的努力通知库存去解锁
它调用一次库存解锁方法,不成了再调一次,再不成了,再调一次。这是我们自己写的同步的调用代码。
既然叫通知,我们就可以改成这样的逻辑,比如订单、库存都执行完了,但是它在下边做我们这个用户积分扣减的时候失败了。
然后接下来订单模块就可以来发一个消息,我们叫努力通知。我们发一个消息给我们的 MQ,接下来我们订单服务、库存服务都来订阅我们的这个消息队列。
而且我们两个服务都能同时收到消息。我们现在应该是 Topic 发布订阅模式。
我们现在两个服务呢都收到了,相当于大订单的创建失败消息。
然后我们库存服务收到这个消息就去解锁库存,
订单服务收到这个消息,就去解锁订单。
什么叫最大努力通知?
就是我们害怕这个订单服务,不知道我们这个消息,就是我们这个消息发出去了,我们害怕 它没收到,万一这个宕机了,一直收不到,那怎么办呢?那这块就失败了。
所以我们隔一秒给它里边发一个消息,说我失败了。再隔三秒,我发个消息,我说我失败了。再隔五秒我发一个消息,我说我失败了,我一直告诉你,我失败了、我失败了…
你哪一次真正的收到我失败了这个消息,而且你把你的业务执行成功了,你就可以告诉我,说行了我知道你失败了,你不用再通知我了。
这是我们说的最大努力通知型方案
。
这个特别是我们在后来开发支付宝的时候,我们要支付订单,订单最终支付到底是成功了还是没成功?
支付宝呢对我们就是要最大努力通知,支付宝一会给我们发一个消息,说你这个成了,一会给你发一个消息,说你这个成了,一会儿发一个消息,当然消息是发给我们订单这个业务的。
五、柔性事务- 可靠消息+最终一致性方案(异步确保型)
其实跟最大努力通知方案一模一样
- 怎么能允许大并发呢?
就是我干完事了,我不用管你的状态,我发一个消息就行了。你在后边,你自己慢慢同步去。所以我们只要希望系统能达到最终的一致就行。
六、以上几种方案的总结
3、4这两种方案的最大好处,就是可以允许大并发
当然1、2这两种方案,我们后来也会有相应的使用场景。
我们分布式事务呢可以用框架 2PC 或者 3PC 或者我们的 TCC 模式。 TCC 呢相当于我们 3PC 的手动版。
3PC 相当于自动准备数据、自动预提交、自动回滚等等。
但是 TCC 相当于把这些自动搞的东西都给整成手动的。
我们自己写业务逻辑,我们可以用这种一失败以后,就立即能帮我们让其它业务都失败的一致性方案。
我们也可以利用消息队列完成我们的这些通知型方案,无论是最大努力通知,还是我们失败了,我们就发一个消息,我通知你一下。
这都是我们分布式系统里边解决分布式事务,我们常用的方案。
以上是关于Day432.BASE理论&分布式事务常见解决方案 -谷粒商城的主要内容,如果未能解决你的问题,请参考以下文章
Day434.订单&库存服务分布式事务的最终解决 -谷粒商城