分布式事务之Seata
概述
Seata有四种模式
AT模式:无侵入式的分布式事务解决方案,适合不希望对业务进行改造的场景,但由于需要添加全局事务锁,对影响高并发系统的性能。该模式主要关注多DB访问的数据一致性,也包括多服务下的多DB数据访问一致性问题。通过更新前快照回滚、更新后快照对比在二阶段提交时是否有人修改(为不受Seata代理的数据源做兜底),解决脏写。
优点:
一阶段完成直接提交事务,释放数据库资源,性能比较好
利用全局锁实现读写隔离
没有代码侵入,框架自动完成回滚和提交
缺点:
两阶段之间属于软状态,属于最终一致
框架的快照功能会影响性能,但比XA模式要好很多
TCC模式:高性能的分布式事务解决方案,适用于对性能要求比较高的场景。该模式主要关注业务拆分,在按照业务横向扩展资源时,解决服务间调用的一致性问题。
优点:
- 一阶段完成直接提交事务,释放数据库资源,性能好
- 相比AT模型,无需生成快照,无需使用全局锁,性能最强
- 不依赖数据库事务,而是依赖补偿操作,可以用于非事务型数据库
- Redis这种也可以使用TCC模式
缺点:
- 有代码侵入,需要人为编写try、Confirm和Cancel接口,太麻烦
- 软状态,事务是最终一致
- 需要考虑Confirm和Cancel的失败情况,做好幂等处理
- 因为如果失败Seata会重试,所有要做好幂等
Saga模式:长事务的分布式事务解决方案,适用于业务流程长且需要保证事务最终一致性的业务系统。Saga 模式一阶段就会提交本地事务,无锁,长流程情况下可以保证性能,多用于渠道层、集成层业务系统,事务参与者可以是其它公司的服务也可以是遗留系统的服务,并且对于无法进行改造和提供 TCC 要求的接口,也可以使用 Saga 模式。
优点:
一阶段提交本地数据库事务,无锁,高性能;
参与者可以采用事务驱动异步执行,高吞吐;
补偿服务即正向服务的“反向”,易于理解,易于实现;
缺点:
- Saga 模式由于一阶段已经提交本地数据库事务,且没有进行“预留”动作,所以不能保证隔离性。后续会讲到对于缺乏隔离性的应对措施。
XA模式:强一致性。XA早期版本是一种规范 主流数据库对XA规范提供了支持,没有TM(事务管理者的概念)只有TC和RM。一阶段不提交事务,在第二阶段TM发起事务提交/回滚时才会让TC检查分支状态做提交/回滚因此是强一致性的
优点:
- 强一致性
- 易于使用:因为主流数据库都支持且无代码侵入
缺点:
- 第一阶段不提交,在等待过程中占用数据库锁,占用系统资源,性能差
Seata核心组件
- TC(Transaction Coordinator):全局事务协调器,负责管理全局事务的状态。
- TM(Transaction Manager):事务管理器,负责发起全局事务,并向TC注册事务。
- RM(Resource Manager):资源管理器,负责管理资源的本地事务,并向TC汇报事务状态。
前置条件
- 引入Seata依赖:在项目中引入Seata的依赖。
- 配置Seata服务:配置Seata的TC服务地址。
- 定义全局事务:使用
@GlobalTransactional
注解定义全局事务。
1. AT 模式(Auto Transaction)
核心原理
-
两阶段提交:
- 阶段一(Branch Commit):拦截业务 SQL,生成前置镜像(before image)和后置镜像(after image),保存到 UNDO_LOG 表。
示例:执行 UPDATE product SET stock = stock - 10 WHERE id = 1 时,记录修改前的 stock=100 和修改后的 stock=90。- 阶段二(Global Commit/Rollback):全局事务提交时,删除 UNDO_LOG;回滚时,根据镜像数据生成反向SQL(如 UPDATE product SET stock = 100 WHERE id = 1)。
-
全局锁机制:
在阶段一提交前,Seata 会获取记录的全局锁,防止其他事务修改同一数据,确保隔离性。
应用场景
-
单服务多数据源:
例如订单服务同时操作 MySQL 和 PostgreSQL,需要保证两个库的事务一致性。
-
简单跨服务调用:
服务 A 调用服务 B 的接口,两者均使用 AT 模式(如订单服务扣减库存服务)。
代码示例
// 订单服务(使用 AT 模式)
@GlobalTransactional // 开启全局事务
public void saveOrder(OrderRequest request) {// 1. 本地事务:创建订单orderDao.insert(request.getOrder());// 2. 远程调用库存服务(Feign 接口)storageFeign.discount(request.getProductId(), request.getCount());// 3. 模拟异常触发回滚if (request.getForceFail()) {throw new RuntimeException("Force rollback");}
}
关键细节
- UNDO_LOG 表结构:需在业务库中提前创建,包含 branch_id、xid、rollback_info 等字段。
- 隔离性牺牲:AT 模式默认隔离级别为读未提交(Read Uncommitted),高并发场景可能脏读,需业务侧处理(如版本号校验)。
- 性能优化:避免单行数据频繁更新,防止全局锁竞争。
2. TCC 模式(Try-Confirm-Cancel)
核心原理
三阶段控制:
Try:预留资源(如冻结库存、预扣余额),完成业务检查。
Confirm:确认操作,真正执行业务(如扣减冻结的库存)。
Cancel:回滚操作,释放预留资源(如解冻库存)。
业务侵入性:需手动编写 Try/Confirm/Cancel 接口,处理幂等性、空回滚、悬挂等问题。
应用场景
- 资金交易:转账前预冻结账户金额,最终扣款或解冻。
- 第三方服务集成:调用外部 API(如支付接口)需要明确的成功/失败确认。
代码示例
// TCC 接口定义(账户扣款)
public interface AccountTccService {@TwoPhaseBizAction(name = "deduct", commitMethod = "confirm", rollbackMethod = "cancel")boolean tryDiscount(@BizActionContextParameter(paramName = "userId") String userId,@BizActionContextParameter(paramName = "amount") BigDecimal amount);boolean confirm(BizActionContext context);boolean cancel(BizActionContext context);
}// Try 阶段实现(冻结资金)
@Override
public boolean tryDeduct(String userId, BigDecimal amount) {if (accountDao.getAvailableBalance(userId).compareTo(amount) < 0) {throw new RuntimeException("余额不足");}accountDao.freeze(userId, amount); // 冻结资金return true;
}// Confirm 阶段(实际扣款)
@Override
public boolean confirm(BizActionContext context) {String userId = (String) context.getActionContext("userId");BigDecimal amount = (BigDecimal) context.getActionContext("amount");accountDao.discount(userId, amount); // 扣减冻结金额accountDao.unfreeze(userId, amount); // 解冻return true;
}// Cancel 阶段(解冻资金)
@Override
public boolean cancel(BizActionContext context) {String userId = (String) context.getActionContext("userId");BigDecimal amount = (BigDecimal) context.getActionContext("amount");accountDao.unfreeze(userId, amount);return true;
}
关键细节
- 幂等性处理:
通过唯一事务 ID(xid)确保 Confirm/Cancel 只执行一次。 - 空回滚问题:
Try 未执行但收到 Cancel 请求时,需插入标记记录,避免误解冻。 - 悬挂问题:
Cancel 比 Try 先到达时,需通过状态判断拒绝后续 Try 操作。
3. Saga 模式
核心原理
- 事件驱动流程:
将分布式事务拆分为多个本地事务,每个事务提交后触发下一个事务。若某个事务失败,按反向顺序执行补偿操作。- 补偿机制:
每个正向操作需定义对应的补偿方法(如 bookHotel() 对应 cancelHotel())。
应用场景
- 长流程业务:
旅行预订(机票 → 酒店 → 租车)、电商订单(下单 → 支付 → 发货)。 - 渠道层、集成层业务:
在渠道层和集成层业务中,往往需要与外部系统进行交互。例如,银行系统与第三方支付平台的集成,或者企业系统与ERP系统的集成。这些场景中,事务的跨服务特性使得Saga模式成为理想的解决方案。 - 跨公司服务集成:
跨公司服务集成中,事务的分布式特性更加明显。例如,一个供应链系统可能涉及多个供应商、物流和零售商的协作。Saga模式通过补偿机制,可以有效地处理这些复杂场景中的事务问题。 - 最终一致性场景:
接受中间状态短暂不一致,但最终一致。Saga模式通过补偿操作保证最终一致性。如果某个Saga单元失败,系统会依次调用之前所有单元的补偿操作,回滚之前的操作。例如,如果支付服务扣款失败,系统会调用库存服务的补偿操作恢复库存。
代码示例
//Saga 流程编排(状态机或注解驱动)
@SagaService
public class OrderSagaService {@Autowiredprivate InventoryService inventoryService;@Autowiredprivate PaymentService paymentService;@SagaStartpublic void createOrder(Order order) {// 1. 扣减库存inventoryService.discount(order.getProductId(), order.getQuantity());// 2. 发起支付paymentService.pay(order.getUserId(), order.getAmount());// 3. 更新订单状态为成功order.setStatus(OrderStatus.SUCCESS);orderDao.update(order);}@Compensatepublic void compensateOrder(Order order) {// 反向操作:释放库存、退款、订单状态回滚inventoryService.restore(order.getProductId(), order.getQuantity());paymentService.refund(order.getUserId(), order.getAmount());order.setStatus(OrderStatus.FAILED);orderDao.update(order);}
}
关键细节
- 状态机配置:
可通过 JSON 或注解定义 Saga 流程,明确每个步骤的补偿方法。- 超时管理:
设置 Saga 事务超时时间,避免流程长期悬挂。- 异步执行:
适合结合消息队列(如 RocketMQ)实现异步 Saga。
注:@Compensate
4. XA 模式
核心原理
- 传统两阶段提交:
Prepare 阶段:所有参与者(数据库)锁定资源,返回就绪状态。
Commit/Rollback 阶段:协调者根据 Prepare 结果提交或回滚。- 强一致性:
所有资源在 Prepare 阶段锁定,直到全局事务结束。
应用场景
- 金融核心系统:
银行转账(必须保证双方账户同时成功或失败)。- 传统数据库集成:
旧系统迁移,依赖数据库原生 XA 协议。
代码示例
// XA 数据源配置
@Bean
public DataSource dataSource() {MysqlXADataSource xaDataSource = new MysqlXADataSource();xaDataSource.setUrl("jdbc:mysql://localhost:3306/test");xaDataSource.setUser("root");xaDataSource.setPassword("***");return new AtomikosDataSourceBean(xaDataSource);
}// 业务方法(依赖 JTA)
@Transactional // 使用 JTA 事务管理器
public void transfer(String fromId, String toId, BigDecimal amount) {jdbcTemplate.update("UPDATE account SET balance = balance - ? WHERE id = ?", amount, fromId);jdbcTemplate.update("UPDATE account SET balance = balance + ? WHERE id = ?", amount, toId);
}
关键细节
- 性能瓶颈:
全局锁持有时间长,高并发下吞吐量低。- 数据库支持:
需数据库支持 XA 协议(如 MySQL InnoDB、Oracle)。- 调试复杂:
XA 事务状态需通过数据库日志或 JTA 工具监控。
选型对比
对比维度
维度 | AT | TCC | Saga | XA |
一致性 | 弱隔离(读未提交) | 强隔离(预留资源) | 最终一致性 | 强一致性 |
性能 | 高(短事务) | 中(两阶段控制) | 高(异步流程) | 低(长锁) |
侵入性 | 低(自动 UNDO_LOG) | 高(手动 TCC 接口) | 中(补偿方法) | 低(数据库支持) |
适用场景 | 简单跨服务/多数据源 | 资金交易、第三方集成 | 长流程业务 | 金融核心、传统系统 |
容错能力 | 自动回滚 | 需处理空回滚、悬挂 | 需补偿逻辑完备 | 依赖数据库 XA 恢复 |
决策树
- 是否需要强一致性?
是 → XA 模式(金融场景)或 TCC 模式(业务可控)。
否 → 进入下一步。 - 是否为长流程业务?
是 → Saga 模式(如电商订单)。
否 → 进入下一步。 - 是否希望低侵入?
是 → AT 模式(简单跨服务调用)。
否 → TCC 模式(精细化控制)。
总结
- AT 模式:快速解决 80% 的分布式事务问题,适合微服务新手。
- TCC 模式:应对资金、库存等核心资源操作,牺牲开发效率换取高可靠性。
- Saga 模式:长流程业务的终极方案,需接受最终一致性。
- XA 模式:传统系统兼容选择,性能敏感场景慎用。
实际开发中,多种模式在同一个工程中不可混用、不能同时使用,但可组合使用多种模式(如 AT + Saga),并配合消息队列、幂等设计、监控告警,构建健壮的分布式事务体系。