我们知道Seata是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA四种事务模式
,为用户打造一站式的分布式解决方案,包括事务管理、本地事务协调、分布式事务日志和分布式锁等组件。
之前我们学习了Seata的简介、Seata客户端和服务端的搭建集成、本篇文章我们了解一下Seata是如何通过四种事务模式解决分布式事务问题的。
一、 @GlobalTransactional
Spring本地事务使用:@Transactional
Seata全局事务使用:@GlobalTransactional
继续以我们之前搭建的用户购买商品微服务系统【微服务整合Seata1.5.2+Nacos2.2.1+SpringBoot】为例:
仓储服务(Stock):对给定的商品扣除仓储数量。
订单服务(Order):根据采购需求创建订单。
账户服务(Account):从用户账户中扣除余额。
创建订单->调用库存服务扣减库存->调用账户服务扣减账户余额->修改订单状态
简单说:下订单->扣库存->减余额->改状态
假设第3步扣减账户超时,在seata-account-service项目中模拟一个异常。不加 @GlobalTransactional 事务控制出现超时会数据异常,当库存和账户金额扣减后,订单状态并没有设置为已经完成,没有从零改为 1。而且由于 feign 的重试机制,账户余额还有可能被多次扣减。
接下来加上加 @GlobalTransactional,注意每个应用都使用Seata对数据源进行代理,再次请求,发现全局事务回滚成功。
/**
* 使用Seata对数据源进行代理
*/
@Configuration
public class DataSourceProxyConfig {
@Value("${mybatis-plus.mapper-locations}")
private String mapperLocations;
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource druidDataSource(){
return new DruidDataSource();
}
@Bean
public DataSourceProxy dataSourceProxy(DataSource dataSource) {
return new DataSourceProxy(dataSource);
}
@Bean
public SqlSessionFactory sqlSessionFactoryBean(DataSourceProxy dataSourceProxy) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSourceProxy);
sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
.getResources(mapperLocations));
sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());
return sqlSessionFactoryBean.getObject();
}
}
如果未成功,先检查异常是否被catch或者有无熔断降级。
一、AT模式
1、原理
AT模式前提
:
基于支持本地 ACID 事务的关系型数据库。
Java 应用,通过 JDBC 访问数据库。
两阶段提交协议的演变
:
一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。
二阶段:提交异步化,非常快速地完成。回滚通过一阶段的回滚日志进行反向补偿。
AT模式
是一种分布式事务处理模式,它通过在每个参与者的本地事务中实现事务的原子性和隔离性,来保证分布式事务的一致性。AT模式避免了全局锁和阻塞的问题,从而提高了系统的并发性能。在AT模式中,参与者的本地事务执行成功后即可提交,而不需要等待其他参与者的状态。
AT模式的原理如下
:
事务的发起者开始一个全局事务,并在本地事务管理器中开始一个本地事务。
事务的发起者调用其他参与者的服务,将全局事务ID传递给它们。
参与者接收到全局事务ID后,在本地事务管理器中开始一个本地事务,并执行操作。
当参与者的本地事务执行成功时,将操作结果记录在本地日志中。
事务的发起者完成所有参与者的服务调用后,调用各个参与者的提交接口。
参与者检查本地事务的日志记录,如果操作都成功,则提交本地事务;否则,回滚本地事务。
seata的AT模式
:Seata的AT模式是在AT模式基础上进行了扩展和优化的实现。Seata引入了Seata Server和Seata Client的概念,通过Seata Server作为事务协调器,集中管理分布式事务的控制逻辑。Seata的AT模式还提供了更多的功能和工具,如分布式事务日志和分布式锁,以增强分布式事务的可靠性和性能。
2 、优缺点
优点
:
- 较高的性能:AT模式在每个参与者的本地事务中执行操作,避免了全局锁和阻塞的问题,提高了系统的并发性能。
- 简化的实现:相对于XA模式,AT模式的实现相对简单,不需要涉及全局事务协调器,减少了开发和维护的复杂性。
- 本地事务的独立性:每个参与者在本地事务管理器中管理自己的事务,可以独立控制和优化本地事务的执行。
缺点
:
- 弱一致性:AT模式对一致性的要求相对较低,可能会出现数据不一致的情况。在某些场景下,可能需要更高的一致性保证,需要考虑其他分布式事务处理模式。
- 隔离级别限制:由于AT模式依赖于本地事务的隔离性,参与者的隔离级别受限于本地事务管理器支持的隔离级别,可能无法满足某些特定的隔离需求。
- 容错性和恢复性:AT模式在发生故障或错误时,需要考虑如何处理事务的回滚和恢复,以确保数据的一致性和可靠性。
3、实现
添加配置seata:data-source-proxy-mode: AT
在需要分布式事务的业务代码上添加注解@GlobalTransactional
重启测试
二、TCC模式
1、原理
TCC模式
是一种分布式事务处理模式,用于解决分布式环境下的一致性问题。它通过将事务分解为三个阶段(Try、Confirm、Cancel)来实现事务的可靠性和一致性。使得每个参与者可以控制自己的操作和资源,从而实现了分布式事务的可靠性和一致性。它要求参与者实现相应的接口和逻辑,确保Try和Cancel操作是幂等的,以处理重试和故障恢复情况。
AT 模式与TCC 模式
:
AT 模式(参考链接 TBD)基于 支持本地 ACID 事务 的 关系型数据库:
一阶段 prepare 行为:在本地事务中,一并提交业务数据更新和相应回滚日志记录。
二阶段 commit 行为:马上成功结束,自动 异步批量清理回滚日志。
二阶段 rollback 行为:通过回滚日志,自动 生成补偿操作,完成数据回滚。
相应的,TCC 模式,不依赖于底层数据资源的事务支持:
一阶段 prepare 行为:调用 自定义 的 prepare 逻辑。
二阶段 commit 行为:调用 自定义 的 commit 逻辑。
二阶段 rollback 行为:调用 自定义 的 rollback 逻辑。
所谓 TCC 模式,是指支持把 自定义 的分支事务纳入到全局事务的管理中。
TCC模式的工作原理如下
:
Try阶段(尝试阶段):在这个阶段,参与者(服务)尝试预留或锁定资源,并执行必要的前置检查。如果所有参与者的Try操作都成功,表示资源可用,并进入下一阶段。如果有任何一个参与者的Try操作失败,表示资源不可用或发生冲突,事务将中止。
Confirm阶段(确认阶段):在这个阶段,参与者进行最终的确认操作,将资源真正提交或应用到系统中。如果所有参与者的Confirm操作都成功,事务完成,提交操作得到确认。如果有任何一个参与者的Confirm操作失败,事务将进入Cancel阶段。
Cancel阶段(取消阶段):在这个阶段,参与者进行回滚或取消操作,将之前尝试预留或锁定的资源恢复到原始状态。如果所有参与者的Cancel操作都成功,事务被取消,资源释放。如果有任何一个参与者的Cancel操作失败,可能需要进行补偿或人工介入来恢复系统一致性。
seata的TCC模式
:Seata的TCC模式是在TCC模式基础上进行了扩展和优化的实现。Seata引入了Seata Server作为事务协调器,集中管理分布式事务的控制逻辑。Seata的TCC模式还提供了分布式事务日志和分布式锁等功能,以增强事务的可靠性和性能。Seata的TCC模式可以更方便地集成到应用中,并提供了更好的事务管理和监控能力。
2 、优缺点
优点
:
- 一阶段完成直接提交事务,释放数据库资源,性能好;
- 相比AT模型,无需生成快照,无需使用全局锁,性能最强;
- 不依赖数据库事务,而是依赖补偿操作,可以用于非事务型数据库;
缺点
:
- 有代码侵入,需要人为编写try、Confirm和Cancel接口,太麻烦
- 软状态,事务是最终一致;
- 需要考虑Confirm和Cancel的失败情况,做好幂等处理;
3、实现
以账户转账为例:
定义参与者接口:
public interface AccountService {
boolean tryTransfer(String fromAccount, String toAccount, double amount);
boolean confirmTransfer(String fromAccount, String toAccount, double amount);
boolean cancelTransfer(String fromAccount, String toAccount, double amount);
}
实现参与者逻辑:
public class AccountServiceImpl implements AccountService {
@Override
public boolean tryTransfer(String fromAccount, String toAccount, double amount) {
// 执行转账操作,预留转出账户金额,检查账户余额等
// 如果成功,返回 true;如果失败,返回 false
}
@Override
public boolean confirmTransfer(String fromAccount, String toAccount, double amount) {
// 确认转账操作,将预留金额转出
// 如果成功,返回 true;如果失败,返回 false
}
@Override
public boolean cancelTransfer(String fromAccount, String toAccount, double amount) {
// 取消转账操作,将预留金额回滚到账户
// 如果成功,返回 true;如果失败,返回 false
}
}
客户端调用:
// 获取Seata全局事务ID
String xid = RootContext.getXID();
// 开启全局事务
TransactionContext context = new TransactionContext();
context.setXid(xid);
GlobalTransaction tx = GlobalTransactionContext.getCurrentOrCreate();
try {
// 调用参与者的tryTransfer方法
boolean tryResult = accountService.tryTransfer(fromAccount, toAccount, amount);
if (tryResult) {
// 提交全局事务
tx.commit();
} else {
// 回滚全局事务
tx.rollback();
}
} catch (Exception e) {
// 异常时回滚全局事务tx.rollback();
}
三、SAGA模式
1 、原理
Saga模式
是一种用于处理分布式事务的模式,它通过将长时间的、复杂的事务分解为多个小的、可逆的事务片段,以实现事务的一致性和可靠性。
在Saga模式中,每个事务片段称为一个补偿操作。每个补偿操作都与一个正向操作相对应,正向操作是事务的一部分,而补偿操作是用于撤销或修复正向操作的。Saga模式通过按照事务执行的顺序,依次执行正向操作和补偿操作,来确保事务在发生失败或异常时能够进行回滚或恢复。
Saga模式的执行过程如下
:
执行正向操作:按照事务的逻辑顺序,依次执行正向操作。每个正向操作都会记录事务的执行状态。
如果所有的正向操作都成功执行,则事务提交完成。
如果某个正向操作失败,将会触发相应的补偿操作。补偿操作会撤销或修复正向操作的影响。
执行补偿操作:按照逆序依次执行已经触发的补偿操作。补偿操作应该具备幂等性,以便可以多次执行而不会造成副作用。
如果所有的补偿操作都成功执行,则事务回滚完成。
如果补偿操作也失败,需要人工介入或其他手段来解决事务的一致性问题。
Seata的Saga模式
:
Seata的Saga模式通过Seata框架来管理和协调分布式事务,提供了对事务的编排和状态管理的支持。它与Seata的其他特性(如AT模式、TCC模式)结合在一起,构成了Seata全面的分布式事务解决方案。
Seata的Saga模式相对于传统的Saga模式,具有以下特点
:
- 集成性:Seata的Saga模式与Seata框架紧密集成,可以与Seata的其他特性一起使用,如分布式事务日志和分布式锁等。
- 强一致性:Seata的Saga模式提供了强一致性的事务支持,确保事务的执行顺序和一致性。
- 可靠性:Seata的Saga模式在补偿操作的执行过程中,支持重试和恢复机制,提高了事务的可靠性和恢复能力。
适用场景
:
业务流程长、业务流程多
参与者包含其它公司或遗留系统服务,无法提供 TCC 模式要求的三个接口
2 、优缺点
优点
:
- 一阶段提交本地事务,无锁,高性能
- 事件驱动架构,参与者可异步执行,高吞吐
- 补偿服务易于实现,不用编写TCC中的三个阶段,实现简单
缺点
:
- 没有锁,不保证隔离性,会有脏写;
- 软状态持续时间不确定,时效性差;
3 、实现
使用Seata的Saga模式,需要进行以下步骤:
定义参与者接口:
public interface OrderService {
boolean createOrder(String orderId, String userId, String productId, int quantity);
boolean cancelOrder(String orderId);
}
public interface ProductService {
boolean reduceStock(String productId, int quantity);
boolean revertStock(String productId, int quantity);
}
实现参与者逻辑:
public class OrderServiceImpl implements OrderService {
@Override
public boolean createOrder(String orderId, String userId, String productId, int quantity) {
// 执行订单创建逻辑,如创建订单记录、扣减用户余额等
// 如果成功,返回 true;如果失败,返回 false
}
@Override
public boolean cancelOrder(String orderId) {
// 执行订单取消逻辑,如回滚订单记录、恢复用户余额等
// 如果成功,返回 true;如果失败,返回 false
}
}
public class ProductServiceImpl implements ProductService {
@Override
public boolean reduceStock(String productId, int quantity) {
// 执行减少库存逻辑,如更新产品库存、记录库存变更日志等
// 如果成功,返回 true;如果失败,返回 false
}
@Override
public boolean revertStock(String productId, int quantity) {
// 执行恢复库存逻辑,如恢复产品库存、删除库存变更日志等
// 如果成功,返回 true;如果失败,返回 false}
}
客户端调用:
// 获取Seata全局事务ID
String xid = RootContext.getXID();
// 开启全局事务
TransactionContext context = new TransactionContext();
context.setXid(xid);
GlobalTransaction tx = GlobalTransactionContext.getCurrentOrCreate();
try {
// 调用参与者的方法
boolean createOrderResult = orderService.createOrder(orderId, userId, productId, quantity);boolean reduceStockResult = productService.reduceStock(productId, quantity);
if (createOrderResult && reduceStockResult) {
// 提交全局事务tx.commit();
} else {
// 回滚全局事务
tx.rollback();
}
} catch (Exception e) {// 异常时回滚全局事务tx.rollback();
}
四、XA模式
1 、原理
XA模式是一种分布式事务处理的协议,它使用两阶段提交(2PC)来保证事务的一致性和可靠性。
准备阶段
:事务协调器向参与者发送准备请求,要求它们准备执行事务操作,并将结果记录在事务日志中。
提交阶段
:如果所有参与者都准备就绪,事务协调器发送提交请求给参与者,要求它们执行事务的提交操作。
中断阶段
:如果任何一个参与者未能准备就绪或发生错误,事务协调器发送中断请求给参与者,要求它们执行事务的中断操作。
通过两阶段提交,XA模式确保所有参与者要么一起提交事务,要么一起中断事务,从而保证事务的一致性。然而,XA模式也存在一些问题,如阻塞和单点故障的风险,因此在某些情况下可能需要考虑其他分布式事务解决方案。
2 、优缺点
优点
:
- 一致性:XA模式通过两阶段提交协议,确保所有参与者要么一起提交事务,要么一起中断事务,从而保证事务的一致性。
- 可靠性:XA模式提供了强一致性和可靠性的保证,在分布式环境下可以确保事务的正确执行。
- 标准化:XA模式是一种标准的分布式事务处理协议,被广泛支持和应用于各种数据库和资源管理器中。
缺点
:
- 性能开销:在XA模式中,需要进行多次网络通信和协调操作,这会引入额外的性能开销,并且可能会导致事务处理的延迟增加。
- 阻塞风险:在准备阶段和提交阶段,所有的参与者都需要等待事务协调器的指令,这可能导致一些参与者在等待期间被阻塞,影响系统的吞吐量和并发性能。
- 单点故障:在XA模式中,事务协调器起着关键的角色,如果事务协调器出现故障,整个分布式事务系统可能无法正常运行。
3、 实现
添加配置seata:data-source-proxy-mode: XA
在需要分布式事务的业务代码上添加注解@GlobalTransactional
从编程模型上,XA 模式与 AT 模式保持完全一致。只需要修改数据源代理,即可实现 XA 模式与 AT 模式之间的切换。
重启测试
五、 四种模式对比
XA | AT | TCC | SAGA | |
---|---|---|---|---|
一致性 | 强一致 | 弱一致 | 弱一致 | 最终一致 |
隔离性 | 完全隔离 | 基于全局锁隔离 | 基于资源预留隔离 | 无隔离 |
代码侵入 | 无 | 无 | 有,需要编写三个接口 | 有,需要编写状态机和补偿业务 |
性能 | 差 | 好 | 非常好 | 非常好 |
场景 | 对一致性、隔离性有高要求的业务 | 基于关系型数据库的大多数分布式务场景都可以 | 1.对性能要求较高的事务; 2.有非关系型数据库要参与的事务; |
1.业务流程长、业务流程多; 2.参与者包含其它公司或遗留系统服务,无法提供TCC模式要求的三个接口 |