Seata (Simple Extensible Autonomous Transaction Architecture) 是一种开源的分布式事务解决方案,致力于在微服务架构下提供高效且对业务无侵入的分布式事务服务。Seata 能够帮助开发者轻松地解决在微服务架构下服务间的数据一致性问题。
图片
在分布式系统中,一个业务操作通常会涉及到多个服务的协作,这些服务可能会对不同的数据库进行操作。传统的分布式事务解决方案(如2PC,即两阶段提交)虽然能够保证强一致性,但却在性能和可用性方面存在不小的挑战。因此,Seata 提出了一种基于AT、TCC、SAGA、XA等模式的轻量级分布式事务协调方案。
Seata 确实支持多种分布式事务模式,包括 AT(自动补偿事务)、TCC(Try-Confirm-Cancel)、SAGA 和 XA(扩展的两阶段提交协议)。每种模式都有自己的使用场景和特点:
1. AT模式(Auto-Compensating Transaction)
图片
- 适用场景:简单的CRUD操作,不需要特殊处理的业务逻辑。
- 特点:
易于使用,开发者不需要编写额外的补偿逻辑,Seata 会自动为每个分支事务生成撤销(回滚)逻辑。
性能较好,因为它减少了准备阶段的通信开销。
通过行锁和UNDO_LOG(回滤日志),加锁和数据改动被记录下来,保证在出现错误时可以回滚事务。
@Service
public class OrderService {
@GlobalTransactional(timeoutMills = 300000, name = "create-order")
public void createOrder(Order order, Payment payment, Inventory inventory) {
// 更新库存
inventoryService.reduceStock(inventory);
// 创建支付记录
paymentService.pay(payment);
// 创建订单
orderRepository.save(order);
}
}
按照官方配置AT模式后,在方法前面添加GlobalTransactional注解。
AT模式的主要思想就是通过一个中间层进行协调事务的确认和回滚操作,将相应的操作记录在例如数据库表里面,在提交后进行事务的信息的删除,再通过一个全局锁来避免资源的竞争。其方式对业务代码不需要侵入。
2. TCC模式(Try-Confirm-Cancel)
图片
- 适用场景:业务逻辑较为复杂,或者需要明确的业务补偿操作。
- 特点:
分为三个操作:Try(尝试执行业务)、Confirm(确认执行业务)、Cancel(取消执行业务),业务逻辑被拆分成这三部分。
开发者需要自行实现这三个操作,提供更强的业务手动控制能力。
具体的业务操作和补偿逻辑是可见的,有助于处理复杂的业务场景。
//定义接口实现注解
@LocalTCC
public interface PaymentService {
// "try" 方法,准备资源,比如冻结用户资金
@TwoPhaseBusinessAction(name = "preparePay", commitMethod = "commitPay", rollbackMethod = "cancelPay")
boolean preparePay(BusinessActionContext context, String accountId, double amount);
// "confirm" 方法,确认并实际扣除冻结的资金
boolean commitPay(BusinessActionContext context);
// "cancel" 方法,取消操作,解冻之前冻结的资金
boolean cancelPay(BusinessActionContext context);
}
//然后,为接口提供一个实现类,并实现try、confirm和cancel方法:
@Service
public class PaymentServiceImpl implements PaymentService {
@Autowired
private AccountRepository accountRepository;
@Override
public boolean preparePay(BusinessActionContext context, String accountId, double amount) {
// 实现资金的冻结逻辑
// ...
return true;
}
@Override
public boolean commitPay(BusinessActionContext context) {
// 实现实际扣除资金的逻辑
// ...
return true;
}
@Override
public boolean cancelPay(BusinessActionContext context) {
// 实现取消扣款,即解冻资金的逻辑
// ...
return true;
}
}
//最后,在需要执行TCC事务的业务逻辑中注入PaymentService并调用上述方法:
@Service
public class OrderService {
@Autowired
private PaymentService paymentService;
@GlobalTransactional
public void createOrder(Order order, String accountId, double amount) {
// TCC try 阶段
boolean result = paymentService.preparePay(null, accountId, amount);
if (!result) {
throw new RuntimeException("Payment preparation failed.");
}
// TCC confirm 或 cancel 阶段将由Seata框架根据全局事务的最终状态自动调用
// ...
}
}
其本质的思想就是通过代码编写确认机制和补偿机制,这种方式需要对业务的代码的侵入。
3. SAGA模式
- 适用场景:长事务场景,例如一系列的步骤或服务调用需要在整体上保持一致性。
- 特点:
基于状态机实现,每个步骤都对应状态迁移过程中的一个节点。
不适用于标准的两阶段提交,而是将一个全局事务分解成多个局部事务,通过定义前向操作和反向补偿操作来保证整体一致性。
适用于事务执行时间较长的业务流程,减少了锁资源的持有时间。
// 伪代码,仅为示例说明
StateMachineBuilder stateMachineBuilder = StateMachineBuilderFactory.create();
StateMachine stateMachine = stateMachineBuilder
.state("Start")
.initial("CreateOrder")
.to("ReserveInventory").on("InventoryReserved")
.to("CancelOrder").on("InventoryReserveFailed")
.step("ReserveInventory")
.to("ProcessPayment").on("PaymentProcessed")
.to("RevertInventory").on("PaymentProcessFailed")
.step("ProcessPayment")
.to("End").on("Success")
.step("CancelOrder")
.compensateWith("CancelOrderOperation")
.step("RevertInventory")
.compensateWith("RevertInventoryOperation")
.build();
stateMachine.start();
使用状态机来进行多事务的描述其本质的思想就是对多事务进行确认和补偿,这种也会需要对业务代码的侵入。
4. XA模式
图片
- 适用场景:需要严格的ACID事务,并且各参与方(数据库、消息中间件等)支持XA接口。
- 特点:
基于两阶段提交(2PC),第一阶段是准备阶段,第二阶段是提交或回滤阶段。
实现了跨资源管理器的全局事务。
通常比AT模式效率低,因为它在第一阶段结束时需要所有参与者就事务结果达成一致,然后在第二阶段进行提交或回滚。
将动态的代理数据源替换成XA模式,然后和AT模式一样,在需要的方法前面添加GlobalTransactional注解
@Service
public class OrderService {
@Autowired
private OrderRepository orderRepository;
@GlobalTransactional
public void createOrder(Order order) {
// 此处的数据库操作会自动被Seata管理
orderRepository.save(order);
// ...其他可能涉及数据库操作的代码
}
}
其本质的思想就是与AT模式类似,但是需要基于数据库能支持XA模式才能运行,AT模式是在中间层进行确认和回滚的日志记录,而XA模式是将记录交给数据库来运行,也避免了AT模式下手动操作数据导致问题。XA模式不需要侵入业务代码。
图片
在选用分布式事务模式时,需要根据具体业务场景和对一致性的需求做出选择。每种模式都有其优势和局限,没有绝对的“最佳”选择。对于开发者来说,了解各模式的内部机制和可能的性能影响是非常重要的。