TCC 模式的原理是什么?Try/Confirm/Cancel 各做什么?
简化版
TCC 是一种业务层面的两阶段补偿型分布式事务,把每个操作拆成三个方法:Try(尝试,预留资源)、Confirm(确认,真正执行)、Cancel(取消,释放预留)。第一阶段所有参与者都执行 Try 预留资源;第二阶段——全成功就都 Confirm 提交,任一失败就都 Cancel 回滚。它把 2PC 的「数据库层锁资源」换成了「业务层预留资源」,不长时间占数据库锁,性能高,但需要每个服务自己实现三个方法,业务侵入大。
详细版
以「账户扣款」为例:
- Try:检查余额是否充足,冻结要扣的金额(可用余额 - X,冻结余额 + X)。不是真正扣,是预留。
- Confirm:真正扣款——把冻结的金额扣掉(冻结余额 - X)。
- Cancel:解冻——把冻结的金额还回可用余额(可用余额 + X,冻结余额 - X)。
执行流程:
- 第一阶段:事务管理器调用所有参与者的 Try,各自预留资源。
- 第二阶段:
- 所有 Try 都成功 → 调用所有参与者的 Confirm,正式提交。
- 任一 Try 失败 → 调用所有已 Try 成功者的 Cancel,释放预留。
和 2PC 的对比:
- 2PC 在数据库层锁资源、由 DB 保证,业务无感但锁得久。
- TCC 在业务层用「冻结」等中间状态预留资源,锁的是业务资源(马上释放 DB 锁),并发高,但要写三个方法。
三大必须处理的问题:空回滚、悬挂、幂等(见专门题目)。
完整版教学
一、TCC 的核心思想:把「锁」变成「预留」
2PC 的问题是数据库锁占用太久(准备到提交全程锁着)。TCC 的巧思是:别锁数据库,改成在业务数据里加一个「预留/冻结」的中间态。Try 阶段做完,数据库事务就提交了(DB 锁马上释放),资源以「冻结」的软状态被预留着。这样即使第二阶段还没来,数据库也不被长时间占用,并发能力大大提升。这本质是用「业务补偿」替代「数据库回滚」,用「软状态冻结」替代「持有锁」。
二、三个方法的职责边界
- Try:做检查 + 预留,不做最终动作。检查业务约束(余额够不够、库存够不够)、预留资源(冻结、扣减可用库存到预留库存)。Try 成功意味着「第二阶段无论 Confirm 还是 Cancel 都不会再失败」——所以检查都放在 Try。
- Confirm:只做真正的业务提交,用 Try 预留的资源。设计上要求 Confirm 不再做业务检查(因为 Try 已经查过、资源已经预留),只管提交,且必须幂等(可能被重试)。
- Cancel:释放 Try 预留的资源,把冻结的还回去。同样要求幂等,且要能处理「Try 没成功却收到 Cancel」的空回滚。
记忆点:Try 负责「一切可能失败的检查与预留」,让 Confirm/Cancel 变成「一定能成功的收尾动作」。这是 TCC 保证第二阶段不失败的关键设计原则。
三、为什么 TCC 性能好
因为 Try 一结束数据库事务就提交了,不像 2PC 那样跨阶段长期持有数据库行锁。资源冲突从「数据库锁」下沉为「业务预留字段」,多个事务可以对同一账户并发做 Try(各自冻结自己那部分),只要总额够就行。所以 TCC 适合高并发、对一致性要求高的资金类场景(支付、账户、优惠券)。
四、代价:业务侵入与复杂度
TCC 不是免费的:
- 每个参与者要为每个操作写 Try/Confirm/Cancel 三个方法,还要设计冻结/预留字段,改造量大。
- 要处理空回滚(Try 没执行就收到 Cancel)、悬挂(Cancel 比 Try 先到)、幂等(Confirm/Cancel 被重试)三大边界问题。
- Confirm/Cancel 要保证最终一定成功(失败要不断重试),所以它们本身必须简单、幂等、无复杂业务判断。
因此 TCC 一般只用在核心的、值得投入改造成本的资金链路,普通业务用 Saga 或可靠消息更划算。
五、TCC vs 2PC vs Saga
| 维度 | 2PC/XA | TCC | Saga |
|---|---|---|---|
| 层面 | 数据库层 | 业务层 | 业务层 |
| 资源锁定 | DB 锁,久 | 业务冻结,短 | 无锁 |
| 隔离性 | 有 | 有(预留隔离) | 无(脏读风险) |
| 业务侵入 | 无 | 大(三个方法) | 中(补偿方法) |
| 性能 | 差 | 好 | 好 |
| 适用 | 传统强一致 | 资金高一致 | 长流程可补偿 |
六、常见误区与追问
这道题不能只背概念,要把「TCC 模式」放回真实分布式系统里解释:谁发起、谁协调、状态如何变化、失败后怎么兜底,以及它和性能、可用性、一致性的取舍。
| 回答层次 | 要讲清的内容 | 容易漏掉的边界 |
|---|---|---|
| 核心结论 | TCC 把业务操作拆成 Try、Confirm、Cancel,靠业务预留、确认和补偿实现最终一致 | 不要停在名词解释 |
| 流程机制 | Try 预留资源 -> 所有分支 Try 成功 -> 执行 Confirm 完成提交 -> 任一 Try 失败执行 Cancel -> Confirm/Cancel 必须幂等 | 说明触发方、参与方、状态变化和兜底 |
| 工程取舍 | 扣款场景 Try 冻结 100 元,Confirm 真扣款,Cancel 解冻 100 元 | 分布式事务没有免费强一致,性能、可用性和业务侵入一定要取舍 |
TCC 模式 面试拆解:
1. Try 预留资源
2. 所有分支 Try 成功
3. 执行 Confirm 完成提交
4. 任一 Try 失败执行 Cancel
5. Confirm/Cancel 必须幂等
记忆钩子:先说本地事务失效的原因,再拆协调、补偿、消息、幂等、对账和回滚边界;回答时要紧扣「TCC 模式」这道题,不要把相邻概念混成一段泛泛的分布式套话。
- 误区:TCC 是数据库自动完成的。 TCC 是业务层事务,每个分支都要实现 Try、Confirm、Cancel。
- 误区:Try 阶段可以直接完成最终扣减。 Try 应预留资源,否则 Cancel 难以安全补偿。
- 误区:Confirm/Cancel 不会重复调用。 网络重试会导致重复调用,必须幂等。
- 追问:TCC 三个典型问题是什么? 空回滚、悬挂、幂等。
- 追问:TCC 适合什么场景? 资金、库存预留等能明确冻结和确认的高一致业务。
- 追问:TCC 缺点是什么? 业务侵入大,接口数量多,状态管理复杂。
七、加强记忆
TCC = Try/Confirm/Cancel 的业务级两阶段补偿事务。Try 做检查和资源预留(冻结),Confirm 用预留资源真正提交,Cancel 释放预留回滚;一阶段全体 Try,二阶段全成则 Confirm、有败则 Cancel。核心思想是把 2PC 的「数据库锁」换成「业务层冻结软状态」,Try 一结束就释放 DB 锁,因此并发高、适合资金类。代价是要写三个方法、业务侵入大,并必须处理空回滚、悬挂、幂等三大问题;Confirm/Cancel 须幂等且保证最终成功。