Seata AT 模式与 XA 模式的区别
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(修正“调用下游前再重新绑定”的顺序逻辑)。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 GoofySatoshi's Blog!
评论