1. 模式区别

  • AT 模式:生成的是数据源代理(DataSource Proxy),通过代理拦截 SQL 执行,无需额外配置默认模式(Seata 默认支持 AT 模式),而非“数据库动态代理”(表述更精准)。
  • XA 模式:生成的是 XA 数据源代理(XADataSource Proxy),需在 YAML 中显式配置 seata.tx-mode=XA(而非仅“配置默认模式为 XA”),且依赖数据库原生 XA 协议支持(如 MySQL、Oracle 的 XA 事务能力)。

2. AT 模式流程

  • 第一步会申请全局事务 ID(XID)(需明确“全局”,与分支事务 ID 区分)。
  • 基于原有分布式服务调用逻辑扩展,实现异常后的自动回滚(而非“自动补偿”)——补偿逻辑通过 Undo Log 反向生成 SQL 实现,需明确概念。
  • 事务开始前:申请 XID 并存储到 ThreadLocal(Seata 内部通过 RootContext 管理 XID,MDC 通常用于日志打印携带 XID,非核心存储位置,修正存储载体);往 TC(事务协调器)的全局事务表(global_table)插入记录;将数据库连接的 Auto Commit 改为 false(手动提交)。
  • SQL 执行前:仅对写操作(insert/update/delete) 分析语法并生成前置镜像(查询操作不会触发镜像生成,也不会拼接 for update——for update 是用户业务 SQL 自主使用的行锁语法,AT 模式通过全局锁控制并发,修正“查询加锁”错误)。
  • SQL 执行后:对写操作获取后置镜像(基于主键/唯一键查询,确保与前置镜像对应),此时事务未提交。
  • 后续操作:将前置/后置镜像、Undo Log 写入本地数据库的 undo_log 表(而非“Seata 的 TC 表”,TC 不存储镜像,仅管理事务状态);向 TC 注册分支事务,TC 分配分支事务 ID(Branch ID),并将分支事务与全局事务关联;同时获取全局锁(往 TC 的 lock_table 插入记录,键为“表名+主键”,而非“事务 ID 作为标识”,修正锁标识逻辑)。
  • 事务提交/回滚
    • 若正常提交:TC 通知各分支删除 Undo Log,释放全局锁;
    • 若出现异常:TC 通知各分支执行 Undo Log 回滚(反向 SQL 恢复数据),之后删除 Undo Log 并释放锁;
    • 后置处理:清除 RootContext 中的 XID,释放数据库连接等资源(修正“清除 XID”的载体)。

3. XID 传递

  • 微服务间调用时,发起方从 RootContext(而非仅 MDC)获取 XID,通过 HTTP 请求头(默认键为 TX_XID)传递给接收方;
  • 接收方通过 Spring MVC 拦截器/Feign 拦截器解析请求头中的 TX_XID,并绑定到自身的 RootContext 中,从而实现 XID 跨服务传递(MDC 仅用于日志携带 XID,方便排查,非传递核心逻辑)。

4. 其他要点

  • AT 模式的全局锁:获取时往 TC 的 lock_table 插入记录,键为“resource_id(数据源标识)+表名+主键”(而非“事务 ID 作为标识”,事务 ID 用于关联全局事务,锁键才是唯一标识);插入失败时会阻塞并重试(默认重试次数可配置),避免并发写冲突。
  • AT 模式的默认隔离级别:读已提交(Read Committed),但通过“全局锁+本地锁”的协同,可避免“脏写”,并在一定程度上缓解“不可重复读”(需补充隔离级别的实际效果)。
  • 取消全局事务绑定:若某服务不想参与全局事务,需在调用下游服务前执行 RootContext.unbind()(解除当前 XID 绑定),下游服务将不会注册分支事务;若后续需重新参与,需再次绑定 XID(修正“调用下游前再重新绑定”的顺序逻辑)。