分布式事务
分布式事务解决跨服务、跨数据库的一致性问题。核心矛盾是强一致性 vs 高可用,因此产生了从 ACID 到 BASE 的理论演进,以及 2PC、TCC、SAGA 等多种实现模式。
提示
CAP、BASE、2PC/TCC/SAGA等分布式一致性解决方案
CAP 定理
分布式系统最多只能同时满足一致性(C)、可用性(A)、分区容忍性(P)中的两个。
| 组合 | 说明 | 代表系统 |
|---|---|---|
| CP | 优先一致性,分区时可能拒绝服务 | ZooKeeper、Nacos(集群模式) |
| AP | 优先可用性,分区时返回旧数据 | Eureka、Cassandra |
| CA | 不存在,网络分区不可避免 | 传统单机数据库 |
实际工程中,分区容忍性是必须选择的,实际是在 CP 和 AP 之间权衡。
金融交易系统 → CP(强一致性优先) 社交网络、秒杀系统 → AP(高可用优先)
BASE 理论
BASE 是对 CAP 中一致性和可用性权衡的结论,柔性的最终一致性,与 ACID 形成对比。
| 特性 | 说明 |
|---|---|
| 基本可用 | 允许降级服务保证核心功能 |
| 软状态 | 允许中间状态存在 |
| 最终一致性 | 经过一定时间后达成一致 |
| 维度 | ACID(传统事务) | BASE(柔性事务) |
|---|---|---|
| 一致性 | 强一致性 | 最终一致性 |
| 可用性 | 低 | 高 |
| 适用场景 | 银行转账 | 社交点赞、订单处理 |
2PC(两阶段提交)
阶段一(Prepare):协调者向所有参与者发送 Prepare 请求,参与者执行事务(写 redo log)但不提交,回复「可以提交」或「失败」。
阶段二(Commit):所有参与者同意则发送 Commit 全部提交,任一失败则发送 Rollback 全部回滚。
问题:同步阻塞(prepare 后锁定资源)、单点故障(协调者崩溃时无限等待)、数据不一致(协调者发送部分 commit 后崩溃)。
TCC(Try-Confirm-Cancel)
业务层面的两阶段提交,每个阶段需要业务代码实现:
| 阶段 | 说明 | 示例 |
|---|---|---|
| Try | 资源检查与预留 | 冻结库存、冻结余额 |
| Confirm | 确认执行 | 扣减库存、扣减余额 |
| Cancel | 取消释放 | 解冻库存、解冻余额 |
@LocalTCC
public interface InventoryTccAction {
@TwoPhaseBusinessAction(name = "inventoryAction")
boolean try扣库存(BusinessActionContext ctx, @BusinessActionContextParameter(paramName = "count") int count);
boolean confirm扣库存(BusinessActionContext ctx);
boolean cancel解冻库存(BusinessActionContext ctx);
}TCC vs SAGA:TCC 有 Try 阶段预留资源,无脏读风险,但开发成本高(三个接口);SAGA 无预留资源,侵入低,但有脏读风险。
SAGA(长事务模式)
一阶段直接提交本地事务,失败时通过补偿操作回滚。
A → B → C → D
↓ ↓ ↓
补偿 补偿 补偿(反向回滚)适用场景:业务流程长(跨多个微服务)、参与者无法改造(遗留系统、外部服务)。
AT 模式(Seata)
Seata 的无侵入式方案,自动生成回滚 SQL。
一阶段:解析业务 SQL → 生成前后镜像 → 执行业务 SQL → 生成 Undo Log
二阶段:提交(删除 Undo Log)、回滚(根据 Undo Log 生成反向 SQL)
| 维度 | AT 模式 | TCC 模式 |
|---|---|---|
| 侵入性 | 无(自动处理) | 高(需要业务代码) |
| 性能 | 较低(有全局锁) | 高(无全局锁) |
| 适用场景 | 普通业务 | 高并发核心系统 |
可靠消息队列
事务消息实现最终一致性的常用方案:发送方发送消息到 MQ(待确认状态)→ 执行本地事务 → 提交/回滚消息状态。
适用场景
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| 单服务多数据源 | XA 模式 | 需要跨数据源强一致性 |
| 普通业务微服务 | AT 模式(Seata) | 无侵入,开发成本低 |
| 高并发核心系统 | TCC 模式 | 无全局锁,性能高 |
| 长事务/遗留系统 | SAGA 模式 | 侵入低,支持补偿 |
| 异步场景 | 可靠消息队列 | 最终一致性,解耦上下游 |
FAQ
Q: AT 模式和 TCC 模式如何选择? A: AT 模式无侵入、开发成本低,适合大多数业务场景;TCC 模式需要业务代码实现三个接口,但无全局锁、性能高,适合高并发核心系统。如果对性能要求不高且不想改动业务代码,优先选 AT。
Q: SAGA 的脏读问题如何解决? A: SAGA 一阶段直接提交本地事务,其他事务可能读到中间状态。解决方式:1)在业务层面设计容忍脏读的逻辑;2)通过语义锁标记中间状态,其他事务读到时等待或拒绝;3)将敏感操作放在 SAGA 的最后几步执行。
Q: 分布式事务一定要用框架吗? A: 不一定。对于简单的异步场景,可靠消息队列(如 RocketMQ 事务消息)即可满足需求。只有涉及多个服务的同步事务协调时,才需要 Seata 等框架。过度使用分布式事务框架会增加系统复杂度。