AT模式是Seata最主推的分布式事务且基于XA演进而来的解决方案,是一种改进的二阶段提交,主要有三个角色:TM、RM和TC,其中TM和RM作为Seata的客户端和业务集成,TC作为Seata服务器独立部署
在AT模式下,数据库资源被当做RM,访问RM时,Seata会对请求进行拦截;每个本地事务提交时,RM会向TC(Transaction Coordinator,事务协调器)注册一个分支事务,用户只需关注自己的 业务SQL ,用户的 业务SQL 作为一阶段,Seata 框架会自动生成事务的二阶段提交和回滚操作
流程
第一阶段
- 当访问 RM 的时候,会拦截解析 SQL 语句,保存 “before image”
- 执行 SQL 更新业务数据
- 接着保存操作后的 “after image”
- 插入回滚日志 undo_log
- 向TC注册分支事务,申请全局锁,并将其纳入到该XID对应的全局事务范围。
- 提交本地事务
- 向 TC 汇报本地事务结果
第二阶段
事务提交
如果在第一阶段所有的分支事务已经提交,则TC决定全局事务提交,此时只需要清理UNDO_LOG日志即可,相比较XA模式,不需要TC触发所有分支事务的提交
具体的流程为:
-
分支事务收到TC的提交请求之后放入异步队列中,马上返回提交成功的结果
这里不需要同步返回的原因是:TC不需要知道分支事务的结果,因为仅仅只是一步删除
UNDO_LOG记录的操作,即使不成功也不会结果造成影响,所以采用异步是有效的方式 -
从异步队列中执行分支提交请求,清理undo_log日志,这里并不需要分支事务的提交了,因为第一阶段中已经提交过了
理解起来就是:AT模式下的全局事务的提交只需要清理UNDO_LOG记录就行,不需要管分支(本地)事务的提交结果!
事务回滚
在第一阶段的分支事务中任何一个分支事务执行失败,都会进入全局事务回滚流程,回滚主要是依赖UNDO_LOG中的记录进行补偿的
具体的流程如下:
接收到TC的回滚请求后,开启本地事务用来执行回滚操作
-
本地事务分支开始进行对查找
UNDO_LOG记录,通过XID+branch ID查到UNDO_LOG记录; -
数据校验(对比更新后镜像数据与当前数据),拿到
rollback_for中afterImage镜像数据与当前业务表中数据比较,不同的话,比如afterImage镜像数据拿出的amount理论上为99,但是实际上amount=98,则说明已经被当前全局事务外的某个操作做了修改(实际上由于全局锁的存在,并不会存在其他全局事务对业务数据进行更新),那么事务不进行回滚。 -
第二步中对比结果是相等的话,就采用
beforeImage和SQL的相关信息进行回滚,即1
UPDATE rep SET acoumt=100 WHERE id=1
-
删除
UNDO_LOG记录 -
提交本地事务
-
将本地事务的回滚执行结果报告给TC
隔离性保证
在AT模式下,多个全局事务操作同一张表时,它的事务隔离性保证是基于全局锁来实现的
-
写隔离,在第一阶段本地事务提交之前,必须确保拿到全局锁,如果拿不到全局锁则一直等待尝试,超出最大尝试次数则放弃全局锁的获取,并回滚释放本地锁(在本地事务开始之前就获到本地锁,这里的本地锁概念是数据库锁,比如对某行记录的行锁)
-
读隔离,Seata AT事务模式的默认全局隔离级别是Read Uncommit,在这种隔离级别下,所有事务都可以看到其它未提交事物的执行结果产生脏读,这在最终一致性的事务模型是被允许的,并且大部分是分布式事务是接受脏读的。
总结
优点:
- 原子性,AT协议保证了分布式事务的原子性,要么所有 RM 都成功提交事务,要么所有 RM 都回滚事务。
- 数据一致性,AT协议确保了分布式事务的一致性,所有 RM 在提交阶段只有在所有其他 RM 也准备好提交时才会提交事务。
- 灵活性,AT协议可以适应各种分布式环境和参与者的异构性,因为它没有对具体的数据库或资源管理器实施特定要求。
缺点:
- 同步阻塞,AT协议在准备和提交阶段都需要等待 RM 的响应,因此可能会引入同步阻塞,影响事务的性能和吞吐量。
- 单点故障,在AT协议中,TC 是关键的中心节点,如果 TC 发生故障,整个分布式事务的执行将受到影响。
- 数据不一致风险,在AT协议中,即使在准备阶段所有参与者都准备就绪,但在提交阶段仍然存在参与者无法提交成功的情况,这可能导致数据的不一致。