SpringCloud Alibaba 核心组件解析分布式事务Seata技术栈Spring Boot 3.2.0 Spring Cloud Alibaba 2023.0.0.0-RC1 Seata AT 模式 Nacos MyBatis-Plus3.1 是什么 — 分布式事务的核心概念3.1.1 生活化类比跨国汇款你在中国的银行 A 向美国的银行 B 汇款 1000 美元 ① 银行 A 从你的账户扣除 $1000 ② 银行 A 通知银行 B给那个账户加 $1000 ③ 银行 B 收到通知给目标账户增加 $1000 问题如果第③步失败了网络中断、银行 B 系统故障 第①步已经扣了你的钱怎么办 → 需要分布式事务来回滚第①步的操作。3.1.2 技术定义分布式事务一个业务操作跨越多个独立的数据库/服务需要保证所有操作要么全部成功Commit要么全部失败回滚Rollback。3.1.3 Seata 的角色分工┌──────────────────────┐ │ TC (Transaction │ ← 事务协调者独立部署的 seata-server │ Coordinator) │ 维护全局事务的提交/回滚状态 └──────────┬───────────┘ │ 注册/汇报 ┌──────────┼──────────┐ │ │ │ ▼ ▼ ▼ ┌─────────┐┌─────────┐┌─────────┐ │ TM ││ RM ││ RM │ │(发起方) ││(参与方) ││(参与方) │ └─────────┘└─────────┘└─────────┘角色说明对应本项目的服务TC事务协调者独立部署seata-serverTM事务管理器标注GlobalTransactionalseata-order-service2001RM资源管理器管理分支事务seata-storage-service2002、seata-account-service20033.2 为什么 — 四种分布式事务模式对比3.2.1 AT 模式本项目使用推荐原理一阶段执行业务 SQL 记录 undo_log二阶段提交时删除 undo_log回滚时执行反向 SQL。优点缺点✅ 对业务代码零侵入❌ 依赖数据库 ACID✅ 自动生成回滚 SQL❌ 仅支持关系型数据库✅ 性能好一阶段即提交❌ 需要额外的 undo_log 表3.2.2 四种模式对比模式数据一致性性能业务侵入适用场景AT最终一致⭐⭐⭐⭐无一般微服务推荐TCC最终一致⭐⭐⭐⭐⭐高需实现 try/confirm/cancel高性能场景SAGA最终一致⭐⭐⭐⭐⭐中需实现补偿长事务/老系统XA强一致⭐⭐无银行/金融3.2.3 AT 模式回滚原理一阶段执行业务 SQL 记录 undo_log Order → INSERT INTO t_order (id1, status0) undo_log: DELETE FROM t_order WHERE id1 Storage → UPDATE t_storage SET usedused10, residueresidue-10 WHERE product_id1 undo_log: UPDATE t_storage SET usedused-10, residueresidue10 WHERE product_id1 Account → UPDATE t_account SET usedused100, residueresidue-100 WHERE user_id1 undo_log: UPDATE t_account SET usedused-100, residueresidue100 WHERE user_id1 二阶段-提交无异常删除所有 undo_log 二阶段-回滚异常触发执行 undo_log 中的反向 SQL数据恢复到一阶段前3.3 怎么做 — Seata AT 完整实战3.3.0 小 Demo先暴露痛点// ❌ 没有分布式事务时本地事务管不了远程调用Transactional// 只管当前数据库publicvoidcreateOrder(Orderorder){orderMapper.insert(order);// ✅ 这个能回滚storageFeignApi.decrease(...);// ❌ Feign 调用不受 Transactional 控制accountFeignApi.decrease(...);// ❌ 万一这里失败库存已经扣了}3.3.1 项目架构用户下单 (userId1, productId1, count10, money100) │ ▼ ┌─────────────────────────┐ │ seata-order-service2001 │ TM发起方 │ 数据库: seata_order │ → ① 创建订单 │ GlobalTransactional │ └──────┬──────────┬────────┘ │ Feign │ Feign ▼ ▼ ┌──────────────┐ ┌──────────────────┐ │ storage-2002 │ │ account-2003 │ │ seata_storage│ │ seata_account │ │ → ② 扣库存 │ │ → ③ 扣余额 │ └──────────────┘ └──────────────────┘3.3.2 步骤 ①启动 Seata Server# 下载 seata-server修改 conf/application.yml 注册到 Nacos# seata:# registry:# type: nacos# nacos:# server-addr: 127.0.0.1:8848# group: SEATA_GROUP# application: seata-server# Windows 启动seata-server.bat3.3.3 步骤 ②三个服务公共 Seata 配置# 每个服务的 application.yml订单/库存/账户 都相同seata:registry:type:nacosnacos:server-addr:127.0.0.1:8848namespace:group:SEATA_GROUPapplication:seata-servertx-service-group:default_tx_group# 事务分组service:vgroup-mapping:# 事务分组 → TC 集群映射default_tx_group:defaultdata-source-proxy-mode:AT# AT 模式logging:level:io.seata:info配置四要素配置含义tx-service-group事务分组名与 TC 端service.vgroupMapping对应vgroup-mapping将事务分组映射到 TC 集群名生产环境有多个 TC 集群registry.type: nacosTM/RM 通过 Nacos 发现 TC 地址data-source-proxy-mode: AT启用 AT 模式数据源代理3.3.4 步骤 ③订单服务 — TM核心// seata-order-service2001/.../service/impl/OrderServiceImpl.javaServiceSlf4jpublicclassOrderServiceImplextendsServiceImplOrderMapper,OrderimplementsOrderService{ResourceprivateOrderMapperorderMapper;ResourceprivateStorageFeignApistorageFeignApi;// Feign → 库存服务ResourceprivateAccountFeignApiaccountFeignApi;// Feign → 账户服务OverrideGlobalTransactional(namezzyy-create-order,rollbackForException.class)// ↑ ↑ ↑ 核心声明全局事务publicvoidcreate(Orderorder){// 查看全局事务 XIDSeata 自动生成StringxidRootContext.getXID();log.info(开始新建订单, xid: {},xid);// ① 新建订单order.setStatus(0);intresultorderMapper.insert(order);OrderorderFromDBnull;if(result0){orderFromDBorderMapper.selectById(order.getId());log.info(------- 新建订单成功: {},orderFromDB);// ② 扣减库存Feign 远程调用log.info(------- 开始调用 Storage 扣减库存);storageFeignApi.decrease(orderFromDB.getProductId(),orderFromDB.getCount());log.info(------- 结束调用 Storage);// ③ 扣减余额Feign 远程调用log.info(------- 开始调用 Account 扣减余额);accountFeignApi.decrease(orderFromDB.getUserId(),orderFromDB.getMoney());log.info(------- 结束调用 Account);// ④ 修改订单状态 → 已完结orderFromDB.setStatus(1);orderMapper.updateById(orderFromDB);log.info(------- 修改订单状态完成);}log.info(结束新建订单, xid: {},xid);}}3.3.5 步骤 ④Feign 接口// cloud-api-commons/.../apis/StorageFeignApi.javaFeignClient(nameseata-storage-service)publicinterfaceStorageFeignApi{PostMapping(/storage/decrease)ResultDatadecrease(RequestParam(productId)LongproductId,RequestParam(count)Integercount);}// cloud-api-commons/.../apis/AccountFeignApi.javaFeignClient(valueseata-account-service)publicinterfaceAccountFeignApi{PostMapping(/account/decrease)ResultDatadecrease(RequestParam(userId)LonguserId,RequestParam(money)Longmoney);}3.3.6 步骤 ⑤库存服务 — RM// seata-storage-service2002/.../service/impl/StorageServiceImpl.javaServiceSlf4jpublicclassStorageServiceImplextendsServiceImplStorageMapper,StorageimplementsStorageService{ResourceprivateStorageMapperstorageMapper;Overridepublicvoiddecrease(LongproductId,Integercount){log.info(-------storage-service 扣减库存开始);storageMapper.decrease(productId,count);log.info(-------storage-service 扣减库存结束);}}// SQL: UPDATE t_storage SET usedused#{count}, residueresidue-#{count}// WHERE product_id#{productId}3.3.7 步骤 ⑥账户服务 — RM含超时测试// seata-account-service2003/.../service/impl/AccountServiceImpl.javaServiceSlf4jpublicclassAccountServiceImplextendsServiceImplAccountMapper,AccountimplementsAccountService{ResourceAccountMapperaccountMapper;Overridepublicvoiddecrease(LonguserId,Longmoney){log.info(-------account-service 扣减余额开始);accountMapper.decrease(userId,money);// 模拟超时异常 → 触发全局事务回滚myTimeOut();// int age 10/0; // 也可模拟除零异常log.info(-------account-service 扣减余额结束);}privatestaticvoidmyTimeOut(){try{TimeUnit.SECONDS.sleep(65);}catch(InterruptedExceptione){e.printStackTrace();}}}测试调用http://localhost:2001/order/create?...→ 账户服务 sleep 65 秒 → TC 超时 → 全局回滚订单删除、库存恢复、余额恢复。3.4 深入原理 — undo_log 表结构每张参与分布式事务的表都需要创建undo_log表CREATETABLEundo_log(idbigintNOTNULLAUTO_INCREMENT,branch_idbigintNOTNULLCOMMENT分支事务ID,xidvarchar(100)NOTNULLCOMMENT全局事务ID,contextvarchar(128)NOTNULL,rollback_infolongblobNOTNULLCOMMENT回滚信息反向SQL,log_statusintNOTNULLCOMMENT状态:0正常,1已全局提交,log_createddatetimeNOTNULL,log_modifieddatetimeNOTNULL,PRIMARYKEY(id),UNIQUEKEYux_undo_log(xid,branch_id))ENGINEInnoDB;3.5 面试题Q1Seata 的 AT 模式和 TCC 模式有什么区别什么时候用哪个答AT自动生成反向 SQL业务代码零侵入依赖数据库 ACID。适合一般业务场景。TCC需手动实现 Try预留、Confirm确认、Cancel取消三个接口。性能更好但开发成本高。适合核心高性能链路。选型原则能用 AT 则 AT除非有极致性能要求才用 TCC。Q2GlobalTransactional 和 Transactional 有什么区别答Transactional是 Spring 本地事务只控制单个数据源GlobalTransactional是 Seata 全局事务由 TC 协调多个 RM 的本地事务实现跨服务的整体提交或回滚。Q3Seata AT 模式下如果 TC Server 挂了怎么办答TC 需要高可用集群部署。AT 模式下一阶段本地事务已提交业务不会阻塞但全局回滚能力暂时丧失。TC 恢复后会根据 undo_log 继续处理未完成的全局事务。3.6 踩坑指南坑现象原因解决 undo_log 不存在Table xxx.undo_log doesnt exist未在每个业务库创建 undo_log 表在每个参与分布式事务的数据库中执行建表 SQL 全局事务不回滚异常了数据还在rollbackFor Exception.class未配置显式写GlobalTransactional(rollbackFor Exception.class) TC 连接不上启动报can not connect to seata-serverTC 未启动或 Nacos 配置不对确认seata-server.bat已运行Nacos 中可看到seata-server服务 MyBatis-Plus 版本冲突NoSuchMethodErrorSeata 对 MyBatis 版本敏感统一使用父 POM 管理的版本 超时设置默认超时太短TC 默认全局事务超时 60s在 TC 端application.yml中调大service.default.grouplist.timeout3.7 章节总结要点说明三大角色TC协调者 TM发起方GlobalTransactional RM参与方AT 模式自动生成 undo_log 反向 SQL业务零侵入二阶段执行提交或回滚四种模式AT推荐、TCC高性能、SAGA长事务、XA强一致核心注解GlobalTransactional(name..., rollbackForException.class)核心配置tx-service-group vgroup-mapping registry.type >