hi !我是小小,我们又见面了,在本篇中,小小将会详细的介绍关于分布式事物的问题,看完了这篇文章,多年来的关于分布式事物的问题全都会了。
工欲善其事,必先利其器。所以需要先有一定的基础知识。
事物指的是对应用程序进行的严密的操作,添加了事物以后,所有的操作都必须完成,否则已经发生的事物进行撤销,即,事物具有四个特点,原子性,一致性,隔离性,持久性,这是个属性称之为ACID
在大型电商系统中,下单接口通常会扣减库存,减去优惠,生成订单id,订单服务与库存,优惠,订单id都是不同的服务,下单接口的成功与否,不仅仅取决于本地的db操作,还依赖于第三方的结果,这些时候就需要使用强分布式事物,达到最终的一致性。
CAP原则称之为CAP定理,指在一个分布式系统中,一致性,可用性,分区容错性,三者不能同时顾及到。
在分布式系统的所有数据,在同一时刻是否有相同的值。
在集群一部分节点故障以后,集群是否还能响应客户端的读写请求。
在分布式系统中,各个集群分为好几区域,每个区域之间应该是互通的。
对于CAP来说,只能满足其中两个,三个不可能全部满足,即
BASE理论为基本可用,软状态,最终一致性。解释如下
当系统出现不可预知的故障的时候,还需要能用,相对于正常的系统而言,需要有
相应时间上的损失,响应时间会相应的延长。
功能上的失去,对于电商网站来说,正常可以完成每一笔订单,但是大促的时候,会引导到一个系统繁忙页面。
允许系统存在于中间状态,例如当用户下单的时候,允许在同一时刻,该分布式系统,可以有中间状态,即库存还没有扣减的状态,但是要求最终都要达到一致性。只是数据同步出现了延迟。
相对于如软状态,不可能一直都是软状态,必须有一个时间期限,在这个期限过完以后,应当保证所有的数据副本都将会保证数据的一致性。
柔性事物是基于BASE理论的概念,通过柔性事物来达到最终的一致性。例如数据库的读写分离,写库同步到读库,会有一个延时,该延时造成数据不一致,这种状态称之为乱状态,在一定期限,达到同步,称之为最终一致性。这个事物,称之为柔性事物。
幂等性指的是,接口无论被调用多少次,其接口的返回值都应该是相同的,不会产生其他的变化。例如第三方支付用于回调的地址,告知某个订单支付成功,该回调地址,无论调用多少次,返回的结果都应该是一致的,
用户A使用银行app发起一笔跨行转账给用户B,银行系统会先扣掉用户A的钱,在增加用户B的余额,此时需要使用分布式事物,达到这个操作具有原子性。
在电商系统中下单扣库存分为两个部分,分别为下单,扣库存,这两个步骤同样需要原子性,需要分布式事物保证其原子性。
以电商系统为例子,在微服务订单里,一个接口对应于多个接口,如下图所示。多个接口也需要实现其原子性,在这里也需要使用分布式事物。
第一阶段,事物管理器会向所有本地资源管理器发起请求,询问是否是ready状态,所有参与者都将会把本事物能否成功的信息反馈给协调者。第二阶段:事物管理器根据所有的本地资源管理器的反馈,通知所有本地资源管理器,步调一致地在所有分支上提交和回滚。
同步阻塞,当参与事物者存在占用公共资源的情况,其中一个占用了资源,其他事物参与者就只能阻塞等待资源释放,处于阻塞状态。
当事物管理器出现故障的以后,怎个系统将会不可用
TCC(Try Confirm Cancel) Try 阶段:尝试执行,完成所有业务检查(一致性), 预留必须业务资源(准隔离性) Confirm 阶段:确认执行真正执行业务,不作任何业务检查,只使用 Try 阶段预留的业务资源,Confirm 操作满足幂等性。要求具备幂等设计,Confirm 失败后需要进行重试。Cancel 阶段:取消执行,释放 Try 阶段预留的业务资源 Cancel 操作满足幂等性 Cancel 阶段的异常和 Confirm 阶段异常处理方案基本上一致。在 Try 阶段,是对业务系统进行检查及资源预览,比如订单和存储操作,需要检查库存剩余数量是否够用,并进行预留,预留操作的话就是新建一个可用库存数量字段,Try 阶段操作是对这个可用库存数量进行操作。基于 TCC 实现分布式事务,会将原来只需要一个接口就可以实现的逻辑拆分为 Try、Confirm、Cancel 三个接口,所以代码实现复杂度相对较高。
当系统 A 被其他系统调用发生数据库表更操作,首先会更新数据库的业务表,其次会往相同数据库的消息表中插入一条数据,两个操作发生在同一个事务中
系统 A 的脚本定期轮询本地消息往 mq 中写入一条消息,如果消息发送失败会进行重试
系统 B 消费 mq 中的消息,并处理业务逻辑。如果本地事务处理失败,会在继续消费 mq 中的消息进行重试,如果业务上的失败,可以通知系统 A 进行回滚操作
对于可靠消息来说,需要实现最终的一致性
A 系统先向 mq 发送一条 prepare 消息,如果 prepare 消息发送失败,则直接取消操作
如果消息发送成功,则执行本地事务
如果本地事务执行成功,则想 mq 发送一条 confirm 消息,如果发送失败,则发送回滚消息
B 系统定期消费 mq 中的 confirm 消息,执行本地事务,并发送 ack 消息。如果 B 系统中的本地事务失败,会一直不断重试,如果是业务失败,会向 A 系统发起回滚请求
mq 会定期轮询所有 prepared 消息调用系统 A 提供的接口查询消息的处理情况,如果该 prepare 消息本地事务处理成功,则重新发送 confirm 消息,否则直接回滚该消息
最大努力通知是最简单的一种柔性事务,适用于一些最终一致性时间敏感度低的业务,且被动方处理结果 不影响主动方的处理结果。
这个方案的大致意思就是:
系统 A 本地事务执行完之后,发送个消息到 MQ;
这里会有个专门消费 MQ 的服务,这个服务会消费 MQ 并调用系统 B 的接口;
要是系统 B 执行成功就 ok 了;要是系统 B 执行失败了,那么最大努力通知服务就定时尝试重新调用系统 B, 反复 N 次,最后还是不行就放弃。
一个生活在一线,生于二线城市的程序猿,我是小小,一个双鱼座的程序猿,我们下期再见。
推荐阅读
● 推荐 | 作为IDEA的死忠粉,这样设置,效果棒棒哒!
● 异常 | 求你别再写满屏的try-catch了,用全局异常好吗?- 夹私货-行测
● 探讨 | 深入探讨Redis管道
● 必备收藏 | 超详细揭秘 Redis 持久化,建议收藏!
● 打卡学习 | Redis原理应用-线程IO模型
给我个好看再走好吗?