分布式事务详解
一、背景与问题引入
在单体系统中,事务(Transaction)通常由数据库本身保证——ACID(原子性、一致性、隔离性、持久性)特性足以覆盖大多数业务需求。
然而,随着系统拆分为多个微服务后,一个完整的业务流程往往需要多个服务、多个数据库的协作。例如:
- 下单服务 → 库存服务 → 支付服务
- 用户服务 → 积分服务 → 消息通知服务
此时,单一数据库事务无法覆盖整个业务链,分布式事务问题由此产生:
如何在多服务、多数据源的协作中,依然保证数据一致性?
二、核心概念与一致性模型
1. 本地事务与全局事务
- 本地事务(Local Transaction):仅作用于单个数据库,由数据库自身保证 ACID。
- 全局事务(Global Transaction):跨多个节点或服务的事务,需要协调各方共同提交或回滚。
2. 一致性模型的取舍
模型 | 描述 | 适用场景 |
---|---|---|
强一致性 | 所有节点在任意时刻数据一致 | 金融核心系统 |
最终一致性 | 数据最终达到一致,但短时可能不一致 | 电商、积分、库存 |
弱一致性 | 某些节点可能长期不一致 | 高可用优先的场景 |
三、分布式事务的典型实现方案
1. XA 协议(两阶段提交)
核心思路:
协调者(Transaction Manager)通过两阶段操作控制多个资源(数据库、消息系统等):
- 阶段一(Prepare):各参与者预提交并锁定资源;
- 阶段二(Commit/Rollback):协调者统一决策提交或回滚。
优点:标准化、理论上可保证强一致性。
缺点:性能开销大、资源锁定时间长,难以应对高并发。
典型实现:Atomikos、Bitronix、Narayana。
2. TCC 模型(Try-Confirm-Cancel)
思想:将业务操作拆分为三个阶段:
- Try:预留资源;
- Confirm:确认执行;
- Cancel:取消执行。
示例(下单流程):
阶段 | 操作 |
---|---|
Try | 预扣库存、冻结资金 |
Confirm | 扣减库存、完成支付 |
Cancel | 回滚库存、解冻资金 |
优点:业务可控,补偿逻辑灵活。
缺点:开发成本高,需要业务方显式实现三种接口。
典型实现:Seata TCC、Hmily。
3. 本地消息表 / Outbox 模式
核心思想:
通过“事务内写消息 + 异步发送”实现最终一致性。
- 在本地事务中同时写入业务数据与消息表;
- 消息发送器异步扫描消息表并推送;
- 下游消费成功后删除消息。
优点:实现简单,无需全局锁。
缺点:消息补偿、重复投递需额外处理。
适用场景:订单创建、状态变更通知等异步链路。
4. Saga 模型
核心思路:
将长事务拆分为一系列可补偿的本地事务。每一步有对应的补偿操作:
T1, T2, T3 ...
C1, C2, C3 ...
当某个步骤失败时,反向调用补偿逻辑(C3、C2、C1)回滚。
优点:易于理解,适合长事务场景。
缺点:补偿逻辑复杂,不适合强一致性场景。
典型实现:Seata Saga、ServiceComb Saga、LRA。
四、分布式事务框架对比
框架 | 模型 | 语言支持 | 优点 | 缺点 |
---|---|---|---|---|
Seata | AT / TCC / Saga / XA | Java | 社区活跃、集成简单 | 对复杂事务性能有限 |
RocketMQ 事务消息 | 本地消息表变体 | Java | 与消息耦合度高、最终一致性 | 无全局事务 |
Hmily | TCC | Java | 性能较好、注解驱动 | 手动实现接口较繁琐 |
LCN | TxManager协调 | Java | 使用透明 | 维护成本高 |