想象你在筹备一场大型演唱会。总导演需要在舞台上同时协调灯光组、音响组、道具组——任何一个小组掉链子,整场演出就可能翻车。但在现实里,各小组在后台各自忙碌,谁也不知道别人的进度。这就是微服务世界里每天发生的事:一个下单操作要同时扣库存、减余额、加积分,三个服务分布在不同的机器上,任何一个失败,钱货就对不上了。 Seata 就是这场演唱会的“总导演”。它把自己拆成三个角色:TM(执行导演)负责宣布“演唱会开始/结束”,TC(总控台)负责掌握所有小组的状态,RM(各小组组长)负责干完自己那摊活并如实汇报。它还提供了四种编排模式:AT 是“全程录像、出错就倒放”,TCC 是“先预约、再确认、不行就取消”,SAGA 是“一个接一个演出、出问题就倒着取消前面的节目”,XA 则是数据库原生的“双门锁死”方案。 这篇文会用一场演唱会的筹备与演出来讲透 Seata 的协作机制,让你从零看懂分布式事务到底在解决什么问题。

岗位关联

学习 Seata 能让你在微服务跨库事务场景下给出可落地的解决方案,是分布式系统设计能力的硬通货。

目录
  1. 可视化演示
  2. 三大角色
  3. AT模式
  4. TCC模式
  5. SAGA模式
  6. 事务流程
  7. 应用实践
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

展示Seata分布式事务TC/TM/RM协作架构、全局事务流程与AT/TCC/SAGA/XA四种模式对比。

本节配有交互动效(进入视口后自动加载,约 3KB)。

三大角色协作:TC/TM/RM架构 TC事务协调者居中,协调TM与RM间的事务 TM发起全局事务,RM管理本地资源分支 TC↔TM↔RM 三方协调 TC独立部署,TM/RM嵌入应用与TC通信

三大角色

想象演唱会的后台:执行导演 TM 站在舞台侧幕,手里攥着节目单,决定“整场演唱会什么时候开始、什么时候结束”。总控台 TC 坐在音控室里,面前的屏幕上实时显示后台每一个小组的状态,哪盏灯没亮、哪支麦克风没通,他全知道。而灯光组、音响组、道具组的组长 RM 们,则带着各自的团队完成自己负责的那部分工作,完成后向总控台大声汇报一句“我们组 OK 了”。

Seata 把这套分工做成了软件架构的三个角色。TM(Transaction Manager)事务管理器负责全局事务的发起、提交和回滚决策,相当于执行导演;TC(Transaction Coordinator)事务协调者负责维护全局事务的运行状态,协调每个分支的注册与最终结果,相当于总控台;RM(Resource Manager)资源管理者则管理每个分支事务里的本地资源,比如数据库行记录、表锁等,相当于各组组长。

在协作机制上,三者的关系可以总结成一句话:TM 只管下命令,TC 负责传话和汇总,RM 负责干活。TM 向 TC 发起一个全局事务时,TC 会生成一个全局事务 ID(XID);RM 执行本地操作时会用这个 XID 向 TC 注册分支事务;最后 TM 根据业务是否全部成功,向 TC 下达提交或回滚的指令。

一场演唱会的成败,取决于各组组长能否及时如实向总控台汇报,总控台能否准确判断整体状态。Seata 的三大角色也是如此:职责清晰、各司其职,才能在跨服务的复杂环境下不乱套。

GlobalTx=⟨TM∣TC∣RM1,RM2,…,RMn⟩\text{GlobalTx} = \langle \text{TM} \mid \text{TC} \mid \text{RM}_1, \text{RM}_2, \ldots, \text{RM}_n \rangle
公式表示一场全局事务由 TM 拍板、TC 统筹、多个 RM 分工完成,类似演唱会由执行导演、总控台和各组组长共同完成。
  1. 全局事务的构成定义一个全局事务 GlobalTx 是一个三元组结构,起始是 TM,中间是 TC,后面跟着若干个 RM 分支。
    GlobalTx=⟨TM∣TC∣RM1,RM2,…,RMn⟩\text{GlobalTx} = \langle \text{TM} \mid \text{TC} \mid \text{RM}_1, \text{RM}_2, \ldots, \text{RM}_n \rangle
  2. 竖线分隔的语义这里的竖线不是数学运算符,而是表示“协作关系”的分隔符,读作“TM 委托 TC,TC 指挥各 RM”。
    ∣=协作关系\mid = \text{协作关系}
  3. 下标 n 的含义RM 的下标从 1 到 n,表示有 n 个参与本次事务的资源分支,n 越大说明事务跨越的服务越多。
    n=#{branch}≥1n = \#\{\text{branch}\} \geq 1

AT模式

现在演出正式开始了。灯光组组长在按下灯光按钮之前,先让助理对着控制台拍了一张“当前灯光亮度”的照片存档。如果之后演出出了意外需要恢复,他们照着照片把亮度调回去就行——这就是 AT 模式最核心的思路:给业务数据拍快照,出错就还原。

AT 模式分为两个阶段。一阶段:RM 在自己的本地数据库里执行业务 SQL,同时把修改前后的数据镜像(before image 和 after image)写入一张名为 undo_log 的日志表。执行完后,本地事务直接提交,释放本地数据库的行锁。二阶段:TC 汇总所有 RM 的上报结果,如果全部都成功,则通知各 RM 删除 undo_log 中的快照,完成提交;如果任何一个分支失败,TC 通知所有 RM 依据 undo_log 中的 before image 逆向生成回滚 SQL,把数据恢复原状。

这里的关键是全局锁。在分布式场景下,两个全局事务可能操纵同一行数据。本地事务提交后行锁已释放,为了让另一个事务的修改不会破坏当前事务的一致性,TC 会为涉及的行分配一把逻辑上的“全局锁”。任何分支在修改数据前,必须先向 TC 申请到这把锁,而全局锁要等到二阶段结束(提交或回滚完成)才释放。

演出结束后,助理把存档照片全部删掉,控制台上的一切恢复如常。AT 模式对业务代码几乎零侵入——你不用写任何补偿逻辑,数据库行锁也只短暂占用,适合大多数对一致性要求较高的业务场景。

Commit=⋀i=1nLocalCommiti\text{Commit} = \bigwedge_{i=1}^{n} \text{LocalCommit}_i
全局提交成功等于所有分支的本地提交都成功做“与”运算。只要有一项为假,整个事务就必须回滚。
  1. 对所有分支取与\(\bigwedge_{i=1}^{n}\) 表示从第 1 个分支到第 n 个分支逐个做逻辑与,全部为真结果才为真。
    ⋀i=1n\bigwedge_{i=1}^{n}
  2. 每个分支的本地提交\(\text{LocalCommit}_i\) 是一个布尔值,表示第 i 个 RM 的本地事务是否提交成功。
    LocalCommiti∈{True,False}\text{LocalCommit}_i \in \{\text{True}, \text{False}\}
  3. 最终全局提交结论整个表达式的结果 Commit 为真,当且仅当所有的 LocalCommit 都为真,这体现了“一票否决”语义。
    Commit=True  ⟺  ∀i,LocalCommiti=True\text{Commit} = \text{True} \iff \forall i, \text{LocalCommit}_i = \text{True}

TCC模式

如果舞台道具非常昂贵,灯光组不会直接下单买下,而是先给供应商打电话“预留一套设备”,供应商记下预约单,但不发货。等演出方案最终敲定,再打第二个电话“确认发货”;如果演出取消,就打第三个电话“取消预约”,供应商撤销预约单。这就是 TCC 模式的业务逻辑:Try 预占资源,Confirm 确认提交,Cancel 取消还回去。

TCC 的三个阶段分别对应三个业务方法。Try 阶段:尝试预占资源,比如冻结账户里的余额、锁定库存数量,此时数据并未真正变化。Confirm 阶段:如果 Try 阶段所有分支都成功,TC 通知各 RM 执行 Confirm,把预占真正变成扣款、扣减库存。Cancel 阶段:如果 Try 阶段有分支失败,TC 通知各 RM 执行 Cancel,把 Try 预占的资源释放回原处。

TCC 的代价是业务侵入性很强。每个参与事务的服务都要自己写 Try、Confirm、Cancel 三个方法,而且要处理幂等、空回滚和悬挂等边界问题。比如网络超时导致 Confirm 重复下发,业务代码必须能识别重复请求;再比如 Try 还没执行完,Cancel 就先到了,业务代码要能判断并跳过未发生的操作。这三个问题处理不好,TCC 比 AT 更容易出事故。

TCC 的优点是不依赖数据库的全局锁,性能比 AT 更高,适用于对一致性要求严苛、愿意为每段业务付出编码成本的场景。它像一场精心排练过的彩排——每一次试灯、试音都留下记录,最终要么正式上演,要么原样撤场。

Confirm(x)=f(Try(x)),Cancel(x)=g(Try(x))\text{Confirm}(x) = f(\text{Try}(x)), \quad \text{Cancel}(x) = g(\text{Try}(x))
Try 的结果会同时决定 Confirm 和 Cancel 两个后继动作,就像预约单既可能变成发货单,也可能变成取消单。
  1. Try 的结果作为输入\(\text{Try}(x)\) 是对业务数据 x 做预占操作后的结果状态,x 表示订单号或账户 ID。
    Try(x)=预留 x\text{Try}(x) = \text{预留 } x
  2. Confirm 是 Try 的正向确认\(f(\text{Try}(x))\) 表示在 Try 成功的基础上执行确认函数 f,把预留变成实际扣减。
    Confirm(x)=f(Try(x))\text{Confirm}(x) = f(\text{Try}(x))
  3. Cancel 是 Try 的反向补偿\(g(\text{Try}(x))\) 表示在 Try 成功但整体失败时执行取消函数 g,把预留归还。
    Cancel(x)=g(Try(x))\text{Cancel}(x) = g(\text{Try}(x))

SAGA模式

有时候大型演唱会要演一整个系列:明天是爵士专场,后天是摇滚专场,大后天是民谣专场。每个专场之间是串联关系,由总控台按照节目单一个一个往下走。如果中间某个专场临时取消了,总控台不会把已经演完的专场再演一遍,而是倒着把前面那些专场的场次依次“退款”给观众。这就是 SAGA 模式:用状态机编排,按顺序执行,失败则反向补偿。

SAGA 模式把一个长事务拆分成多个本地事务,每个本地事务对应一个状态节点。正向补偿就是按照编排顺序依次执行各个子事务,每个子事务执行完就提交并记录执行状态;反向补偿是当某个子事务失败时,从不成功的节点开始,逆序执行前面所有子事务的补偿动作。比如订机票、订酒店、订车三个步骤,酒店失败了,就依次取消机票和之前的其他预订。

SAGA 的核心是状态机编排,即每个全局事务都有一个状态机,记录了当前执行到第几个子事务、每个子事务是成功还是失败。与 TCC 相比,SAGA 不需要 Try 阶段,业务代码里不需要写预占资源的逻辑,因此无侵入性更好。但 SAGA 有一个先天弱点:子事务提交后的中间状态对其他事务可见,如果中间状态被读走了而后续又回滚,就可能产生脏读。

SAGA 适合长事务、多服务、对实时一致性要求不苛刻的场景。它像一场多场次的系列演出,总控台靠清晰的节目单和退票规则,把复杂的流程打理得井井有条。

Rollback=CN∘CN−1∘⋯∘C1\text{Rollback} = C_N \circ C_{N-1} \circ \cdots \circ C_1
回滚路径是第 N 个子事务的补偿操作复合上前一个子事务的补偿操作,按倒序一直复合到第 1 个子事务。
  1. 补偿操作组合\(C_i\) 表示第 i 个子事务的补偿函数(比如退票、退款),\(\circ\) 是函数复合符号,先执行右边的函数。
    Ci=补偿第 i 步C_i = \text{补偿第 } i \text{ 步}
  2. 倒序执行的顺序从 \(C_N\) 开始到 \(C_1\) 结束,下标从大到小,表示补偿必须严格逆着正向顺序执行。
    N→N−1→⋯→1N \to N-1 \to \cdots \to 1
  3. 整体回滚结果整个复合表达式 Rollback 表示“从最后一步开始,一路退回到第一步”,任何一步的补偿失败都会导致回滚链路中断。
    Rollback=CN∘CN−1∘⋯∘C1\text{Rollback} = C_N \circ C_{N-1} \circ \cdots \circ C_1

事务流程

现在把整场演唱会从头到尾捋一遍。执行导演 TM 在演出开始前对总控台 TC 说:“启动全局档期,编号 TX-001。”TC 在调度表上登记这个档期,生成全局事务 ID,并把这个 ID 告诉所有工作小组——这是全局开启。

接着,各组组长 RM 领到任务后,开始干活。每完成一项本地任务,RM 就拿着全局事务 ID 向 TC 报告“我是灯光组,我的分支业务 id=branch-1,状态 OK”,TC 在调度表里把该分支记为“已注册”——这是分支注册。所有活都干完后,RM 把“成功/失败”的结果上报给 TC。

最后是决策阶段。如果所有 RM 都报告成功,TM 就对 TC 下达“全局提交”指令,TC 通知每个 RM 把快照清理掉,锁释放,一场演出完美收官;只要有一个 RM 报告失败,TM 就下达“全局回滚”指令,TC 通知每个 RM 用 undo_log 中的快照恢复数据,再释放全局锁,舞台重新回到演出前的状态。

从筹备到收工,每一个环节都环环相扣。理解了这条流程,你就能清楚地看懂 Seata 在报错日志里到底发生在哪一步——是全局没开启、分支没注册,还是回滚指令没送达。

Flow=Begin→Register→Exec→Commit / Rollback\text{Flow} = \text{Begin} \to \text{Register} \to \text{Exec} \to \text{Commit / Rollback}
一次完整事务可以看成四个有序环节:开启、注册、执行、终局决策,每个环节由不同角色推动。
  1. 开启环节Begin 表示 TM 向 TC 发起全局事务请求,TC 生成全局事务 ID 并保存,此时事务处于进行中状态。
    TM→BeginTC\text{TM} \xrightarrow{\text{Begin}} \text{TC}
  2. 注册环节Register 表示各 RM 带着 XID 向 TC 注册分支事务,TC 记录每个分支的标识和状态。
    RMi→RegisterTC\text{RM}_i \xrightarrow{\text{Register}} \text{TC}
  3. 执行环节Exec 表示各 RM 在本地执行 SQL,并保存执行结果快照,完成后上报执行结果。
    RMi→ExecDBi\text{RM}_i \xrightarrow{\text{Exec}} \text{DB}_i
  4. 终局决策Commit/Rollback 表示 TM 汇总所有结果后,向 TC 下达全局提交或回滚指令,TC 再逐一下发到各 RM。
    TC→Commit/RollbackRMi\text{TC} \xrightarrow{\text{Commit/Rollback}} \text{RM}_i

应用实践

再完美的编排方案,落到真实舞台上都会遇到各种意外。Seata 在实践中最重要的第一步是配置部署:TC 需要作为一个独立服务部署(TCServer),客户端通过配置文件指定 TC 的地址;数据库里要建好 undo_log 表并初始化 AT 模式所需的表结构;file.conf 和 registry.conf 两个配置文件决定了事务日志存储方式和注册中心类型。

第二步是高并发优化。AT 模式虽然业务入侵小,但全局锁会让跨行更新变成串行,高并发下吞吐量会明显下滑。常用的优化手段包括:尽量缩小事务范围,把无关读操作移出全局事务;用 TCC 替换热点账户的 AT 模式;对热点数据做分片,避免所有请求都锁同一行。

第三步是踩坑经验。最常见的坑有:undo_log 表和数据表不在同一数据库导致回滚失败;服务重启后 TC 出现孤儿事务;超时时间设置过短导致长事务被误杀;以及回滚 SQL 生成失败(比如使用了不支持的类型或函数)。这些坑大多要靠配置检查、日志分析和压测来提前规避。最终目标只有一个——在高并发微服务场景下保证数据的最终一致性:允许短暂的不一致,但最终要对账收货。

实践出真知。演唱会办得越多,对现场的突发状况就越有预判。Seata 的落地同样如此:配置、优化、踩坑三轮打磨之后,分布式事务才能在高并发环境下稳稳兜住数据的底线。

---

Psuccess=∏i=1npiP_{\text{success}} = \prod_{i=1}^{n} p_i
全局成功率是所有分支成功率的乘积。分支越多,整体成功率越低,因此减少参与事务的分支数本身就是一种优化。
  1. 各分支成功率\(p_i\) 表示第 i 个分支事务的执行成功率,取值范围是 0 到 1。
    0≤pi≤10 \leq p_i \leq 1
  2. 累乘运算\(\prod_{i=1}^{n}\) 表示把 n 个分支的成功率连乘,只要有一个分支成功率小于 1,整体就会明显下降。
    ∏i=1n=p1×p2×⋯×pn\prod_{i=1}^{n} = p_1 \times p_2 \times \cdots \times p_n
  3. 全局成功率公式\(P_{\text{success}}\) 是全局事务的成功概率,这个公式解释了为什么“事务越大越容易失败”。
    Psuccess=∏i=1npiP_{\text{success}} = \prod_{i=1}^{n} p_i

AI 应用场景

微服务订单支付跨库事务

阶段:数据一致性保障 / 业务链路编排

解决的问题:在电商下单链路中,订单服务、库存服务、账户服务各自独立部署、独立数据库。传统写法里,库存扣了但支付失败时,只能依赖于人工对账或定时任务补救,钱货不一致问题频发。Seata AT 模式用 undo_log 快照自动完成回滚,让跨库操作像本地事务一样安全。

具体案例:某电商平台在用户下单时,订单服务插入订单记录,库存服务扣减商品库存,账户服务冻结用户余额。三个操作通过 Seata 全局事务 XID 关联,任一环节抛异常,TC 自动触发所有分支回滚,用户看到的始终是“要么全部成功,要么全部失败”。

营销系统的TCC积分发放

阶段:资源预占与补偿执行

解决的问题:大促活动中,积分发放量动辄千万级,若用 AT 模式对积分账户加全局锁,会拖垮整个账户服务的吞吐。TCC 模式把“扣减积分”拆成 Try(冻结积分)、Confirm(确认扣减)、Cancel(解冻积分),把锁粒度缩小到业务代码层面,性能明显提升。

具体案例:某营销平台在用户完成分享任务后发放 100 积分。Try 阶段冻结 100 积分额度,Confirm 阶段真正入账;若同一批次内有用户账号异常,Cancel 阶段统一解冻,保证总积分池不超发。

长流程SAGA出行预订编排

阶段:长事务编排与反向补偿

解决的问题:OTA 平台一次出行预订要依次调用机票、酒店、租车三个外部系统,每个系统事务时间很长,AT 模式的全局锁无法长期持有。SAGA 模式用状态机编排三个子系统,任一环节失败就逆序取消前面的预订,符合外部系统“预订即可取消”的商业模式。

具体案例:用户预订“机票+酒店+租车”打包产品。系统先出机票,再锁酒店,最后订租车;若租车系统返回无车,SAGA 自动执行“退掉酒店、再退机票”的倒序补偿,全程无需人工介入。

Python 演示

python53 行
class ATModeSimulator:    """极简版 AT 模式:模拟 Seata 的一阶段执行 + 二阶段提交/回滚"""     def __init__(self):        # 两张"账本"(全局数据表)        self.data = {"account_a": 1000.0, "account_b": 1000.0}        # 快照日志:记录每个分支事务修改前的镜像        self.undo_log = []     def branch_exec(self, target, delta):        """一阶段:执行本地 SQL,同时把旧值存入 undo_log 快照"""        if target not in self.data:            raise KeyError("目标账户不存在: %s" % target)        old_value = self.data[target]        new_value = old_value + delta        if new_value < 0:            raise ValueError("余额不足,写入失败")        self.data[target] = new_value        self.undo_log.append((target, old_value))        print("[RM] 更新 %s: %.2f -> %.2f,已记录快照" % (target, old_value, new_value))        return True     def commit(self):        """二阶段提交:删除快照,释放行锁"""        self.undo_log.clear()        print("[TC] 全局提交成功,快照已清理")     def rollback(self):        """二阶段回滚:按反顺序恢复旧值"""        while self.undo_log:            target, old_value = self.undo_log.pop()            self.data[target] = old_value            print("[TC] 回滚 %s 至 %.2f" % (target, old_value))        print("[TC] 全局回滚完成")  def main():    at = ATModeSimulator()    print("初始账本:", at.data)    try:        at.branch_exec("account_a", -300.0)   # A 扣款        at.branch_exec("account_b", 300.0)    # B 入账        # 模拟第二步失败:B 入账失败,触发全局回滚        at.branch_exec("account_b", -99999.0)        at.commit()  # 全部成功才提交    except ValueError as exc:        print("[TM] 捕获异常:", exc)        at.rollback()  # 回滚    print("最终账本:", at.data)  if __name__ == "__main__":    main()
目的:用 Python 模拟 Seata AT 模式的一阶段执行与二阶段提交/回滚,直观展示“快照 + 回滚”机制。
  1. class ATModeSimulator:定义 AT 模式模拟器,对应 Seata 中客户端侧对分支事务的管理能力。
  2. self.undo_log = []快照日志,模拟 Seata 的 undo_log 表,记录每个分支修改前的旧值。
  3. old_value = self.data[target]在执行 SQL 修改前先读旧值,这是生成回滚 SQL 的基础。
  4. self.undo_log.append((target, old_value))把旧值追加到快照栈,模拟 AT 模式的 before image 记录过程。
  5. def rollback(self):二阶段回滚:从栈尾开始 pop,逆序遍历快照,正好对应 Seata 倒序生成回滚 SQL 的策略。
  6. while self.undo_log:循环直到快照栈为空,模拟 TC 通知 RM 逐个分支执行回滚。

常见误区

误区AT 模式对业务零侵入,用起来最省事,那我把所有分布式事务都换成 AT 模式不就好了?它肯定是四种模式里最好的。

正解AT 模式虽然入侵小,但它依赖全局锁和 undo_log,在高并发写热点时会锁冲突严重,长事务会长时间占用全局锁导致吞吐量暴跌。TCC 和 SAGA 在性能和灵活性上有各自优势,选型要看业务场景:短事务选 AT,热点账户选 TCC,长流程选 SAGA。

为何常见:因为 AT 模式“零改造”的卖点太深入人心,初学者容易忽略它在高并发和长事务场景下的代价,一叶障目。

误区AT 模式不就是数据库两阶段提交的变种吗?都是先准备再提交,本质都是 XA 协议。

正解传统 2PC 的一阶段要锁定资源并阻塞等待协调者指令,期间其它事务访问同一资源会被阻塞。AT 模式一阶段本地事务直接提交、提前释放本地锁,二阶段通过 undo_log 做异步补偿,不需要同步阻塞。虽然都需要协调者,但 AT 的“先提交后补偿”在性能和可用性上远优于传统 2PC。

为何常见:AT 模式名字里有“两阶段”,且也是协调者统一决策,表面流程相似,容易让人误以为底层实现相同。

误区SAGA 子事务提交后就释放资源,不持有全局锁,这不就是最完美的分布式事务方案?以后都用 SAGA。

正解SAGA 确实不持锁,但它也放弃了隔离性——子事务提交后的中间结果对其它事务可见,一旦回滚就可能出现脏读。而且反向补偿要业务自己实现,补偿失败还得处理。SAGA 的优势是“长事务编排”,代价是“一致性弱”,不能一句“更快更好”就下定论。

为何常见:人们常把“性能好”和“方案优”划等号,忽略了分布式事务里一致性和性能往往此消彼长。

误区全局事务里某个 RM 报错了,TC 马上就会通知所有分支做回滚,所以数据永远是安全的。

正解Seata 的正常流程确实是“分支失败 → TM 收到失败 → 下发回滚”。但网络分区抖动时,回滚指令可能延迟或丢失;TM 宕机时,XID 状态可能残留;超时时间设置不当还可能让长事务被误杀。生产环境必须依赖 TC 的事务超时守护机制、消息重试和幂等处理,并在回滚失败后人工介入或对账补偿。

为何常见:入门者通常只学过理想流程,不理解分布式环境下“消息可能丢失、进程可能宕机”的常态,把理想流程当成了全部。

误区TCC 不就是写三个方法按顺序调一遍吗?Try 失败就调 Cancel,全成功就调 Confirm,这有什么难的。

正解TCC 的难点在三个业务方法之间的状态一致性:Confirm 和 Cancel 必须保证幂等(重复调用不产生副作用);Try 还没执行完,Cancel 先到的“空回滚”要能正确处理;Try 成功但 Confirm 未到达的“悬挂”资源要能回收。这些边界问题处理不好,TCC 的准确性还不如 AT 模式。

为何常见:TCC 的三个方法命名直白,给的直觉就是“依次调用”,让人低估了分布式场景下乱序、重复、超时带来的复杂度。

面试题

Q1. Seata 中负责全局事务发起和最终决策的角色是?

单选题 · easy

  • A.RM 负责本地事务执行和上报
  • B.TM 负责发起提交回滚决策← 正确答案
  • C.TC 只负责协调分支注册
  • D.各 RM 自行决定是否提交

答案:B。TM(事务管理器)负责全局事务的开启、提交和回滚决策,相当于执行导演;TC 是协调者,RM 是资源执行者。A、C 描述的是 TC 和 RM 的职责,D 完全错误,因为分支不能自行决定提交。

Q2. AT 模式二阶段回滚的数据依据是什么?

单选题 · easy

  • A.业务自己写的补偿方法
  • B.undo_log 中的 before image← 正确答案
  • C.数据库的重做日志 redo log
  • D.应用服务器的内存快照

答案:B。AT 模式一阶段把修改前的旧值写入 undo_log,二阶段回滚时根据 before image 逆向生成回滚 SQL 恢复数据。A 是 TCC 的做法,C 是数据库崩溃恢复用的,D 不涉及持久化数据。

Q3. TCC 模式下 Try 阶段失败后,系统应执行什么?

单选题 · mid

  • A.直接放弃,不做任何操作
  • B.对所有分支执行 Confirm
  • C.对已成功的 Try 执行 Cancel← 正确答案
  • D.不执行任何补偿,等超时

答案:C。TCC 中只要有一个 Try 失败,TC 就要对已经执行成功的 Try 调用 Cancel,释放预占资源。B 是全部成功才做,A 和 D 会造成资源泄漏,属于错误处理。

Q4. SAGA 模式相比 AT 模式最大的不同是什么?

单选题 · mid

  • A.SAGA 使用全局锁保证隔离
  • B.SAGA 子事务提交后中间状态可见← 正确答案
  • C.SAGA 不需要任何协调角色
  • D.SAGA 只能用于单数据库

答案:B。SAGA 没有全局锁,子事务提交后数据立即可见,所以不具备隔离性,可能产生脏读。A 说反了,C 错误因为 SAGA 也需要协调器编排,D 错误因为 SAGA 用于跨服务长事务。

Q5. Seata 中全局锁的作用是什么?

单选题 · mid

  • A.锁住整个数据库表
  • B.防止两个全局事务并发修改同一行← 正确答案
  • C.代替数据库的行锁
  • D.加快查询速度

答案:B。全局锁是 TC 维护的逻辑锁,防止两个全局事务同时修改同一行数据造成数据不一致。它不锁表(A 错),不能代替数据库行锁(C 错),与查询性能无关(D 错)。

Q6. 一笔全局事务分支成功率为 0.9,共有 4 个分支,全局成功率约为?

单选题 · hard

  • A.0.90
  • B.0.81
  • C.0.66← 正确答案
  • D.0.36

答案:C。全局成功率等于各分支成功率连乘,0.9^4 = 0.6561,约 0.66。这个公式说明分支越多整体成功率越低,因此设计上要尽量减少分布式事务的分支数量。

Q7. AT 模式一阶段本地事务提交后立即释放了什么?

单选题 · hard

  • A.全局锁和本地锁都释放
  • B.本地行锁,但全局锁保留← 正确答案
  • C.全局锁,但本地行锁保留
  • D.所有锁都保留到二阶段

答案:B。AT 模式一阶段本地事务直接提交、释放本地数据库行锁,但 TC 维护的全局锁要保留到二阶段结束,防止其它事务读到中间状态。A 说全释放会破坏一致性,C、D 与设计矛盾。

Q8. Seata 的三大角色包含哪些?

多选题 · easy

  • A.TM 事务管理器← 正确答案
  • B.TC 事务协调者← 正确答案
  • C.RM 资源管理者← 正确答案
  • D.DB 数据库连接池管理

答案:ABC。Seata 的三大角色是 TM(发起决策)、TC(协调调度)、RM(资源执行)。数据库连接池是数据访问层的组件,不参与 Seata 的角色划分,所以 D 不选。

Q9. 以下哪些场景适合使用 SAGA 模式?

多选题 · mid

  • A.跨多服务的长流程业务编排← 正确答案
  • B.每个子服务都有补偿方法可用← 正确答案
  • C.要求强隔离、禁止中间态可见
  • D.预占资源后需显式确认的业务

答案:AB。SAGA 适合长事务串联且允许中间态可见的场景,子服务必须提供补偿方法。C 要求强隔离,SAGA 做不到;D 描述的是 TCC 的 Try/Confirm 特性,不是 SAGA 的核心特征。

Q10. AT 模式在哪些情况下不适合使用?

多选题 · hard

  • A.高并发热点账户频繁被更新← 正确答案
  • B.长事务持有全局锁导致大量阻塞← 正确答案
  • C.单库内普通的本地事务
  • D.业务需要自定义资源预占流程

答案:AB。AT 模式对热点数据的全局锁竞争激烈,长事务会长时间占用全局锁,都会导致吞吐降低,不适合。C 是本地事务,压根不需要分布式事务框架;D 应该用 TCC 来自定义预占与确认逻辑。