-
AT模式(Automatic Transaction):通过代理数据源实现自动化的分布式事务管理。
-
TCC模式(Try-Confirm-Cancel):用户自定义分布式事务的三个阶段。
-
Saga模式:适用于长事务,通过定义补偿操作实现最终一致性。
-
XA模式:基于数据库XA协议的分布式事务管理。
XA模式
XA模式为使用数据库锁实现的强一致模式,假设一个电商系统中,用户下单时需要同时更新以下两个服务的数据:
-
订单服务:在订单表中创建一条订单记录。
-
库存服务:扣减商品库存
订单服务完成后告诉事务管理器完成然后等待,库存服务完成后也告诉数据库完成然后等待,最后全部服务完成后再进行提交。
XA模式的优点
-
强一致性:保证所有参与的事务分支都要么成功提交,要么全部回滚,适用于银行、金融等高一致性场景。
-
标准化协议:基于数据库标准的 XA 接口,支持主流数据库(如 MySQL、Oracle)。
-
自动管理事务:开发者无需手动处理复杂的事务逻辑,直接交由数据库驱动实现。
XA模式的缺点
-
性能问题:
-
锁持有时间长:两阶段提交过程中,资源锁可能会被长时间占用,影响系统吞吐量。
-
分布式事务开销:每个分支的事务参与都会产生额外的网络和事务日志开销。
-
-
可用性问题:
-
单点故障:事务协调器(TC)的不可用可能导致整个事务无法执行。
-
失败恢复复杂:事务协调器需要处理各种失败场景(例如节点宕机、网络分区等)。
-
-
依赖数据库:必须使用支持 XA 协议的数据库,限制了适用场景的多样性。
AT模式(Automatic Transaction)
AT 模式是 Seata 提供的自动化分布式事务解决方案,主要通过代理数据源实现分布式事务的全局管理。相比 XA 模式,它性能更高,适合互联网高并发场景。是最终一致的模式
如果涉及 订单服务 和 库存服务,两者使用同一个事务进行操作。
订单服务完成后会直接提交不进行等待,库存服务完成后也会直接提交不等待。并且再每一个服务完成是记录一个修改之前数据的log 如果有服务报错则根据log来回滚数据。
实现方式:在全局事务的入口加上@GlobalTransactional
@Service
public class OrderService {
@Autowired
private OrderRepository orderRepository;
@Autowired
private InventoryService inventoryService;
@GlobalTransactional // 全局事务入口
public void placeOrder(Long userId, Long productId, int quantity) {
// 创建订单
orderRepository.createOrder(userId, productId, quantity);
// 扣减库存
inventoryService.deductStock(productId, quantity);
}
}
AT模式的优点
-
性能高:
-
只锁定必要的资源(行级锁),并在本地事务完成后释放,避免长时间锁定资源。
-
减少两阶段提交的开销,不需要等待所有分支 prepare 的结果。
-
-
易用性强:
-
不需要业务开发人员处理分布式事务逻辑,Seata 自动生成 Undo Log 并管理事务状态。
-
-
灵活性高:
-
无需数据库支持特殊协议(如 XA),适配主流数据库。
-
-
适合高并发场景:
-
避免全局锁,能在分布式高并发环境下表现优异。
-
AT模式的缺点
-
数据一致性要求高:
-
如果业务表有大量更新操作,Undo Log 可能会显著增加存储和回滚的开销。
-
-
回滚复杂性:
-
Undo Log 只能回滚 幂等性 和 无副作用 的操作,复杂业务可能需要额外补偿机制。
-
-
不支持非关系型资源:
-
只能用于支持 SQL 的数据库,无法管理非关系型数据(如 Redis、Kafka)。
-
TCC模式(Try-Confirm-Cancel)
TCC模式是 Seata 提供的一种分布式事务解决方案,允许用户显式定义事务的三个阶段:Try、Confirm 和 Cancel。
它适用于需要自定义事务逻辑、复杂业务场景以及高性能需求的场景。
TCC模式示例
以下是一个用户下单扣减库存的场景,涉及 订单服务 和 库存服务。
场景描述
-
用户下单时需要扣减库存。
-
若订单创建失败,则需要释放已锁定的库存。
实现逻辑是通过业务代码来实现扣除参数和回滚参数
代码实现
定义 TCC 接口: 每个参与者需要定义 TCC 的三个阶段操作。
public interface InventoryService {
// Try 阶段:锁定库存
boolean tryLockStock(Long productId, int quantity);
// Confirm 阶段:扣减库存
boolean confirmDeductStock(Long productId, int quantity);
// Cancel 阶段:释放锁定的库存
boolean cancelLockStock(Long productId, int quantity);
}
@Service
public class InventoryServiceImpl implements InventoryService {
@Autowired
private InventoryRepository inventoryRepository;
private final Map<Long, Integer> lockedStock = new ConcurrentHashMap<>();
@Override
public boolean tryLockStock(Long productId, int quantity) {
// 检查库存是否足够
if (inventoryRepository.getStock(productId) >= quantity) {
// 锁定库存
lockedStock.put(productId, quantity);
return true;
}
return false;
}
@Override
public boolean confirmDeductStock(Long productId, int quantity) {
// 扣减锁定的库存
inventoryRepository.deductStock(productId, quantity);
lockedStock.remove(productId);
return true;
}
@Override
public boolean cancelLockStock(Long productId, int quantity) {
// 释放锁定的库存
inventoryRepository.releaseLockedStock(productId, quantity);
lockedStock.remove(productId);
return true;
}
}
@Service
public class OrderService {
@Autowired
private InventoryService inventoryService;
@GlobalTransactional
public void placeOrder(Long userId, Long productId, int quantity) {
// Try:锁定库存
boolean locked = inventoryService.tryLockStock(productId, quantity);
if (!locked) {
throw new RuntimeException("库存不足");
}
// 创建订单(这里可调用其他服务)
createOrder(userId, productId, quantity);
// Confirm:确认扣减库存
// 如果有异常,全局事务框架会自动调用 Cancel 方法
}
}
TCC模式的优点
-
高性能:
-
没有全局锁,事务提交和回滚由应用代码实现,性能较高。
-
每个分支事务都是短事务,资源锁定时间短。
-
-
灵活性强:
-
可以完全自定义 Try、Confirm 和 Cancel 逻辑,适应复杂的业务场景。
-
-
最终一致性:
-
通过显式的补偿机制,确保事务的最终一致性。
-
TCC模式的缺点
-
开发成本高:
-
需要显式实现 Try、Confirm 和 Cancel 方法,增加开发工作量。
-
对于复杂业务,补偿逻辑可能会非常复杂。
-
-
业务入侵性强:
-
业务代码需要依赖 TCC 框架,导致分布式事务逻辑与业务逻辑耦合。
-
-
事务悬挂问题:
-
在某些场景下,Cancel 阶段可能会执行在 Try 阶段之前,需要小心设计事务流程。
-
注意事项
事务悬挂
定义
事务悬挂是一种异常情况,指在分布式事务中,Cancel 阶段的操作被执行在 Try 阶段之前。通常是因为某些分支事务操作顺序错乱或不正确地处理分布式事务的协调逻辑所导致。
所以我们执行cancal阶段的时候需要检查一下try阶段是否执行
Saga模式
Saga模式是一种分布式事务解决方案,它通过一系列有序的本地事务来实现全局事务管理,并通过定义补偿操作来保证最终一致性。
与 TCC 和 AT 不同,Saga 模式不需要锁定资源,适用于长时间运行的业务流程或跨多个微服务的场景。
Saga模式工作流程
Saga 模式由多个步骤(事务分支)组成,每个步骤包含:
-
正向事务(T, Transaction): 执行正常的业务逻辑。
-
补偿事务(C, Compensation): 如果后续步骤失败,执行补偿逻辑撤销当前步骤。
Saga模式示例
场景:在线订单
用户下单时,涉及三个步骤:
-
扣减账户余额(Account Service)。
-
扣减库存(Inventory Service)。
-
生成订单(Order Service)。
如果某一步失败,需回滚已完成的操作。
正向事务
-
扣减账户余额: 扣除用户的账户余额。
-
扣减库存: 扣除商品的库存。
-
生成订单: 创建订单记录。
补偿事务
-
恢复账户余额: 如果库存扣减失败,恢复已扣除的用户余额。
-
恢复库存: 如果订单创建失败,恢复已扣减的库存。
Saga实现示例
-
定义 Saga 执行步骤
使用 Saga 执行器管理事务步骤:
@Service
public class OrderSagaService {
@Autowired
private AccountService accountService;
@Autowired
private InventoryService inventoryService;
@Autowired
private OrderService orderService;
public void placeOrder(Long userId, Long productId, int amount) {
SagaExecution saga = SagaExecution.start()
// Step 1: 扣减账户余额
.step(() -> accountService.deductBalance(userId, amount),
() -> accountService.refundBalance(userId, amount))
// Step 2: 扣减库存
.step(() -> inventoryService.deductStock(productId),
() -> inventoryService.refundStock(productId))
// Step 3: 生成订单
.step(() -> orderService.createOrder(userId, productId),
() -> orderService.cancelOrder(userId, productId));
saga.execute(); // 执行 Saga
}
}
-
执行事务和补偿事务
-
Saga 执行器会逐步执行每个事务步骤。
-
如果某一步失败,执行器会按逆序调用补偿事务。
Saga模式优点
-
无全局锁:
每个步骤是一个独立的本地事务,不需要全局锁资源,性能较高。 -
高可用性:
即使某些步骤失败,通过补偿机制也能恢复一致性。 -
适合长事务:
Saga 可以处理长时间运行的事务,适合复杂的业务流程(如跨境支付、物流管理)。 -
弹性强:
各步骤之间解耦,可以独立扩展、重试或优化。
Saga模式缺点
-
一致性要求弱:
只保证最终一致性,在某些时间段内可能存在数据不一致。 -
补偿逻辑复杂:
开发者需显式定义每个步骤的补偿逻辑,尤其是复杂业务场景,补偿操作可能异常繁琐。 -
状态管理复杂:
需要可靠的 Saga 执行器来管理事务状态、执行顺序及异常处理。 -
失败场景难处理:
如果补偿逻辑本身失败,可能导致更复杂的问题。
总结
特性 | AT模式 | TCC模式 | XA模式 | Saga模式 |
---|---|---|---|---|
事务粒度 | 数据库层,自动生成 Undo Log | 应用层,显式定义 Try/Confirm/Cancel | 数据库层,全局锁定资源 | 应用层,显式定义正向和补偿事务 |
一致性 | 最终一致性 | 最终一致性 | 强一致性 | 最终一致性 |
性能 | 较高 | 高 | 低(全局锁) | 高 |
开发成本 | 低(框架自动生成日志) | 高(需开发三阶段逻辑) | 中(依赖数据库 XA 协议) | 中(需开发补偿逻辑) |
实现难度 | 简单,框架透明化处理 | 复杂,开发者需显式实现业务逻辑 | 较简单,但依赖数据库支持 | 中等,需可靠的 Saga 执行器 |
对业务代码侵入性 | 低 | 高 | 中 | 中 |
分布式事务范围 | 单一数据库为主 | 跨多个服务 | 单一数据库为主 | 跨多个服务 |
适用场景 | 高并发、互联网业务(如电商) | 高性能、高一致性需求的复杂业务 | 金融、银行等强一致性场景 | 长事务、跨多个服务的复杂流程 |
典型缺点 | 回滚时依赖 Undo Log,性能有限 | 开发成本高,补偿逻辑复杂 | 性能低,全局锁定资源 | 补偿事务可能失败,数据短暂不一致 |