终于有人把“TCC分布式事务”实现原理讲明白了!
最近在看技术论坛,发现了一篇写的很好的TCC分布式事务的处理文章,在此进行转载,并添加上自己的理解。希望对你有用。
作者:小菜亦牛🐂
原文地址
一、业务场景介绍
假设你现在有一个电商系统,里面有一个支付订单的场景。
那对一个订单支付之后,我们需要做下面的步骤:
- 更改订单的状态为“已支付”
- 扣减商品库存
- 给会员增加积分
- 创建销售出库单通知仓库发货
这是一系列比较真实的步骤,无论大家有没有做过电商系统,应该都能理解。
二、没有分布式事务的问题场景
1 | public class OrderService { |
上述这几个步骤,要么一起成功,要么一起失败,必须是一个整体性的事务。
举个例子,现在订单的状态都修改为“已支付”了,结果库存服务扣减库存失败。那个商品的库存原来是 100 件,现在卖掉了 2 件,本来应该是 98 件了。
结果呢?由于库存服务操作数据库异常,导致库存数量还是 100。
但是如果你不用 TCC 分布式事务方案的话,就用个 Spring Cloud 开发这么一个微服务系统,很有可能会干出这种事儿来。
我们来看看下面的这个图,直观的表达了上述的过程:
所以说,我们有必要使用 TCC 分布式事务机制来保证各个服务形成一个整体性的事务。
上面那几个步骤,要么全部成功,如果任何一个服务的操作失败了,就全部一起回滚,撤销已经完成的操作。
比如说库存服务要是扣减库存失败了,那么订单服务就得撤销那个修改订单状态的操作,然后得停止执行增加积分和通知出库两个操作。
三、TCC分布式事务实现思路
那么现在到底要如何来实现一个 TCC 分布式事务,使得各个服务,要么一起成功?要么一起失败呢?
大家稍安勿躁,我们这就来一步一步的分析一下。咱们就以一个 Spring Cloud 开发系统作为背景来解释。
1. 实现阶段一:Try
首先我们要对上面的业务思路下手,进行点改动。
这个操作,一般都是锁定某个资源,设置一个预备类的状态,冻结部分数据,等等,大概都是这类操作。
2. 实现阶段二:Confirm
理想情况: 都不出错都成功
这时候就要库存服务、积分服务、创建出库单服务。这些服务分别对他们提供的服务。提供一个confirm
方法。
如: 库存经过了第一步,库存并没有真是扣减完,而是冻结状态。提供一个 confirm
方法。这个方法将冻结的库存,也给释放掉。
积分和出库单服务都要提供类的confirm
接口。
3. 实现阶段三:Cancel
上面说的是正常场景,现在就开始说不正常场景。
假如说在第一步Try时候没有问题,但是在Confirm出错了。就要执行回滚逻辑。这就是最后一个Cancel。
和阶段二一样,要求其他的系统都提供一个,cancel
。
如库存: 将之前冻结的在加回去。库存从98重新回到100。
四、假如Cancel也失败了怎么办?
无可避免,是代码都会出现问题。在经过测试通过,难免线上还有很多场景。
1. 解决办法一
启一个线程去定时轮训订单状态,如果一个订单处理经过了15分钟。他的状态还是支付中,这种中间状态。此时就要主动的调用各个系统去询问处理结果。
2. 解决办法二
将支付中的订单状态,放到延迟消息队列中。延迟处理结果,如果在延迟处理中还是没有查询到,就可以设置更长的时间然后继续放到延迟队列。
eg: 第一次5分钟去主动查询一次。第二次10分钟去主动查询一次。第三次15分钟去主动查询一次。
五、总结
以上就是所谓的 TCC 分布式事务。TCC 分布式事务的核心思想,说白了,就是当遇到下面这些情况时:
- 某个服务的数据库宕机了。
- 某个服务自己挂了。
- 那个服务的 Redis、Elasticsearch、MQ 等基础设施故障了。
- 某些资源不足了,比如说库存不够这些。
在原文中作者是建议使用第三方的分布式事务的技术方案,在这里小编有不一样的看法。因为如果使用第三方技术框架来保证事务,首先每次请求会涉及到事务协调器的多次调用,
这个是不利于高并发的。其次如果通过技术来保证,就没有必要使用tcc的方案,直接就xa两次提交就完了。因为分布式事务框架已经会给你保证事务了,因为任何一个失败都会数据库回滚。
在此作为看官,请问你的看法是什么呢? 欢迎评论留言。