面向 P6-P7+ 工程师 体系架构型 · 长文 约 9,000 字 信息截止 2026-08

凤凰架构视角下分布式事务选型:
TCC、Seata AT 与本地消息表的场景取舍

这篇文章不是 Seata 配置速查表,而是把一次「产品要求强一致 + 架构师上全局 XA + 大促峰值下 TC 成为瓶颈 + 悬挂订单与积分重复发放」叠加成端到端资损的复合事故,还原成可复用的选型框架: 何时真正需要分布式事务、TCC / AT / 本地消息表各自锁定的失败窗口、 以及如何用 Seata 2.1.x 把模式落地成可演练、可对账的治理面。

主线风格:体系架构 55% + 框架训练 30% + 问题现场 15% 版本假设:模式通用;示例基于 Seata 2.1.0 + Spring Cloud Alibaba 2023.0.x;兼容 Seata 1.8.x 语义 证据等级:官方文档优先;场景数字为合成示例;标注推断与生产经验
问题现场 · 复合场景

1当「必须强一致」遇上大促峰值:XA 误选导致的悬挂与重复发放

复合场景 · 综合电商下单链路与金融积分发放的典型对话,非指代单一具体事件 COMPOSITE SCENARIO

某会员电商平台在重构订单中台时,产品提出「下单、扣库存、发积分必须强一致,用户看到的积分与订单状态不能差一秒」。 架构评审会上,团队将需求直接映射为全局分布式事务,选型 Seata AT 模式, 在 order-serviceinventory-servicepoints-service 三个微服务上开启 @GlobalTransactional,TC 集群部署 3 节点,压测环境验证通过。

大促 T+0 峰值 QPS 达设计值 8000(合成示例),前 10 分钟一切正常。 T+12 min,inventory-service 某分库出现热点 SKU 行锁等待,P99 从 80 ms 升至 2.3 s。 Seata TC 全局会话数从常态 200 飙至 1.8 万,branch_register 延迟上升, 部分全局事务在二阶段提交窗口内超时——订单库已 commit,库存 undo 待回滚,积分服务 Confirm 已执行。

T+18 min,对账脚本发现:悬挂订单 3400 笔(合成示例)——订单状态为「已支付」、库存未扣减、积分已到账; 另有 120 笔重复积分发放,因补偿任务与 Seata 异步回滚并发执行且缺少幂等键。 值班工程师的三个困惑正是本文要拆解的复合模式: 业务真的需要强一致吗?AT 为何在峰值下放大行锁?为何「上了 Seata」仍要本地消息表做对账?

T+25 min,架构组紧急切换:核心下单链路改为本地消息表 + 异步扣库存, 积分发放走 Outbox 消费者;仅「跨主体资金划转」子域保留 TCC。 事后复盘写入架构委员会结论:默认最终一致 + 显式对账,而非默认全局 XA。

上述场景折射出一个在《凤凰架构》中反复强调的误区:把「业务不能出错」等同于「必须强一致分布式事务」。 周志明在书中指出,分布式系统首先要接受分区与延迟客观存在, 架构师的工作是在可接受的异常窗口内选择协调机制—— 是同步阻塞的 2PC/XA,是业务层补偿的 TCC/Saga,还是异步可靠的本地消息表, 取决于资损边界、吞吐预算与运维复杂度,而非名词上的「一致性级别」。

版本说明:本文模式层与语言无关,示例基于 Seata 2.1.0(Apache Seata 2.x 主线,AT/TCC 语义与 1.8.x 兼容) 与 Spring Cloud Alibaba 2023.0.x 默认绑定版本。Seata 1.8.x 在大量生产环境仍运行,机制描述以 Seata 官方文档 与《凤凰架构》分布式事务章节为准。性能数字若无特别说明均为合成示例

阅读前提:你已理解微服务拆分、数据库本地事务、RPC 超时与消息队列至少一次语义。 本文不再解释「什么是 ACID」,而是聚焦三种主流模式在失败窗口如何耦合、如何用架构手段选型。 55% 篇幅用于谱系与机制推导,30% 用于 Seata 2.1.x 落地与配置边界,15% 留给复合现场与排障顺序。 目标读者为已在生产环境处理过悬挂、重复与对账事故的 P6-P7+ 工程师。

已核验事实 · CAP 与分布式事务的边界

在分区(P)发生时,分布式系统无法在「线性一致(C)」与「可用(A)」之间同时满足——这是 CAP 定理的直接推论。 全局 2PC/XA 在协调者可用时提供强一致,但在协调者故障或长事务持有锁时,可用性与吞吐显著下降。 来源:Brewer CAP、Gray & Reuter 事务处理理论;与《凤凰架构》「分布式的代价」一节一致。

1.1 告警时间线:四类信号交织

复合事务事故的识别特征是全局会话、行锁、业务对账、补偿任务四类信号在同一时间窗重叠。 下表按演练时间线整理(数字为合成示例):

时刻Seata TC数据库侧业务侧
T+0~10 min全局事务 ~200,延迟正常行锁竞争低下单成功率 99.95%
T+10~18 min会话 1.8 万,register 延迟升热点 SKU 行锁等待 > 2 s支付成功但库存偶发超时
T+18~25 min二阶段超时、异步回滚队列堆积undo_log 表膨胀悬挂订单、重复积分
T+25 min+紧急降级,关闭非核心 @GlobalTransactional人工对账 SQL 执行活动公告、补偿发券

1.2 与单次超时的边界区分

维度单次 RPC 超时(可重试)复合事务事故(需架构介入)
数据状态单服务本地回滚跨库不一致,需对账
TC 指标全局会话稳定branch 注册/报告延迟持续恶化
锁持有短于单事务全局锁跨多 RPC,放大热点
触发源网络抖动、单依赖慢误选 XA + 峰值 + 补偿缺幂等
图 1 · 「强一致误判」导致的跨域不一致因果链
自制示意图 · 复合场景抽象
需求层 → 选型层 → 峰值放大 → 业务后果(示意) 产品:不能差一秒 Requirement → Strong Consistency 架构:全局 @GlobalTransactional Seata AT on Hot Path 大促峰值 + 热点行锁 Lock Amplification TC 全局会话堆积 二阶段超时 · 异步回滚 跨库状态分裂 订单 commit · 库存悬挂 · 积分已发 补偿任务无幂等 与 Seata 回滚并发 对账脚本滞后 资损窗口扩大 误配监控 TC 绿但业务悬挂 业务后果:悬挂订单 · 重复积分 · 紧急降级 「上了分布式事务」不等于「不会不一致」
读图方式:自上而下读四层。顶部红色/琥珀色为需求误判与选型(把「不能出错」等同于全局 XA); 紫色与青色为峰值下机制响应(TC 堆积、跨库分裂); 中间层是运维与对账最先看到的表象(补偿并发、对账滞后、TC 假 green); 最底行是业务层最终受损。注意「热点行锁 + 全局事务」的耦合——这是 AT 模式上大促峰值的主风险。

1.3 现场应急:有序止损

复合事务事故的恢复顺序应为冻结全局事务入口 → 隔离热点 → 对账 → 补偿: 先关闭非核心链路上的 @GlobalTransactional、再扩容 TC 仅当 register 延迟是瓶颈(否则无效)、 再启动对账脚本标记悬挂、最后才批量补偿且必须带幂等键。 顺序颠倒——尤其「先批量补偿再对账」——往往把悬挂变成重复发放。 架构师在 war room 的第一职责是划定资损冻结窗口:禁止改 undo 策略、禁止手工删 global_table 记录。

1.4 需求翻译:把「强一致」改写成 invariant

复盘会上产品坚持「积分必须与订单同时成功」,架构组用一张 invariant 表完成需求翻译: 「用户最终看到的积分 = 已支付订单应付 × 比例」可在 T+15 min 内通过对账满足; 「支付成功瞬间积分栏非零」仅影响体验,不构成资损 invariant。 前者 Outbox 足够,后者若必须满足才考虑同步写——而同步写仍不必全局 AT,单库双表或 TCC Try 冻结即可。 这一翻译步骤若前置到立项阶段,可避免 80% 的误用 XA(合成经验比例,非统计结论)。 《凤凰架构》强调架构师是业务与技术之间的翻译者——分布式事务选型首先是语义翻译,其次才是框架选型。

体系架构 · 谱系

2凤凰架构中的分布式事务谱系:从 2PC 到最终一致

《凤凰架构》将分布式事务问题置于演进叙事中:单机 ACID 是默认能力, 拆分微服务后,一次用户操作跨越多个自治数据库,「事务」从存储引擎内部机制变成跨进程协调协议。 周志明强调:架构师应先问「能否用单库事务 + 领域边界」消解问题,再问「若不能,接受何种异常窗口」。

2.1 协调模式谱系

  • 强一致 2PC/XA:协调者统一投票,参与者阻塞至 commit/abort。代表:数据库 XA、Seata AT 底层两阶段。代价:锁、协调者单点、长事务吞吐低。
  • TCC(Try-Confirm-Cancel):业务层三阶段,Try 预留资源,Confirm 确认,Cancel 释放。强一致语义由业务代码保证,无全局行锁,但侵入性高。
  • Saga:长事务拆为本地事务 + 补偿链,正向执行、失败反向补偿。适合长流程,一致性强弱取决于补偿设计与对账。
  • 本地消息表 / Outbox:同库写入业务数据与待发消息,异步投递下游。接受最终一致,通过对账与幂等收敛异常窗口。
  • 最大努力通知:上游多次通知直至下游 ACK,适合支付回调等场景,需配套查询与对账接口。

用一个下单例子固定直觉:创建订单、扣库存、发积分涉及三个库。 若用 XA/AT,三次写在一个全局事务内——任一参与者慢,全局锁持有时间延长; 若用 TCC,Try 阶段冻结库存与积分额度,Confirm 异步或同步提交; 若用本地消息表,订单库内写订单 + 消息行,库存与积分由消费者至少一次处理并幂等。

架构推断 · 默认立场

在《凤凰架构》语境下,推荐默认立场为: 单库事务优先 → 本地消息表/Outbox → Saga → TCC → 全局 AT/XA(从严到宽排序的是一致性强弱, 从宽到严排序的是推荐优先级)。 仅当业务明确无法容忍秒级不一致且流量可控时,才将 AT/TCC 放上核心热路径。

2.2 一致性与隔离级别:别混淆名词

产品口中的「强一致」常混指三种不同约束: (1)用户可见状态不能矛盾;(2)财务账本不能差分;(3)实时读到的必须是写后最新值。 架构师应将其翻译为可测 invariant:例如「积分总额 = 订单应付金额 × 比例」、 「库存不能为负」。许多 invariant 可在最终一致 + 对账下满足,无需全局 XA。

模式一致性强弱吞吐与锁侵入性典型失败窗口
XA / Seata AT强(分支全 commit/rollback)全局锁、热点敏感低(AT)二阶段超时 → 悬挂
TCC强(业务 Confirm/Cancel)无全局 DB 锁高(三接口)空回、悬挂、幂等
Saga最终 / 可配置较高中(补偿逻辑)补偿失败、乱序
本地消息表最终一致中(Outbox 组件)重复消费、消息滞后

2.3 与微服务边界的耦合

书中「限界上下文」与事务选型直接相关:理想情况下,一个聚合根的生命周期应落在单库内, 跨上下文协作优先用事件而非同步 XA。拆分过细的服务边界若强行 AT, 等于用基础设施掩盖建模缺陷——事务越全局,团队越难独立演进 schema 与发布节奏。 与 T14 微服务治理、T15 异地多活衔接:跨单元场景下全局 2PC 几乎不可用,Saga/TCC + 单元内消息表是常态。

2.4 演进视角:从 JTA 到 Seata 再到云原生

Java 传统 JTA 绑定应用服务器与单厂商数据源;Seata 将 TC/TM/RM 模型开源并适配微服务注册发现; 云厂商托管 DTS 进一步隐藏 TC 运维。选型时不应问「是否上 Seata」,而应问 协调边界放在哪一层、失败时谁对账:框架可以托管协议,资损边界仍属业务架构。

2.5 Saga 与 TCC 的边界(补充谱系)

评审材料常把 Saga 与 TCC 混为一谈。简要区分:Saga 每步是已提交的本地事务, 失败则按逆序调用补偿;中间态对外可见,适合长流程(差旅预订、保险核保)。 TCC 在 Try 与 Confirm 之间资源处于预留态,对外可设计为不可见或「处理中」, 适合短链路、高价值、需避免中间态被误用的场景。 Seata 2.1.0 同时提供 Saga 状态机与 TCC 注解,但状态机编排解决的是步骤顺序与持久化, 不替代补偿接口的幂等设计。若流程超过五步且可容忍中间态,优先 Saga;若涉及资金冻结且步骤少,优先 TCC。 二者均可与 Outbox 组合:Saga 某步完成后发事件驱动下一步,避免同步链过长。

体系架构 · TCC

3TCC:业务层三阶段与空回、悬挂、幂等

TCC 将两阶段提交上移到业务语义:Try 预留资源(冻结库存、预扣余额), Confirm 确认消耗,Cancel 释放预留。Seata 2.1.x 的 TCC 模式通过 @TwoPhaseBusinessAction 注解绑定 Try 与 Confirm/Cancel 方法,TM 仍由 @GlobalTransactional 或 TCC 事务管理器驱动。

3.1 三阶段职责

  1. Try:检查并预留;必须幂等,重复 Try 返回相同预留结果。
  2. Confirm:确认业务;仅当 Try 成功后可执行;必须幂等,防重复 Confirm 重复扣款。
  3. Cancel:释放预留;Try 未 Confirm 时执行;须处理「Try 未到达」的空回。
已核验事实 · Seata TCC 注解模型

Seata TCC 模式要求业务提供 Try、Confirm、Cancel 三个方法, @TwoPhaseBusinessAction(name, commitMethod, rollbackMethod) 声明资源名与二阶段方法名。 TC 记录 branch 状态,二阶段由 TC 异步驱动或同步调用。来源: Seata 官方文档 · TCC 模式

3.2 三大经典异常:空回、悬挂、幂等

空回(Empty Rollback):Cancel 先于 Try 到达(网络重排),Cancel 不应报错但须识别「无预留可释」。 悬挂(Hang):Cancel 已执行而 Try 后到,Try 不得再创建有效预留——需事务控制表记录状态。 幂等:Confirm/Cancel 重复调用不得改变已终态结果。生产实现通常引入 tcc_transaction_log 表,以 xid + branch_id + action 为幂等键。

异常触发条件后果防御
空回Cancel 先于 Try误释资源Cancel 检查 Try 标记;无则空操作
悬挂Cancel 后 Try 到达预留复活,资损Try 前查 log,Cancel 后拒绝 Try
重复 ConfirmTC 重试二阶段重复扣款Confirm 幂等表终态短路
Confirm 丢失TC 故障长期冻结定时对账 + 人工 Confirm

3.3 TCC 适用边界

TCC 适合跨主体、高价值、流量可控的写路径:资金划转、授信占用、库存预占与确认分离。 不适合:只读聚合、可异步化的通知类副作用、超高 QPS 热点 SKU——Try/Confirm 双写与日志表会成为新热点。 《凤凰架构》在讨论服务容错时也指出:同步协调链越长,与 T10 可靠性四件套的耦合越紧,超时预算必须重算。

3.4 TCC 与 Seata 2.1.0 落地清单

框架训练侧最小清单:为每个参与方实现 Try/Confirm/Cancel;引入 fence 或事务日志表; 在 TM 层设置短于业务 SLA 的全局 timeout;Confirm/Cancel 注册为独立 RPC 且可重入; 压测覆盖「Try 超时」「Confirm 重复」「Cancel 先于 Try」三条路径。 与 AT 不同,TCC 分支注册不依赖 undo_log,但业务日志表成为新运维对象—— 需纳入备份、慢查询监控与数据保留策略,避免 fence 表膨胀成为下一个 hidden bottleneck。

常见陷阱 · 把 TCC 当「加强版 AT」

团队仅在接口上加 @TwoPhaseBusinessAction,Try 直接扣减库存而非冻结, Cancel 用「加回库存」——本质仍是非幂等写,Confirm 失败时无法恢复。 TCC 要求 Try 阶段可撤销的预留语义,不是把本地事务拆成三个 RPC 名字。

体系架构 · Seata AT

4Seata AT:无侵入两阶段与 undo_log 失败窗口

AT(Automatic Transaction)模式是 Seata 默认推广模式:业务 SQL 无需改写法, RM 拦截本地 SQL,一阶段提交并写 undo_log,二阶段 TC 通知全局 commit 或 rollback。 Rollback 通过 undo 镜像生成反向 SQL,实现无业务侵入的补偿。

4.1 一阶段与二阶段机制

  1. 解析 SQL,查询前镜像(before image),执行业务 SQL,查询后镜像(after image)。
  2. 插入 undo_log,提交本地事务(释放本地锁,但全局锁由 TC 协调)。
  3. 向 TC 注册 branch;全局 commit 时异步删除 undo_log;rollback 时用 undo 生成补偿 SQL。
已核验事实 · AT 模式与 undo_log

Seata AT 一阶段在本地事务提交后持有全局锁(默认 AT 模式), 二阶段 commit 仅删 undo_log;rollback 读 undo_log 生成反向更新。 要求数据库支持可解析元数据(MySQL/InnoDB 等)。来源: Seata 官方文档 · AT 模式

4.2 全局锁与热点行

AT 在全局事务未 commit 前,对修改行持有全局锁,其他全局事务不能修改同一行—— 这是避免脏写的代价。大促热点 SKU 若走 AT,多个用户的全局事务竞争同一行, 等价于把分布式锁集中在单行上,P99 急剧上升。架构评审应识别热点实体, 热点写路径改用 TCC 预留、或异步扣减 + 对账,而非全局 AT。

4.3 AT 的典型失败窗口

  • 二阶段 commit 超时:一阶段已提交,TC 未收到 commit 响应 → branch 状态 UNKNOWN,需 TC 重试与人工介入。
  • rollback 失败:undo 与当前数据不一致(业务手工改数)→ 需对账修复。
  • undo_log 堆积:大促峰值 + 长全局事务 → 表膨胀,影响磁盘与清理任务。
图 2 · Seata AT 两阶段与失败窗口
Seata 2.1.x · 机制抽象
TM 开启全局事务 → RM 一阶段 → TC 决策 → 二阶段 TM TC RM 订单库 RM 库存库 begin 一阶段:执行业务 SQL + 写 undo_log + 本地 commit + 注册 branch 全局锁持有至二阶段完成 · 热点行竞争在此放大 二阶段 Commit 异步删 undo_log 二阶段 Rollback undo 镜像生成反向 SQL 失败窗口:TC 超时 · branch UNKNOWN · undo 与现数据不一致 一阶段已 commit 的分支无法「当作未发生」——必须对账
读图方式:从左到右读角色(TM/TC/RM),再读一阶段长条(本地已提交是关键转折点), 最后分叉到 Commit 与 Rollback。重点看底部失败窗口: AT 的「无侵入」不等于「无状态分裂」——一阶段提交后,全局失败只能补偿而非回滚时间。

4.4 AT 与本地事务混用

同一服务内,未纳入全局事务的本地写与 AT 分支并发时,可能出现脏读业务逻辑: 本地写已可见,AT 分支随后 rollback。规范是:全局事务边界内全部 DML 走同一数据源代理, 只读查询若需一致视图应放在全局 commit 之后或使用快照读。Seata 2.x 支持多数据源代理, 但每个 RM 仍对应独立 branch,跨库热点问题不因此消失。

常见陷阱 · AT 与 REQUIRES_NEW 嵌套

@GlobalTransactional 方法内声明 @Transactional(propagation = REQUIRES_NEW) 时, 内层提交的分支可能未被 Seata 代理正确注册,全局 rollback 时留下「已提交孤岛」。 复合场景中的部分悬挂订单,经排查根因是积分服务在独立新事务中写入了流水, 却未纳入 xid 管辖。规范:全局事务方法内禁止随意 REQUIRES_NEW; 若必须独立提交,该写路径应移出全局边界或改 Outbox。

4.5 Seata 1.8.x 与 2.1.0 迁移注意

生产环境大量集群仍运行 Seata 1.8.x(Spring Cloud Alibaba 2021.x 绑定)。 2.1.0 在 TC 高可用、配置中心集成与 metrics 暴露上增强,但 AT/TCC 核心语义不变。 升级路径建议:先升级 TC 至 2.1.0 并保持 RM 1.8.x 客户端兼容窗口,验证 global_table 迁移脚本与 Nacos 配置项命名变更;再分批升级 RM。 切勿在大促窗口做 TC 大版本跳跃——曾有机房在升级窗口丢失未完成二阶段会话, 触发批量 UNKNOWN branch,对账耗时整周末。

体系架构 · 本地消息表

5本地消息表:最终一致、Outbox 与对账闭环

本地消息表(Transactional Outbox)的核心思想: 在同一本地数据库事务内完成业务写入与「待发送消息」写入, 再由独立投递器(Polling Publisher 或 CDC)将消息发往 MQ, 下游消费者幂等处理,达成跨服务最终一致。 《凤凰架构》将其归入「可靠事件/可靠消息」—— 用消息交换替代跨库 2PC,是互联网规模下最常用的基线方案。

5.1 表结构与状态机

典型 Outbox 表字段:idaggregate_typeaggregate_idpayloadstatus(NEW/SENT/FAILED)、created_atsent_at。 投递器扫描 NEW 记录 → 发 MQ → 更新 SENT;失败则指数退避重试,超过阈值告警人工介入。 与 Seata 无关,不依赖 TC——一致性的锚点是本地 ACID + 至少一次投递 + 消费幂等

生产经验 · 投递器与 DB 压力

轮询投递器切勿 SELECT * FROM outbox WHERE status='NEW' LIMIT 1000 无索引扫全表。 生产应使用 status + created_at 复合索引,或 Debezium CDC 读 binlog 推 Kafka。 曾有多起事故在峰值因 Outbox 扫表打满从库 IOPS,与业务 SQL 争抢连接—— Outbox 表应独立分库或限流投递,并与业务表分开容量规划。

5.2 与 Seata 的协作关系

复合场景紧急降级后,团队将「扣库存 + 发积分」改为 Outbox,并非否定 Seata, 而是按步骤拆分一致性强弱: 订单创建与支付状态仍可在单库本地事务内完成; 库存扣减消息被消费后,消费者用业务幂等键(orderId + skuId)扣减; 积分同理。悬挂订单通过对账脚本发现「已支付无扣库存消息 SENT」即可补偿重发。 此模式异常窗口可预测(MQ P99 + 消费延迟),且峰值不经过 TC。

5.3 Inbox 与幂等表

消费侧建议 Inbox 表或 Redis 幂等键,记录 messageIdbizId, 插入唯一约束防重复。复合场景中 120 笔重复积分,若消费端有 UNIQUE(order_id, event_type),第二次插入会失败并告警,而非静默重复。 架构评审检查项:生产端 Outbox + 消费端 Inbox 成对出现,缺一即半套方案。

5.4 CDC 投递 vs 轮询:容量与延迟权衡

轮询投递实现简单,但在 Outbox 表超过千万行后,即使有索引,峰值扫描仍可能与业务争抢 IOPS。 Debezium 等 CDC 方案读 binlog,延迟通常低于轮询一个数量级,且对业务库压力更小, 但引入 Kafka Connect 集群与 schema 变更协调成本。架构师选型应问: 异常窗口 P99 要求是否低于 500 ms?若是,倾向 CDC; 若可接受秒级且团队规模小,轮询 + 分区 Outbox 表足够。 《凤凰架构》在事件驱动章节强调:投递可靠性不取决于 MQ 品牌,而取决于 业务库与消息系统的原子写入边界是否画对。

复合场景降级后,团队将积分 Outbox 从轮询改为 Canal 推 MQ,投递 P99 从 2.1 s 降至 180 ms(合成示例), 近线对账悬挂笔数下降 70%——并非 CDC 魔法,而是缩短异常窗口使补偿任务不必与峰值重叠。

环节失败模式防护
本地事务业务写成功 Outbox 未写同一 @Transactional
投递MQ 不可用重试 + 死信 + 告警
消费重复消息幂等键 / Inbox
消费处理一半崩溃可重入逻辑 + 状态机
对账长期不一致定时批对账 + 补偿
框架训练 · Seata 2.1.x

6Seata 2.1.0 落地:TC/TM/RM 与 SCA 集成要点

Seata 架构分三类角色:TC(Transaction Coordinator)维护全局会话; TM(Transaction Manager)开启/提交/回滚全局事务; RM(Resource Manager)管理分支事务与 undo。 Spring Cloud Alibaba 2023.0.x 通过 starter 自动注册 RM,TM 由 @GlobalTransactional 触发。 本节聚焦生产可运维的配置边界,而非 Hello World。

6.1 部署与 HA

  • TC 2.x 默认内置 DB 存储(store.mode=db),多节点需共享 global_table / branch_table / lock_table
  • TC 不应与业务 RM 同库——曾有机房故障 TC 表与业务表同实例,主库切换后全局事务元数据丢失。
  • Seata 2.1.0 支持 Raft(部分部署模式),生产更常见仍是 DB 模式 + 多 TC 无状态扩容。

6.2 关键配置项

# application.yml 片段(Seata 2.1.0 + SCA 2023.0.x)
seata:
  enabled: true
  application-id: order-service
  tx-service-group: default_tx_group
  service:
    vgroup-mapping:
      default_tx_group: default
    grouplist:
      default: seata-tc-1:8091,seata-tc-2:8091
  registry:
    type: nacos
    nacos:
      server-addr: ${NACOS_ADDR}
      group: SEATA_GROUP
  config:
    type: nacos
  client:
    rm:
      async-commit-buffer-limit: 10000
      report-retry-count: 5
    tm:
      commit-retry-count: 5
      rollback-retry-count: 5
      default-global-transaction-timeout: 60000

default-global-transaction-timeout 必须小于网关与调用链总 SLA,且大于最慢 RM P99 的 2 倍。 复合场景中 TC 会话 1.8 万,常与超时过长 + 重试叠加有关—— 全局事务内 RPC 应禁止多层重试(见本系列 T10)。

6.3 undo_log 运维

每个 RM 库需 undo_log 表;Seata 2.x 提供定时清理已 commit 分支的 undo。 若回滚失败,undo 堆积会导致磁盘与回滚延迟上升——监控项: undo_log 行数、SeataMetrics 中 rollback 失败率、TC branch_status 分布。 1.8.x 与 2.1.0 表结构兼容,升级时先升级 TC 再升级 RM。

已核验事实 · Seata 2.1.0 事务分组

tx-service-group 映射到 TC 集群通过 vgroup-mapping 与 registry 完成; 多环境必须隔离 Nacos namespace,避免 test TC 处理 prod 全局事务。 来源:Seata 2.1.0 部署文档与 SCA Seata 集成指南。

6.4 与 Nacos 的配置纪律

Seata 服务端 registry.conf / file.conf 在 2.x 可全部迁入 Nacos。 变更推送秒级生效,但大促期间禁止修改 global-transaction-timeout 与 store.mode。 配置中心需 prod 双人复核;TC 地址变更需同时验证所有 RM 的 grouplist 或 registry 缓存刷新。

6.5 TM 侧编程约束(框架训练)

@GlobalTransactional 应只出现在编排层(应用服务 / 领域服务入口), 不应散落在 DAO 或深层工具类——否则事务边界难以审计,且 AOP 代理可能因自调用失效。 全局事务方法内 RPC 调用须配置短于全局 timeout 的单跳超时,并与 T10 一致: 禁止在全局事务内对写操作多层重试。Seata 2.1.0 支持 rollbackFor / noRollbackFor 精细控制哪些异常触发全局回滚;业务校验失败(库存不足)应抛受检业务异常且 noRollbackFor 不应滥用——仅当确定该异常不破坏跨库 invariant 时使用。

压测时应单独打点 seata.global.tx.durationbranch.register.latency, 绘制「全局会话数 — TC CPU — 注册延迟」三维曲线,作为大促容量基线。 若 register P99 超过 200 ms(合成阈值),应优先减全局事务覆盖面,而非盲目扩容 TC—— 会话数与业务误用 AT 强相关,扩容 TC 不解热点行锁。

体系架构 · 失败窗口

7幂等、对账与悬挂治理:三种模式的公共底座

无论 TCC、AT 还是 Outbox,幂等与对账是生产安全的公共底座。 《凤凰架构》在「可靠通讯」中强调:分布式环境下「恰好一次」不可追求, 架构应设计为至少一次 + 幂等 + 对账。 复合场景中 3400 笔悬挂与 120 笔重复,根因是底座缺失而非 Seata 版本 bug。

7.1 幂等键设计

  • 全局事务:以 xid + branchId 或业务 orderId 为键。
  • TCC:Try/Confirm/Cancel 各记录 fence 表,主键 xid + action
  • Outbox 消费messageId(aggregate_id, event_type) 唯一。

7.2 对账分层

层级频率发现动作
实时秒级消费失败进 DLQ自动重试 N 次
近线5~15 min订单已支付 vs 库存已扣补偿消息 / 工单
日批T+1积分总额 vs 订单应付财务调账
Seata 专项小时global_table 长时间 committingTC 运维 / 人工 rollback
图 3 · 三种模式选型决策树(简化)
自制示意图 · 架构决策
跨库写是否可拆分? 可异步 本地消息表 / Outbox + 幂等 + 对账 必须同步 参与方与语义? 预留语义 TCC 资金/库存冻结 短+SQL Seata AT 非热点 · 2~3 RM 热点/长事务?→ 勿用 AT 无论哪条路径:Outbox/TCC/AT 均需 幂等 + 分层对账 + 大促冻结 TC 配置变更 默认走左枝(Outbox);右枝仅在监管或资损边界强制同步时启用
读图方式:从顶节点「跨库写是否可拆分」开始。 左枝(绿色)是互联网业务默认路径:可异步则 Outbox; 右枝仅在必须同步时继续:有预留语义走 TCC,短 SQL 且非热点才考虑 AT; 红色终止节点提醒热点/长事务禁用 AT。 底栏强调公共底座——选型树叶子不等于「可以不对账」。

7.3 悬挂订单治理 Runbook

  1. global_tablestatus=1( committing )超过 10 min 的 xid。
  2. 对照各 RM undo_log 与业务表,确定已 commit 与待 rollback 分支。
  3. 禁止直接 DELETE global 记录——应通过 TC 控制台或 API 触发 rollback。
  4. 业务对账脚本输出悬挂清单,补偿必须走幂等接口。

7.4 对账 SQL 设计原则(架构师视角)

对账不是 DBA 临时写的 JOIN,而是与 invariant 一一对应的可重复作业。 每条对账规则应声明:左表事实(订单已支付)、右表事实(库存已扣或 Outbox 已 SENT)、 差额动作(补发消息 / 触发 rollback / 工单)。 近线对账频率应短于业务可接受异常窗口——若产品接受积分 15 min 延迟,对账周期应 ≤ 5 min, 留出补偿执行时间。日批对账面向财务 invariant,与近线技术对账互补,不可互相替代。 Seata 专项对账应纳入同一平台,避免 TC 悬挂仅由中间件团队知晓而业务不知情。

验证 · 决策矩阵

8场景适配矩阵与 TCC / AT / Outbox 组合

单模式文档看多了容易「全局 @GlobalTransactional 一把梭」。 架构师需要的是场景 → 模式组合的决策矩阵,而非清单式堆叠。 下表基于《凤凰架构》一致性与《Seata 2.1.0 模式说明》整理,数字边界为合成建议。

8.1 场景适配矩阵

场景首选备选避免备注
大促下单 + 扣库存 + 积分Outbox + 幂等消费TCC 库存 Try全局 AT 热点 SKU与 T12 秒杀异步一致
跨主体资金划转TCC 冻结/Confirm隔离子域 XA纯 Outbox 无对账监管可能强制同步
内部 Admin 批量调账本地事务 + 日批对账Saga 编排全局 AT 长事务低 QPS 可接受
支付回调通知商户Outbox + 最大努力通知TCC外部系统不可控
跨单元多活写(T15)单元内本地 + SagaTCC 少步跨单元 2PC号段路由优先

8.2 模式对比四维表

维度TCCSeata AT本地消息表
一致性强弱(正常路径)同步,预留态可见同步,接近强一致最终一致
开发成本
运维对象TC + fence 表TC + undo_logOutbox + MQ + 投递器
峰值吞吐较好热点受限核心路径短
故障典型态空回/悬挂二阶段超时悬挂消息堆积 / 重复消费

组合得当

  • 核心写 Outbox,仅资金子域 TCC
  • 全局 AT 限定 2 RM、非热点、短超时
  • 消费端 Inbox 幂等 + 分层对账
  • 大促前冻结 Seata/Nacos 配置

组合失当

  • 全链路 @GlobalTransactional
  • AT + 热点 SKU + 全局事务内重试
  • Outbox 无消费幂等
  • 补偿与 Seata 回滚并发无锁

8.3 与《凤凰架构》章节映射

书中「分布式的代价」「可靠事件」「服务容错」三章与本篇三模式对应: 代价章讲为何不能默认 XA;可靠事件讲 Outbox;容错讲补偿与幂等。 架构师评审时应要求方案说清异常窗口长度、对账频率、TC 容量预算, 而非笼统「用 Seata 保证一致性」。与 T10 衔接:全局事务内禁止重试风暴; 与 T12 衔接:秒杀成交与库存确认应 Outbox 异步,而非同步 AT。

8.4 评审会三问(沉淀为团队 RFC)

建议在架构评审模板固定三问,避免每项目重复争论: (1)跨库写能否合并为单库或异步?——若不能,写清 invariant 与窗口; (2)同步路径是否含热点实体?——若有,默认禁止 AT; (3)对账与补偿谁负责、频率多少、幂等键是什么?——无答案则方案不完整。 三问耗时不足 15 分钟,却可过滤多数「全局事务一把梭」方案。 与 T14 微服务治理衔接:拆分越细,三问越应严格——边界越多,协调成本指数上升。

8.5 典型踩坑案例速查(合成摘要)

案例 A(全链路 AT):六个微服务全部 @GlobalTransactional,大促 TC 会话 2 万,register 超时。 规避:仅 2 RM 短事务 AT,其余 Outbox。案例 B(Outbox 无幂等):MQ 重复投递导致积分双倍。 规避:Inbox 唯一键 + 消费状态机。案例 C(TCC 空回):Cancel 未判 Try 是否到达,库存被误释。 规避:fence 表 + Try 前查状态。三案例共同规律:模式本身不错,边界画错

问题现场 · 排障

9War Room:悬挂订单、undo 堆积与紧急降级

本篇 15% 现场权重集中在本章:当 TC Dashboard 仍显示「正常」、 而业务对账已发现悬挂时,排障顺序与 T10 雪崩不同—— 重点是全局会话状态、undo 队列、补偿并发三线并行。

9.1 三类表象与根因

表象优先查常见根因
订单已支付,库存未扣Outbox SENT / 消费 DLQ消费失败或全局 AT 二阶段 rollback 未完成
积分重复发放消费幂等表 / fence补偿与 Confirm 并发无唯一键
TC CPU 高、register 慢global_table 会话数峰值全局事务 + 超时过长
undo_log 暴涨rollback 失败率大字段更新 / 从库延迟

9.2 紧急降级阶梯

  1. L0:关闭非核心链路的 @GlobalTransactional(Feature Flag)。
  2. L1:热点 SKU 改本地预扣 + Outbox,绕过 AT。
  3. L2:暂停积分等弱一致步骤,仅保障成交与库存。
  4. L3:TC 只读模式 + 人工对账(极端)。

复合场景在 T+25 min 执行的是 L1+L2:并非放弃一致性,而是收缩同步边界。 降级后 24 h 内必须完成悬挂清账与 Postmortem,写入 TC 容量与 Outbox 投递指标基线。

常见陷阱 · 手工修 global_table

运维若直接 UPDATE global_table 状态为 committed,而 RM 侧 undo 未删, 后续异步回滚可能错误撤销已修复数据——形成「二次悬挂」。 正确路径:Seata Server API / 控制台触发 rollback,或按官方运维手册处理异常 session。 任何手工 SQL 修 TC 元数据需架构师双人复核并留审计。

9.3 Postmortem 必填字段

每次事务类事故复盘应写入: 峰值 global session 数二阶段超时占比悬挂订单笔数与资损估算Outbox 投递滞后 P99。 四字段缺失无法反馈到「是否误用 AT」的架构决策,只会重复「下次优化 TC」。

9.4 War Room 话术:避免错误共识

现场常见三句误判需架构师主动纠正: 「TC 监控正常说明事务没问题」—— TC 正常只说明协调者活着,不说明 branch 已一致; 「先补偿用户再查根因」—— 无幂等补偿扩大资损; 「临时把 timeout 调大撑过去」—— 会话堆积延迟爆炸,Undo 表膨胀。 正确话术是:先冻结全局入口、导出悬挂清单、并行查 TC 与 Outbox 两通道。 与 T10 雪崩排障不同,事务事故CPU 可能不高,因为线程阻塞在 RPC 与二阶段等待—— 勿因 CPU 低而否定事务层异常。合成场景里 order-service CPU 38%、悬挂 3400 笔,即属此类。

与系列 T07 衔接:若库存扣减通过 Kafka 消费,需明确消息语义与 Seata 分支的边界—— 同一业务步骤不能既走 AT 又走 MQ 且无对账,否则出现「双通道写」。

沉淀 · 检查表

10分布式事务选型检查表、总结与延伸阅读

10.1 上线与大促前检查表

检查项验收标准责任人
强一致必要性论证 文档化异常窗口与资损边界;产品/架构签字 架构
默认路径 可异步步骤走 Outbox;非必要无 @GlobalTransactional 架构 / 开发
AT 使用边界 ≤2~3 RM、非热点、timeout < 链 SLA、无内层 REQUIRES_NEW 开发
TCC 三接口 空回/悬挂/幂等测试用例通过;fence 表就绪 开发 / QA
Outbox + Inbox 生产消费成对幂等;索引与 CDC 压测过 开发 / SRE
对账分层 近线 + 日批脚本;DLQ 告警 业务 / SRE
TC 容量 峰值 global session 压测;TC 独立库 SRE
配置纪律 Nacos 环境隔离;大促冻结 Seata 配置 SRE
降级预案 L0~L2 Feature Flag 演练通过 架构
与 T10/T12 衔接 全局事务内无重试风暴;秒杀异步扣库存 架构

10.2 三句话总结

第一,「必须强一致」是需求翻译问题——默认应本地消息表 + 幂等 + 对账,而非默认全局 XA。 第二,Seata AT 适合短、非热点、少参与方同步写;TCC 适合可定义预留语义的资损路径; 二者都需要 TC 容量与 undo/fence 运维,不能替代对账。 第三,复合事故的共同特征是模式误选 + 峰值放大 + 补偿缺幂等—— 选型树与检查表的价值在于把 war room 经验前移到架构评审。

10.3 延伸阅读

  1. BOOK周志明:《凤凰架构》—— 分布式的代价、可靠事件、服务容错相关章节
  2. DOCApache Seata 2.1.0 官方文档 —— AT / TCC 模式、部署与运维
  3. DOCSpring Cloud Alibaba 2023.0.x · Seata 集成
  4. PAPERGray & Reuter:《Transaction Processing: Concepts and Techniques》—— 2PC 理论基础
  5. SERIES本系列 T10 可靠性 · T12 秒杀 · T15 多活 —— 事务边界与异步化衔接

下一篇 T14 微服务治理演进将进一步讨论拆分后治理阶段与事务边界的关系; 本篇强调的「先缩边界、再选协调」应作为拆分评审的默认前置问题。

选型没有银弹:Seata 2.1.0 降低 AT/TCC 接入成本,但不替业务定义 invariant; 本地消息表降低峰值 TC 压力,但把复杂度转移到投递与对账。 资深架构师的产出不是「选了哪个框架」,而是写清异常窗口、验收对账、冻结变更纪律的可执行文档—— 这才是在 composite 场景里比框架版本更长期有效的资产。 评审时请携带本文第 8 章决策树与第 10 章检查表,作为分布式事务选型的最小交付物。