想象你在筹备一场大型演唱会。总导演需要在舞台上同时协调灯光组、音响组、道具组——任何一个小组掉链子,整场演出就可能翻车。但在现实里,各小组在后台各自忙碌,谁也不知道别人的进度。这就是微服务世界里每天发生的事:一个下单操作要同时扣库存、减余额、加积分,三个服务分布在不同的机器上,任何一个失败,钱货就对不上了。 Seata 就是这场演唱会的“总导演”。它把自己拆成三个角色:TM(执行导演)负责宣布“演唱会开始/结束”,TC(总控台)负责掌握所有小组的状态,RM(各小组组长)负责干完自己那摊活并如实汇报。它还提供了四种编排模式:AT 是“全程录像、出错就倒放”,TCC 是“先预约、再确认、不行就取消”,SAGA 是“一个接一个演出、出问题就倒着取消前面的节目”,XA 则是数据库原生的“双门锁死”方案。 这篇文会用一场演唱会的筹备与演出来讲透 Seata 的协作机制,让你从零看懂分布式事务到底在解决什么问题。
学习 Seata 能让你在微服务跨库事务场景下给出可落地的解决方案,是分布式系统设计能力的硬通货。
可视化演示
展示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,后面跟着若干个 RM 分支。
- 竖线分隔的语义这里的竖线不是数学运算符,而是表示“协作关系”的分隔符,读作“TM 委托 TC,TC 指挥各 RM”。
- 下标 n 的含义RM 的下标从 1 到 n,表示有 n 个参与本次事务的资源分支,n 越大说明事务跨越的服务越多。
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 模式对业务代码几乎零侵入——你不用写任何补偿逻辑,数据库行锁也只短暂占用,适合大多数对一致性要求较高的业务场景。
- 对所有分支取与\(\bigwedge_{i=1}^{n}\) 表示从第 1 个分支到第 n 个分支逐个做逻辑与,全部为真结果才为真。
- 每个分支的本地提交\(\text{LocalCommit}_i\) 是一个布尔值,表示第 i 个 RM 的本地事务是否提交成功。
- 最终全局提交结论整个表达式的结果 Commit 为真,当且仅当所有的 LocalCommit 都为真,这体现了“一票否决”语义。
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 更高,适用于对一致性要求严苛、愿意为每段业务付出编码成本的场景。它像一场精心排练过的彩排——每一次试灯、试音都留下记录,最终要么正式上演,要么原样撤场。
- Try 的结果作为输入\(\text{Try}(x)\) 是对业务数据 x 做预占操作后的结果状态,x 表示订单号或账户 ID。
- Confirm 是 Try 的正向确认\(f(\text{Try}(x))\) 表示在 Try 成功的基础上执行确认函数 f,把预留变成实际扣减。
- Cancel 是 Try 的反向补偿\(g(\text{Try}(x))\) 表示在 Try 成功但整体失败时执行取消函数 g,把预留归还。
SAGA模式
有时候大型演唱会要演一整个系列:明天是爵士专场,后天是摇滚专场,大后天是民谣专场。每个专场之间是串联关系,由总控台按照节目单一个一个往下走。如果中间某个专场临时取消了,总控台不会把已经演完的专场再演一遍,而是倒着把前面那些专场的场次依次“退款”给观众。这就是 SAGA 模式:用状态机编排,按顺序执行,失败则反向补偿。
SAGA 模式把一个长事务拆分成多个本地事务,每个本地事务对应一个状态节点。正向补偿就是按照编排顺序依次执行各个子事务,每个子事务执行完就提交并记录执行状态;反向补偿是当某个子事务失败时,从不成功的节点开始,逆序执行前面所有子事务的补偿动作。比如订机票、订酒店、订车三个步骤,酒店失败了,就依次取消机票和之前的其他预订。
SAGA 的核心是状态机编排,即每个全局事务都有一个状态机,记录了当前执行到第几个子事务、每个子事务是成功还是失败。与 TCC 相比,SAGA 不需要 Try 阶段,业务代码里不需要写预占资源的逻辑,因此无侵入性更好。但 SAGA 有一个先天弱点:子事务提交后的中间状态对其他事务可见,如果中间状态被读走了而后续又回滚,就可能产生脏读。
SAGA 适合长事务、多服务、对实时一致性要求不苛刻的场景。它像一场多场次的系列演出,总控台靠清晰的节目单和退票规则,把复杂的流程打理得井井有条。
- 补偿操作组合\(C_i\) 表示第 i 个子事务的补偿函数(比如退票、退款),\(\circ\) 是函数复合符号,先执行右边的函数。
- 倒序执行的顺序从 \(C_N\) 开始到 \(C_1\) 结束,下标从大到小,表示补偿必须严格逆着正向顺序执行。
- 整体回滚结果整个复合表达式 Rollback 表示“从最后一步开始,一路退回到第一步”,任何一步的补偿失败都会导致回滚链路中断。
事务流程
现在把整场演唱会从头到尾捋一遍。执行导演 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 在报错日志里到底发生在哪一步——是全局没开启、分支没注册,还是回滚指令没送达。
- 开启环节Begin 表示 TM 向 TC 发起全局事务请求,TC 生成全局事务 ID 并保存,此时事务处于进行中状态。
- 注册环节Register 表示各 RM 带着 XID 向 TC 注册分支事务,TC 记录每个分支的标识和状态。
- 执行环节Exec 表示各 RM 在本地执行 SQL,并保存执行结果快照,完成后上报执行结果。
- 终局决策Commit/Rollback 表示 TM 汇总所有结果后,向 TC 下达全局提交或回滚指令,TC 再逐一下发到各 RM。
应用实践
再完美的编排方案,落到真实舞台上都会遇到各种意外。Seata 在实践中最重要的第一步是配置部署:TC 需要作为一个独立服务部署(TCServer),客户端通过配置文件指定 TC 的地址;数据库里要建好 undo_log 表并初始化 AT 模式所需的表结构;file.conf 和 registry.conf 两个配置文件决定了事务日志存储方式和注册中心类型。
第二步是高并发优化。AT 模式虽然业务入侵小,但全局锁会让跨行更新变成串行,高并发下吞吐量会明显下滑。常用的优化手段包括:尽量缩小事务范围,把无关读操作移出全局事务;用 TCC 替换热点账户的 AT 模式;对热点数据做分片,避免所有请求都锁同一行。
第三步是踩坑经验。最常见的坑有:undo_log 表和数据表不在同一数据库导致回滚失败;服务重启后 TC 出现孤儿事务;超时时间设置过短导致长事务被误杀;以及回滚 SQL 生成失败(比如使用了不支持的类型或函数)。这些坑大多要靠配置检查、日志分析和压测来提前规避。最终目标只有一个——在高并发微服务场景下保证数据的最终一致性:允许短暂的不一致,但最终要对账收货。
实践出真知。演唱会办得越多,对现场的突发状况就越有预判。Seata 的落地同样如此:配置、优化、踩坑三轮打磨之后,分布式事务才能在高并发环境下稳稳兜住数据的底线。
---
- 各分支成功率\(p_i\) 表示第 i 个分支事务的执行成功率,取值范围是 0 到 1。
- 累乘运算\(\prod_{i=1}^{n}\) 表示把 n 个分支的成功率连乘,只要有一个分支成功率小于 1,整体就会明显下降。
- 全局成功率公式\(P_{\text{success}}\) 是全局事务的成功概率,这个公式解释了为什么“事务越大越容易失败”。
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 演示
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()
class ATModeSimulator:定义 AT 模式模拟器,对应 Seata 中客户端侧对分支事务的管理能力。self.undo_log = []快照日志,模拟 Seata 的undo_log表,记录每个分支修改前的旧值。old_value = self.data[target]在执行 SQL 修改前先读旧值,这是生成回滚 SQL 的基础。self.undo_log.append((target, old_value))把旧值追加到快照栈,模拟 AT 模式的 before image 记录过程。def rollback(self):二阶段回滚:从栈尾开始 pop,逆序遍历快照,正好对应 Seata 倒序生成回滚 SQL 的策略。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 中负责全局事务发起和最终决策的角色是?
- A.RM 负责本地事务执行和上报
- B.TM 负责发起提交回滚决策← 正确答案
- C.TC 只负责协调分支注册
- D.各 RM 自行决定是否提交
答案:B。TM(事务管理器)负责全局事务的开启、提交和回滚决策,相当于执行导演;TC 是协调者,RM 是资源执行者。A、C 描述的是 TC 和 RM 的职责,D 完全错误,因为分支不能自行决定提交。
Q2. AT 模式二阶段回滚的数据依据是什么?
- A.业务自己写的补偿方法
- B.undo_log 中的 before image← 正确答案
- C.数据库的重做日志 redo log
- D.应用服务器的内存快照
答案:B。AT 模式一阶段把修改前的旧值写入 undo_log,二阶段回滚时根据 before image 逆向生成回滚 SQL 恢复数据。A 是 TCC 的做法,C 是数据库崩溃恢复用的,D 不涉及持久化数据。
Q3. TCC 模式下 Try 阶段失败后,系统应执行什么?
- A.直接放弃,不做任何操作
- B.对所有分支执行 Confirm
- C.对已成功的 Try 执行 Cancel← 正确答案
- D.不执行任何补偿,等超时
答案:C。TCC 中只要有一个 Try 失败,TC 就要对已经执行成功的 Try 调用 Cancel,释放预占资源。B 是全部成功才做,A 和 D 会造成资源泄漏,属于错误处理。
Q4. SAGA 模式相比 AT 模式最大的不同是什么?
- A.SAGA 使用全局锁保证隔离
- B.SAGA 子事务提交后中间状态可见← 正确答案
- C.SAGA 不需要任何协调角色
- D.SAGA 只能用于单数据库
答案:B。SAGA 没有全局锁,子事务提交后数据立即可见,所以不具备隔离性,可能产生脏读。A 说反了,C 错误因为 SAGA 也需要协调器编排,D 错误因为 SAGA 用于跨服务长事务。
Q5. Seata 中全局锁的作用是什么?
- A.锁住整个数据库表
- B.防止两个全局事务并发修改同一行← 正确答案
- C.代替数据库的行锁
- D.加快查询速度
答案:B。全局锁是 TC 维护的逻辑锁,防止两个全局事务同时修改同一行数据造成数据不一致。它不锁表(A 错),不能代替数据库行锁(C 错),与查询性能无关(D 错)。
Q6. 一笔全局事务分支成功率为 0.9,共有 4 个分支,全局成功率约为?
- A.0.90
- B.0.81
- C.0.66← 正确答案
- D.0.36
答案:C。全局成功率等于各分支成功率连乘,0.9^4 = 0.6561,约 0.66。这个公式说明分支越多整体成功率越低,因此设计上要尽量减少分布式事务的分支数量。
Q7. AT 模式一阶段本地事务提交后立即释放了什么?
- A.全局锁和本地锁都释放
- B.本地行锁,但全局锁保留← 正确答案
- C.全局锁,但本地行锁保留
- D.所有锁都保留到二阶段
答案:B。AT 模式一阶段本地事务直接提交、释放本地数据库行锁,但 TC 维护的全局锁要保留到二阶段结束,防止其它事务读到中间状态。A 说全释放会破坏一致性,C、D 与设计矛盾。
Q8. Seata 的三大角色包含哪些?
- A.TM 事务管理器← 正确答案
- B.TC 事务协调者← 正确答案
- C.RM 资源管理者← 正确答案
- D.DB 数据库连接池管理
答案:ABC。Seata 的三大角色是 TM(发起决策)、TC(协调调度)、RM(资源执行)。数据库连接池是数据访问层的组件,不参与 Seata 的角色划分,所以 D 不选。
Q9. 以下哪些场景适合使用 SAGA 模式?
- A.跨多服务的长流程业务编排← 正确答案
- B.每个子服务都有补偿方法可用← 正确答案
- C.要求强隔离、禁止中间态可见
- D.预占资源后需显式确认的业务
答案:AB。SAGA 适合长事务串联且允许中间态可见的场景,子服务必须提供补偿方法。C 要求强隔离,SAGA 做不到;D 描述的是 TCC 的 Try/Confirm 特性,不是 SAGA 的核心特征。
Q10. AT 模式在哪些情况下不适合使用?
- A.高并发热点账户频繁被更新← 正确答案
- B.长事务持有全局锁导致大量阻塞← 正确答案
- C.单库内普通的本地事务
- D.业务需要自定义资源预占流程
答案:AB。AT 模式对热点数据的全局锁竞争激烈,长事务会长时间占用全局锁,都会导致吞吐降低,不适合。C 是本地事务,压根不需要分布式事务框架;D 应该用 TCC 来自定义预占与确认逻辑。