分布式事务框架:SEATA

什么是分布式事务

本地事务 :
用数据库本身提供的事务特性来实现,也叫数据库事务。

分布式事务
事务的参与者位于不同的节点、不同的应用,操作不同的数据库,单个数据库事务无法控制参与者的操作同时成功or同时失败
因此有了分布式事务。

Seata简介

Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。
Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。

官方网站:https://seata.io/zh-cn/docs/overview/what-is-seata.htmlopen in new window

各模式的优缺点

项目AT模式TCC模式XA模式
描述会代理Datasource进行数据库操作,2PC模式的改良版不会代理应用的数据库操作X/Open组织定义了分布式事务接口,Oracle、mysql等厂商进行具体的实现
一致性最终一致性强一致性
整合方法@GlobalTransactional + undo_log(事务回滚日志表)@LocalTCC、@TwoPhaseBusinessAction、定义3个接口对应3个阶段@GlobalTransactional
优点1无侵入性,很少分布式事务相关代码性能高,框架层面解决了空回滚、幂等性、防悬挂的问题无侵入性,极少分布式事务相关代码
优点2弥补了 XA 模式中资源锁定周期过长的缺点,增加了undo_log 机制,性能更好应用自主程度高,事务参与者不局限于数据库,可延伸至其它组件如:Redis支持任何实现了 XA 协议的数据源
缺点1只能回滚数据库这种资源侵入性高,应用需要以 TCC 规范去编写业务逻辑只能回滚数据库这种资源
缺点2不适合用于单个SQL修改大量数据的场景改造成本大,对于存量业务来说,需要将原本的业务逻辑一分为三性能差
适用场景适用于依靠关系型数据库的保存数据的业务场景对性能有较高要求的业务场景适用于基于 XA 协议设计且无法进行改造的业务场景

AT模式是如何弥补XA模式资源锁定周期过长这个缺点的?

AT模式不锁定资源,而是先生成执行前的数据快照,然后直接执行并提交;
然后记录执行后的数据快照;
一旦需要回滚了,便直接用旧的数据快照覆盖新的即可;

什么是空回滚、幂等性、防悬挂?

简单讲讲AT、tcc、XA 3种模式分别的原理?

参考:https://wu55555.blog.csdn.net/article/details/124510770open in new window

1.AT模式

AT模式中,假设有服务A,服务B,服务C

服务A,标记了@GlobalTransactional,因此服务A属于TM
服务B、C,被服务A调用,因此是RM
还有一个Seata-Server,负责全局事务的协调,是TC

1.1 服务A向TC申请开启全局事务,获得一个全局事务ID(XID);
1.2 调用服务B并带上XID;
1.3 服务B向TC注册,并带上XID,好将服务B与服务A分到同一组;同时获得本地事务ID(BID
1.4 服务B在提交前,服务B会:1. 申请全局行锁 2. 生成事务前数据快照(before image)
1.5 服务B提交后,还会生成事务后数据快照(after image)
1.6 然后,服务B提交事务执行结果
1.6.1 成功。根据before image,生成回滚SQL保存到undo_log里;
1.6.2 失败。根据after image,判断数据是否被脏读。有则报错,没有则事务回滚,并通知TC让其他本地事务回滚
1.7 服务C同理,按照1.3、1.4、1.5、1.6进行
1.8 服务A提交整体执行结果:(1)成功。TC通知服务A删除undo_log及其他事务管理数据;(2)失败:TM通知服务A所有本地事务回退

2.TCC模式

TCC模式中,有Try、Confirm、Cancel三个阶段。

假设现在有一个提交订单的场景。提交完订单后需要扣减库存。

2.1 Try阶段

  • 创建订单,并标注为未提交
  • 业务检查(是否有足够的库存扣减)
  • 资源预留(冻结库存,冻结的库存其他业务不能使用)

2.2 Confirm阶段

  • 将订单状态改为已提交
  • 冻结的库存取消冻结,正式扣减

2.3 Cancel阶段

  • 删除未提交的订单
  • 取消冻结的库存

在锁使用的方面,整个实现TCC的过程,实际上是没有用到全局锁的,这是和AT模式另外一个大的区别。
TCC模式更多的是利用本地行锁或者乐观锁、状态区分的形式来实现资源隔离。
因为没有全局锁的限制,所以其速度飞快提升。因此呢TCC模式也就适用于对性能有较高的分布式事务场景。

2.4 建立TCC模式的重试机制

confirm或者cancel方法也出现报错时,为了保证事务的最终一致性,我们应当做好重试机制处理:
比如将请求发送MQ,然后再接收处理。

2.5 幂等性问题

如果扣减库存时多次重试,仍然能保证只扣了1次库存,这种就称为保证了幂等性

换句话说,操作多次和操作一次的效果一样,就是所谓的幂等。

如何保证幂等性:

  • 添加状态字段,判断是否执行过

2.6 悬挂问题(又叫空回滚)

所谓悬挂问题,就是二阶段模式中,cancel比try先执行

拿上面的例子,cancel是解冻库存,try是将订单状态改为未提交。

2.6.1 因为网络问题,导致商品服务的try一直卡住,然后报错

2.6.2 TM通知库存服务回滚,执行了cancel方法,解冻了库存;

2.6.3 此时商品服务又好了,执行了try方法,将订单状态改为未提交;

2.6.4 然而事务已经被cancel过了,就不会再执行confirm,也就没有谁再来将资源状态从预处理更新为已处理了。

针对悬挂问题进行防悬挂处理,方案呢就是限制如果二阶段执行完成,一阶段就不能再执行。

当然这些处理呢,seata已经帮我们实现了,这也是使用现成的分布式事务框架的好处。省心!但是我们自己要知道这些问题和原理。

seata中的解决方案是增加一个事务记录表,在cancel阶段最后往事务记录表中插入一条记录(xid-status)标记cancel阶段已经执行过。
此时try阶段进入时发现已经执行过回滚操作,则放弃try阶段的执行

Last Updated:
Contributors: dongyz8