解决分布式事务的方案有很多,但实现起来都比较复杂,因此我们一般会使用开源的框架来解决分布式事务问题。
在众多的开源分布式事务框架中,功能最完善、使用最多的就是阿里巴巴在2019年开源的Seata了。
1. 初识Seata
Seata是 2019 年 1 月份蚂蚁金服和阿里巴巴共同开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务。
Seata 可能给是目前已知最可靠的分布式事务解决方案。
官网地址:Seata | Seata,其中的文档、播客中提供了大量的使用说明、源码分析。
2. Seata的架构
其实分布式事务产生的一个重要原因,就是参与事务的多个分支事务互相无感知,不知道彼此的执行状态,因此解决分布式事务的思想非常简单:
- 就是找一个统一的事务协调者,与多个分支事务通信,检测每个分支事务的执行状态,保证全局事务下的每一个分支事务同时成功或失败接口,大多数的分布式事务框架都是基于这个理论来实现的。
- Seata的开发者坚定认为:一个分布式事务是由若干个本地事务组成的。
Seata也不例外,在Seata的事务管理中有三个重要的角色:
作为一个分布式事务,它肯定也会有一个入口方法,在这个入口方法当中,一定会去调用多个其它的微服务,每调一个微服务,这个微服务不就是一个分支事务,因此将来调了多少个微服务,将来我们这个全局事务就包含多少个分支事务,因此在这个入口方法里就定义了全局事务的范围了,而TM就会去监控这个入口的方法或者说是代理这个入口方法,这样一来,TM就知道了全局事务里面总共有多少个分支事务,整个范围就确定下来了,当入口方法被执行时,TM会首先拦截当前的这个执行,会去向TC发起一个请求,去注册全局事务,接下来,就可以去执行这个入口的业务逻辑了,去调用每一个微服务,到了微服务里面每个分支事务就要开始执行了,此时RM就排上用场了。。。
Seata的工作架构如图所示:
其中,TC 为单独部署的 Server 服务端,TC服务则是事务协调中心,是一个独立的微服务,是一个独立的JVM进程,里面不包含任何业务代码,需要单独部署;TM 和 RM 为嵌入到应用中的 Client 客户端,引入到参与事务的微服务依赖中即可,将来TM和RM就会协助微服务,实现本地分支事务与TC之间交互,实现事务的提交或回滚。
在 Seata 中,一个分布式事务的生命周期如下:
微服务集成Seata
-
参与分布式事务的每一个微服务都需要集成Seata,为了方便各个微服务集成Seata,我们需要把Seata配置共享到Nacos,因此模块不仅仅要引入Seata依赖,还要引入Nacos依赖。
<!--统一配置管理-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--读取bootstrap文件-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<!--seata-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
<!--sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
- 由于TC服务将来有可能搞集群,所以将来微服务肯定是要去注册中心获取TC服务地址。
将原来的@Transactional注解改为Seata提供的@GlobalTransactional注解,@GlobalTransactional注解就是在标记事务的起点,将来TM就会基于这个方法判断全局事务范围,初始化全局事务。
Seata是如何解决分布式事务的?
Seata基于上述架构提供了四种不同的分布式事务解决方案:
无论哪种方案,都离不开TC,也就是事务的协调者。
3. XA模式(性能最差):强一致性的事务
- XA规范是 X/Open 组织定义的分布式事务处理(DTP,Distributed Transaction Processing)标准,XA规范描述了全局的TM与局部的RM之间的接口,几乎所有主流的数据库都对XA规范提供了支持。
XA是规范,目前主流数据库都实现了这种规范,实现的原理都是基于两阶段提交。
- 两阶段提交协议(2PC)和三阶段提交协议(3PC)是一种用于实现强一致性的分布式事务协议。
两阶段提交 - Two-phaseCommit - 2PC - 两阶段提交协议
Seata的XA模型
- Seata对原始的XA模式做了简单的封装和改造(大差不差),以适应自己的事务模型,基本架构如图:
XA模式的优缺点?
- 由两阶段提交可能会出现的各个问题衍生出三阶段提交
三阶段提交 - 3PC - 三阶段提交协议
由于三阶段提交协议3PC非常难实现,目前市面主流的分布式事务解决方案都是2PC协议。
Seata实现XA模式
Seata的starter已经完成了XA模式的自动装配,实现非常简单,步骤如下:
4. AT模式
- AT模式同样也是分阶段提交的事务模型,不过却弥补了XA模型中资源锁定周期过长的缺陷。
- AT模式也是一种无侵入的分布式事务解决方案,阿里的Seata框架,实现了该模式。
在AT模式下,用户只需关注自己的"业务SQL",用户的"业务SQL"作为一阶段,Seata框架会自动生成事务的二阶段提交和回滚操作。
一阶段:
- 在一阶段,Seata(RM)会拦截"业务SQL",首先解析SQL语义,找到"业务SQL"要更新的业务数据,在业务数据被更新前,将其保存成"before image"原快照,然后执行"业务SQL"更新业务数据,在业务数据更新完成之后,再将其保存成"after image"新快照 => 记录undo_log数据快照,最后生成行锁,以上操作全部在一个数据库事务内完成,这样保证了一阶段操作的原子性;
- 各个本地事务申请全局锁,申请成功则提交本地事务 => 提交异步化,非常快速的完成;
AT模式的脏写问题
一阶段本地事务提交前,需要确保先拿到全局锁:
- 拿不到全局锁,不能提交本地事务 => 这是为了解决在多线程并发访问AT模式的分布式事务时,有可能出现的脏写问题,先拿到全局锁,避免同一时刻有另外一个事务来操作当前数据
全局锁的作用?
- 基于锁的原理,对于同一个行数据,一个时刻只能有一个分布式事务执行,提供互斥的作用
为什么全局锁不在本地事务开始时直接加锁?
- 为了提高性能,减少全局锁对数据库的占用时间。
全局锁带来的性能问题?
- 因为有全局锁的存在,所有的分布式事务只能串行执行,不能并发的操作数据库当中的数据。
二阶段:
第二阶段根据阶段一的结果来判断
二阶段提交:大多数情况下全局事务都是执行成功
- 如果每一个分支事务都成功,则二阶段提交 => 二阶段如果是提交的话,因为"业务SQL"在一阶段已经提交至数据库,事务已经结束,所以Seata框架只需将一阶段保存的快照数据和行锁删掉,完成数据清理即可。
二阶段回滚:回滚通过一阶段的回滚日志进行反向补偿
- 如果有任意分支事务失败,则二阶段回滚 => 二阶段如果是回滚的话,Seata就需要回滚一阶段已经执行的"业务SQL",还原业务数据,回滚方式便是用"before image"还原业务数据,根据快照恢复到更新前数据,然后删除快照;
- 但在还原前要首先要数据校验 - 校验脏写,对比"数据库当前业务数据"和"after image",如果两份数据完全一致就说明没有脏写,可以还原业务数据,如果不一致就说明有脏写(说明数据被当前全局之外的动作做了修改)=> 出现丢失更新,出现脏写就需要转人工处理。
- 分支事务提交后,遇到异常回滚时,其它的本地事务修改了数据,此时Seata会默认抛出异常
事务1在等事务2释放数据库锁,事务二在等事务一释放全局锁,它两互相等待,产生了死锁:
本地事务为什么在一阶段提交?
AT模式的隔离级别?
AT模式的优缺点:
5. AT模式与XA模式的区别?
6. TCC模式
TCC模式与AT模式非常相似,每阶段都是独立事务,不同的是TCC通过人工编码来实现数据恢复
TCC模式,全称Try-Confirm-Cancel,通过名称也能看出来其流程主要有三个步骤 - 两个阶段,TCC实际上是服务化的两阶段提交协议:
- 事务发起方在一阶段执行Try方法,在二阶段提交执行Confirm方法,二阶段回滚执行Cacel方法。
TCC三个方法描述:
TCC执行流程:
分支事务成功的情况:
分支事务失败的情况 :
TM事务管理器
TCC模式 VS AT模式
TCC 的实践经验
蚂蚁金服TCC实践,总结以下注意事项:
➢ 业务模型分2阶段设计
➢ 并发控制
➢ 允许空回滚
➢ 防悬挂控制
➢ 幂等控制
1. TCC设计 - 业务阶段分2阶段设计
2. TCC 设计 - 允许空回滚:
3. TCC 设计 - 防悬挂控制(拒绝业务悬挂):
4. TCC 设计 - 幂等控制:
声明TCC接口
- TCC的Try、Confirm、Cancel方法都需要在接口中基于注解来声明:@LocalTCC、@TwoPhaseBusinessAction。
7. Saga模式
Saga模式是Seata提供的长事务解决方案,由蚂蚁金服主要贡献,也分为两个阶段:

- 一阶段:直接提交本地事务
- 二阶段:成功则什么都不做;失败则通过编写反向补偿业务来执行逆向回滚操作
Saga和TCC分布式事务的区别?
-
Sage和TCC有些类似,都是补偿型事务,与TCC不同的是,Saga不需要Try,而是直接进行Confirm、Cancel操作。
Saga模式的优缺点:
8. 四种模式对比
我们从以下几个方面来对比四种实现:
-
一致性:能否保证事务的一致性?强一致还是最终一致?
-
隔离性:事务之间的隔离性如何?
-
代码侵入:是否需要对业务代码改造?
-
性能:有无性能损耗?
-
场景:常见的业务场景
9. Seata的高可用异地容灾架构模型
Seata的TC服务作为分布式事务核心,一定要保证集群的高可用性和异地容灾。
- 搭建TC服务集群非常简单,启动多个TC服务,注册到Nacos注册中心即可。
但集群并不能确保100%安全,万一集群所在机房故障怎么办?
- 所以如果要求较高,一般都会做异地多机房容灾,比如一个TC集群在上海,另一个TC集群在杭州。