在分布式架构中有两种经典的分布式事务解决方案:二阶段提交(2PC)与三阶段提交(3PC)。

二阶段提交

第一个阶段:事务预处理阶段

相比单点事务,分布式事务中增加了一个新的角色:事务协调者(Coordinator)

它的职责就是协调各个分支事务的开启与提交、回滚的处理

2PC阶段二:提交阶段

在提交阶段,事务协调者会向每一个服务下达提交命令,每个服务收到提交命令后在本地事务中对阶段一未提交的数据执行 Commit 提交以完成数据最终的写入,之后服务便向事务协调者上报“提交成功”的通知。当事务协调者收到所有服务“提交成功”的通知后,就意味着一次分布式事务处理已完成。

对于二阶段提交来说,它有一个致命问题,当阶段二某个服务因为网络原因无法收到协调者下达的提交命令,则未提交的数据就会被长时间阻塞,可能导致系统崩溃。

其实只要在服务这一侧增加超时机制 因超时自动执行提交操作,释放锁定资源

尽管这样做会导致数据不一致,但也比线程积压导致服务崩溃要好,出于此目的,三阶段提交(3PC)便应运而生。

三阶段提交

将二阶段中的提交阶段拆分为“预提交阶段”与“提交阶段”

同时在服务端都引入超时机制,保证数据库资源不会被长时间锁定

阶段一:事务预处理阶段。

3PC 的事务预处理阶段与 2PC 是一样的,用于处理本地事务,锁定数据库资源,当所有服务返回成功后,进入阶段二。

阶段二:预提交阶段。

预提交阶段只是一个询问机制,以确认所有服务都已准备好,同时在此阶段协调者和参与者都设置了超时时间以防止出现长时间资源锁定。当阶段二所有服务返回“可以提交”,进入阶段三“提交阶段”。

阶段三:提交阶段。

3PC 的提交阶段与 2PC 的提交阶段是一致的,在每一个数据库中执行提交实现数据的资源写入,如果协调者与服务通信中断导致无法提交,在服务端超时后在也会自动执行提交操作来保证资源释放。

通过加入预提交阶段引入了超时机制,让数据库资源不会被长期锁定

但这也会带来一个新问题,数据一致性也很可能因为超时后的强制提交被破坏

1
2
解决方案:
⭐增加异步的数据补偿任务、日终跑批前的数据补偿、更完善的业务数据完整性的校验代码、引入数据监控及时通知人工补录这些都是不错的补救措施。
1
Alibaba Seata    🍉AT模式

Drawing 8.png

1
2
3
4
5
6
7
通过Seata组件图我们可以看到三个组成部分:

第一个是事务协调者(TC),它的作用是维护全局和分支事务的状态,驱动全局事务提交或者回滚,这正是前面讲解 2PC 或者 3PC 方案时提到的事务协调者组件的具体实现,TC 由 SEATA 官方提供。

第二个是事务管理器(TM),事务管理器用于定义全局事务的范围,开始全局事务提交或者回滚全局事务都是由 TM 来决定。

第三个是资源管理器(RM),他用于管理分支事务处理的资源,并且报告分支事务的状态,并驱动分支事务提交或者回滚。
1
2
3
4
5
6
7
8
9
10
11
12
例子:
🍉会员采购(){
订单服务.创建订单();
积分服务.增加积分();
库存服务.减少库存();
}
在会员采购方法中,需要分别执行创建订单、增加积分、减少库存三个步骤完成业务,对于“会员采购”来说方法执行成功,则代表这个全局分布式事务需要提交,如果中间过程出错,则需要全局回滚,这个业务方法本身就决定了全局提交、回滚的时机以及决定了哪些服务需要参与业务处理,因此商城应用的会员采购方法就充当起事务管理器(TM)的角色。

而与之对应的在订单服务中创建订单、会员服务中增加积分、库存服务减少库存这些实际产生的数据处理的服务模块,则被称为资源管理器(RM)。

最后就是由Seata提供的Seata-Server中间件则提供事务协调者(TC)这个角色,实施全局事务1的提交、回滚命令下发。

图片7.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
新增订单时执行的 SQL 语句如下:
INSERT INTO order(id,...) values(1001,...)
Seata 的回滚日志是基于 SQL 反向生成,新增订单创建了 1001 订单,那 Seata会对 SQL 进行解析生成反向的回滚 SQL 日志保存在 UNDO_LOG 表,如下所示:
DELETE FROM order WHERE id = 1001
------
与之类似会员积分会生成加积分的业务 SQL 以及减积分的回滚 SQL。
#加积分
UPDATE FROM points SET point = 180 + 20 WHERE mid = 182
#UNDO_LOG表中的减积分SQL
UPDATE FROM points SET point = 200 - 20 WHERE mid = 182

当 RM 的分支事务执行成功后,会自动向 TC 上报分支事务处理成功。
当会员采购方法正确执行,所有 RM 也向 TC 上报分支事务处理成功,在“会员采购”方法退出前,TM 内置的 Seata 客户端会向 TC 自动发起“提交全局事务”请求。TC 收到“提交全局事务”请求,向所有 RM 下达提交分支事务的命令,每一个 RM 在收到提交命令后,会删除之前保存在 UNDO_LOG 表中的回滚日志。

假设某个 RM 分支事务处理失败,此时 TM 便不再向 TC 发起“提交全局事务”,转而发送“回滚全局事务”,TC 收到后,通知所有之前已处理成功的 RM 执行回滚 SQL 将数据回滚。

比如 1001 订单在第三步“减少库存”时发现库存不足导致库存服务预处理失败,那全局回滚时第一步订单服务会自动执行删除 1001 订单的回滚 SQL。
DELETE FROM order WHERE id = 1001
UPDATE FROM points SET point = 200 - 20 WHERE mid = 182
1
Seata AT模式就是通过执行反向 SQL 达到数据还原的目的,当反向 SQL 执行后便自动从 UNDO_LOG 表中删除。这便是 Seata AT 模式的大致执行过程,在这个过程中我们发现 Seata AT 模式设计的巧妙之处,Seata 为了能做到无侵入的自动实现全局事务提交与回滚,它在 TM端利用了类似于“Spring 声明式事务”的设计,在进入 TM 方法前通知 TC 开启全局事务,在成功执行后自动提交全局事务,执行失败后进行全局回滚。同时在 RM 端也巧妙的采用了 SQL 解析技术自动生成了反向的回滚 SQL 来实现数据还原。
1
2
3
官方文档
⭐⭐⭐⭐⭐
https://seata.io/zh-cn/docs/overview/what-is-seata.html