Seata AT模式案例

wen java案例 2

Seata AT模式在微服务分布式事务中的落地案例与避坑指南

目录导读

  1. 分布式事务的困境与Seata的选择
  2. AT模式核心机制剖析(三组件+两阶段)
  3. 电商下单案例:从零搭建Seata AT模式
  4. 数据一致性验证 & 异常场景回滚演练
  5. 性能损耗实测与调优策略
  6. 高频问题问答(FAQ)
  7. AT模式的适用边界与未来

分布式事务的困境与Seata的选择

在微服务架构中,一个业务操作往往跨越多个服务(如订单、库存、账户),每个服务拥有独立数据库,传统本地事务无法解决跨库的一致性问题,而2PC协议因同步阻塞、协调者单点等缺陷在实际生产中难以落地。

Seata AT模式案例

Seata(Simple Extensible Autonomous Transaction Architecture)作为阿里开源的一站式分布式事务方案,其AT模式(Automatic Transaction)凭借“无侵入、高性能、对业务代码零改造”的特点,成为目前国内使用最广泛的方案,相比TCC需要手写Confirm/Cancel,AT模式利用快照与反向SQL自动完成回滚,这是它最大的杀手锏。

AT模式核心机制剖析(三组件+两阶段)

Seata AT模式由三个核心组件协同工作:

组件 角色 职责
TC (Transaction Coordinator) 独立部署的Server 全局事务的提交/回滚决策、事务状态管理
TM (Transaction Manager) 嵌入在业务发起方 开启全局事务、提交/回滚全局事务
RM (Resource Manager) 嵌入在数据源层 管理分支事务、注册分支、上报状态

两阶段执行流程:

  • Phase 1(执行+快照):业务SQL正常执行,RM在操作数据行前生成前置镜像(查询原始数据),操作后生成后置镜像(新数据),并连同undo_log日志一并写入该事务的本地undo_log表。
  • Phase 2(提交或回滚):若全局事务成功,RM异步删除undo_log快照(一阶段已提交本地事务,二阶段仅清理);若全局失败,RM根据undo_log逆方向生成反向SQL(如DELETE变成INSERT,UPDATE逆向UPDATE),恢复原始数据,若回滚冲突(如脏数据被其他事务修改),则需人工或异步重试。

电商下单案例:从零搭建Seata AT模式

业务场景:用户下单时,同时操作订单服务(db_order)、库存服务(db_stock)、账户服务(db_account),要求“扣库存”和“扣余额”必须与“生成订单”同生共死。

环境准备

  • Seata Server 1.5.2(默认端口8091,注册到Nacos)
  • Spring Cloud Alibaba 2021.0.5.0 + MyBatis-Plus

关键配置(以库存服务为例)

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/db_stock
    driver-class-name: com.mysql.cj.jdbc.Driver
seata:
  enabled: true
  application-id: stock-service
  tx-service-group: my_test_tx_group
  registry:
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
  service:
    vgroup-mapping:
      my_test_tx_group: default

核心代码(订单服务发起全局事务)

@GlobalTransactional(name = "create-order", timeoutMills = 60000)
public void createOrder(OrderDTO dto) {
    // 1. 本地事务:插入订单
    orderMapper.insert(buildOrder(dto));
    // 2. 远程调用:扣减库存(库存服务本地事务,但受全局事务管理)
    stockFeignClient.deduct(dto.getProductId(), dto.getQuantity());
    // 3. 远程调用:扣减余额
    accountFeignClient.decrease(dto.getUserId(), dto.getAmount());
    // 4. 若上述任一步抛异常(如库存不足),AT自动回滚订单插入
}

注意:每个服务必须建一张undo_log表:

CREATE TABLE `undo_log` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `branch_id` bigint(20) NOT NULL,
  `xid` varchar(100) NOT NULL,
  `context` varchar(128) NOT NULL,
  `rollback_info` longblob NOT NULL,
  `log_status` int(11) NOT NULL,
  `log_created` datetime NOT NULL,
  `log_modified` datetime NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

数据一致性验证 & 异常场景回滚演练

模拟失败场景:在账户服务扣款方法中,故意设置余额不足校验并抛出异常。

预期结果

  • 账户服务本地事务回滚(余额不变)。
  • 库存服务的扣减操作被Seata自动反向SQL恢复(库存数量回到扣减前)。
  • 订单服务中的insert记录被删除(快照回滚)。

实际验证:通过查看三张业务表数据,确认无脏数据残留,通过SELECT * FROM undo_log;可看到一阶段结束后生成的日志,二阶段回滚后日志被删除或标记。

性能损耗实测与调优策略

实测数据(4核8G,MySQL 5.7,并发50线程):

  • 无分布式事务:单次事务耗时约15ms。
  • 开启Seata AT:单次事务耗时约28ms(含两阶段通信、镜像生成、全局锁竞争)。
  • 性能损耗约46%,主要开销在全局锁(写隔离)和undo_log持久化

优化建议

  1. 缩短事务时间:避免在全局事务内做远程慢调用(如调用第三方支付),仅保留必要的DB操作。
  2. 批量操作合并:将多条Insert合并为一条批量Insert,减少RM注册分支数。
  3. 调整全局锁超时seata.tm.timeoutseata.rm.global.lock.timeout适当调大,避免频繁锁等待失败。
  4. 分库分表场景:务必保证同一分片内的数据操作,否则AT模式会因跨库查询快照而退化。

高频问题问答(FAQ)

Q1:AT模式与XA模式有什么区别? 答:传统XA是数据库层面的两阶段锁,整个全局事务期间资源被锁定(Phase1不提交),阻塞严重,而AT模式在一阶段就本地提交并释放本地锁,仅保留全局锁(优化了范围,从整表到行),二阶段异步回滚,并发性能显著优于XA。

Q2:AT模式会存在脏写问题吗? 答:会,如果两个全局事务同时操作同一行数据,Seata通过全局写锁进行排队,但如果一个AT事务和一个非Seata管理的本地事务同时操作同一行,则可能发生脏写,解决方案是在业务层面约束所有写路径必须走Seata。

Q3:undo_log磁盘膨胀如何治理? 答:定时任务定期清理已提交成功且超过保留周期的undo_log记录(二阶段提交时标记log_status=1,表示可清理),建议每天凌晨低峰期执行DELETE FROM undo_log WHERE log_status=1 AND log_created < NOW() - INTERVAL 24 HOUR;

Q4:为什么我的回滚没生效?分支事务一直显示PhaseTwo_Timeout? 答:大概率是TC端口不通或网络抖动,导致二阶段回滚请求未到达RM,检查server.port(默认8091)是否被防火墙拦截,以及RM与TC之间的心跳超时设置。

AT模式的适用边界与未来

适用场景

  • 微服务中跨库操作、但并发冲突概率较低的业务(如订单、积分、优惠券)。
  • 业务团队规模大,无法接受TCC的侵入式改造。
  • 读多写少、单行数据竞争不激烈的系统。

不适合

  • 高并发热点账户扣款(如秒杀库存,全局锁会成为性能瓶颈)。
  • 长事务(超过30秒),因为全局锁资源被长时间占用。
  • 需要跨多个Seata集群或异构数据库(如MongoDB)的场景。

未来趋势:Seata 1.6+推出了BR(Branch Report)优化与Seata-Golang版本,以及AT模式结合多级缓存(异步提交)来降低二阶段开销,对于追求极致的团队,可考虑混合架构:核心交易走TCC(如资金),外围低风险业务走AT模式。


行动建议:先在小流量业务(如积分变更)灰度上线Seata AT,同时监控TC的线程池(默认接收线程池coreSize=100,max=500)和RM的全局锁等待时长,用数据决定是否推广到核心链路。

抱歉,评论功能暂时关闭!