什么是分布式事务?常见的分布式事务解决方案有哪些?
分布式事务是随着微服务架构和分布式系统的普及而产生的一个核心技术难题。
下面我将分两部分为你详细解答:什么是分布式事务,以及目前业界主流的解决方案。
一、 什么是分布式事务?
在单体应用中,我们通常使用关系型数据库的本地事务来保证操作的 ACID 特性(原子性、一致性、隔离性、持久性)。例如:扣减库存和生成订单在同一个数据库中,要么同时成功,要么同时失败。
分布式事务(Distributed Transaction) 是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。
通俗地说:
当一个业务流程需要跨越多个独立的服务(或多个独立的数据库)才能完成时,为了保证这些跨服务/数据库的操作“要么全部成功,要么全部失败”,就需要用到分布式事务。
典型场景:
- 跨行转账: A银行扣钱(服务A/库A),B银行加钱(服务B/库B)。
- 电商下单: 订单服务生成订单,库存服务扣减库存,积分服务增加积分。
分布式事务之所以难,是因为网络是不可靠的(存在延迟、丢包、宕机等),这引出了分布式系统的两大理论基石:CAP定理(一致性、可用性、分区容错性最多满足两个)和 BASE理论(基本可用、软状态、最终一致性)。
二、 常见的分布式事务解决方案
目前的解决方案大致可以分为两类:强一致性方案(牺牲性能换取一致性)和最终一致性方案(牺牲实时一致性换取高性能,也是目前微服务的主流)。
1. 两阶段提交(2PC / XA协议)—— 【强一致性】
这是最古老、最标准的分布式事务方案。它引入了一个“协调者(Coordinator)”来统筹所有的“参与者(Participant)”。
- 第一阶段(Prepare): 协调者询问所有参与者“可以提交吗?”。参与者执行本地事务并锁定资源,但不提交,回复Yes或No。
- 第二阶段(Commit/Rollback): 如果所有参与者都回复Yes,协调者发送Commit指令;如果有任何一个回复No或超时,协调者发送Rollback指令。
- 优点: 尽量保证了强一致性。
- 缺点: 性能差(整个过程中资源被死锁,并发度极低)、单点故障(协调者挂了就全完了)、数据不一致风险(第二阶段如果部分节点没收到网络包也会不一致)。
- 适用场景: 单体应用下的多数据源,不适合高并发的微服务。
2. TCC(Try-Confirm-Cancel)—— 【最终一致性/业务补偿】
TCC 是业务层面的两阶段提交,不依赖数据库的锁,而是需要开发者针对每个操作写三个方法。
- Try(尝试): 对业务系统做检测及资源预留(例如:冻结账户里100块钱,而不是直接扣除)。
- Confirm(确认): 执行真正的业务操作(例如:把冻结的100块钱真正扣掉)。
- Cancel(取消): 在业务执行错误时,取消Try阶段预留的资源(例如:把冻结的100块钱解冻)。
- 优点: 性能高(没有长事务的数据库锁),数据一致性较好。
- 缺点: 开发成本极高(一个接口要写三个方法),需要处理网络异常导致的“空回滚”、“幂等性”、“悬挂”等复杂问题。
- 适用场景: 对数据一致性要求较高、并发量也高的金融/支付核心业务。
3. Saga 模式 —— 【最终一致性/长事务补偿】
Saga 是一种长事务解决方案,它将一个长事务拆分为多个本地短事务。每个本地事务都有一个对应的补偿操作。
- 正向操作: T1 -> T2 -> T3 -> T4
- 补偿操作: C1 <- C2 <- C3 <- C4 (与正向操作一一对应)
如果执行到 T3 失败了,系统会自动反向执行 C2 -> C1,来撤销之前的操作。 - 优点: 性能很高(每个步骤都是独立的本地事务,直接提交释放锁),非常适合长流程业务、对接第三方遗留系统。
- 缺点: 缺乏隔离性(T2执行完,T3没执行完时,外部可能会读到T2中间状态的“脏数据”)。
- 适用场景: 跨度长、涉及外部第三方服务(如订机票、订酒店组成的旅游套餐业务)。
4. 可靠消息最终一致性(基于 MQ)—— 【最终一致性】
核心思想是将分布式事务拆分成了两个本地事务,通过消息队列(MQ)连接起来,保证下游一定会消费成功。有以下两种常见做法:
- 本地消息表: 业务数据和消息数据放在同一个数据库中。在执行本地业务时,顺便在一个本地事务中插入一条状态为“待发送”的消息记录。然后由后台定时任务扫描这张表,发送到 MQ,下游消费成功后更新状态为“已完成”。
- 事务消息(如 RocketMQ): 类似 2PC 的思想。
- 发送“半消息”(Half Message)到 MQ(MQ收到但不投递给消费者)。
- 执行本地事务。
- 本地事务成功,向 MQ 发送 Commit(MQ开始投递);本地失败,发送 Rollback(MQ丢弃消息)。
- 如果 MQ 没收到确认,会回查本地系统事务状态。
- 优点: 实现了各服务之间的解耦,系统吞吐量极高。
- 缺点: 实时性差(只能保证最终一致),依赖 MQ 的高可用。
- 适用场景: 绝大多数不需要强一致性的电商、互联网业务(如:支付成功后发短信、加积分、送优惠券)。
5. 最大努力通知 —— 【最终一致性】
这是最简单的柔性事务方案。上游系统完成本地事务后,尽最大努力把结果通知给下游系统。
- 机制: 上游通过 MQ 或者 定时任务向下游发通知。如果下游没响应,上游会按照阶梯时间(如1m, 5m, 10m, 1h)不断重试。同时,上游提供一个查询接口,下游如果实在没收到,可以主动来查。
- 适用场景: 对时间不敏感的边缘业务,或者跨企业的系统调用(例如:支付宝/微信支付成功后的异步回调通知)。
6. Seata 框架(阿里巴巴开源)
Seata 是目前 Java/Spring Cloud 生态中最火的分布式事务框架,它封装了上述多种模式,提供了 AT、TCC、Saga、XA 四种模式。其中最常用的是 AT 模式。
- AT 模式原理: 零代码入侵。开发者只需要加上
@GlobalTransactional注解,像写本地事务一样。Seata 会拦截 SQL,自动生成重做日志(undo_log)。如果全局事务回滚,Seata 会利用 undo_log 自动进行反向 SQL 补偿。 - 优点: 使用极其简单,对业务代码无侵入。
- 缺点: 存在全局锁,对高并发场景有一定性能影响。
三、 总结与选型建议
在实际架构中,“没有最好的方案,只有最合适的方案”,通常会遵循“能不用分布式事务就不用”的原则,尽量通过领域驱动设计(DDD)合并服务。如果必须用,选型参考如下:
- 单体应用多数据源
2PC / XA - 业务流程短、对一致性要求极高、高并发(如金融)
TCC - 流程长、涉及第三方外部系统调用(如机酒预订)
Saga - 高并发的互联网常规业务(如下单发积分/发货)
基于MQ的可靠消息最终一致性 - 跨平台/跨公司的结果通知(如支付回调)
最大努力通知 - 中小团队,希望开发快、代码侵入少
Seata AT模式