转载

终于有人把“TCC分布式事务”实现原理讲明白了!

最近在看技术论坛,发现了一篇写的很好的TCC分布式事务的处理文章,在此进行转载,并添加上自己的理解。希望对你有用。

作者:小菜亦牛🐂
原文地址

一、业务场景介绍

假设你现在有一个电商系统,里面有一个支付订单的场景。

那对一个订单支付之后,我们需要做下面的步骤:

  1. 更改订单的状态为“已支付”
  2. 扣减商品库存
  3. 给会员增加积分
  4. 创建销售出库单通知仓库发货
    这是一系列比较真实的步骤,无论大家有没有做过电商系统,应该都能理解。

二、没有分布式事务的问题场景

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class OrderService {

// 库存服务
@Autowired
private InventoryService inventoryService;

// 积分服务
@Autowired
private CreditService creditService;

// 仓储服务
@Autowired
private WmsService wmsService;

// 对这个订单完成支付
public void pay(){
//对本地的的订单数据库修改订单状态为"已支付"
orderDAO.updateStatus(OrderStatus.PAYED);

//调用库存服务扣减库存
inventoryService.reduceStock();

//调用积分服务增加积分
creditService.addCredit();

//调用仓储服务通知发货
wmsService.saleDelivery();
}
}

上述这几个步骤,要么一起成功,要么一起失败,必须是一个整体性的事务。

举个例子,现在订单的状态都修改为“已支付”了,结果库存服务扣减库存失败。那个商品的库存原来是 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两次提交就完了。因为分布式事务框架已经会给你保证事务了,因为任何一个失败都会数据库回滚。

在此作为看官,请问你的看法是什么呢? 欢迎评论留言。

yao

分享