【关于分布式事务一致性】
📅 2026/7/5 8:50:43
👁️ 阅读次数
📝 编程学习
“事务一致性”是数据库和分布式系统中最核心、也是最难的概念之一。在面试和实际架构设计中,它通常是区分初级和中高级开发的“分水岭”。
以下是对“事务一致性”的全面深度解析:
一、 什么是事务一致性?
在数据库事务的ACID特性中,C 代表一致性(Consistency)。
- 定义:事务执行前后,数据库必须从一个一致性状态变换到另一个一致性状态。
- 通俗理解:事务不能破坏数据库的业务规则和完整性约束。
- 例子:A转账给B 100元。A的余额减少100,B的余额增加100。如果A扣了钱但B没加钱,或者总金额变多了/变少了,这就破坏了“一致性”。
注意:一致性是事务的目的,而原子性(A)、隔离性(I)、持久性(D)是数据库为了保证一致性而提供的手段。
二、 本地事务一致性 vs 分布式事务一致性
1. 本地事务(单库场景)
在单体架构或单数据库场景下,保证一致性非常简单。
- 实现方式:利用数据库自身的事务机制(如 MySQL 的 InnoDB 引擎),配合代码中的
@Transactional注解。 - 特点:强一致性,实现简单,性能高。
2. 分布式事务(微服务/多库场景)
在微服务架构下,一个业务操作(如:下单 -> 扣库存 -> 扣余额 -> 加积分)可能跨越多个服务和多个数据库。
- 痛点:本地事务无法跨库/跨服务。如果“扣库存”成功,但“扣余额”失败,数据就不一致了。
- 目标:保证跨服务、跨数据库操作的最终数据一致性。
三、 分布式事务的核心理论基石
在解决分布式事务前,必须了解两大理论:
1. CAP 定理
一个分布式系统最多只能同时满足以下三项中的两项:
- C (Consistency) 一致性:所有节点在同一时间的数据完全一致。
- A (Availability) 可用性:服务一直可用,且正常响应时间范围内。
- P (Partition tolerance) 分区容错性:遇到网络分区故障时,系统仍能继续运行。
- 结论:在分布式系统中,P 是必须的(网络总会出问题),所以我们只能在CP(强一致,牺牲可用性)和AP(高可用,牺牲强一致)之间做选择。
2. BASE 理论
是对 CAP 中 AP 方案的延伸,核心思想是即使无法做到强一致性,但可以通过适当的方式实现最终一致性。
- BA (Basically Available) 基本可用:系统出现故障时,允许损失部分可用性(如响应时间变长、降级页面)。
- S (Soft state) 软状态:允许系统中的数据存在中间状态,且该状态不影响系统整体可用性。
- E (Eventually consistent) 最终一致性:系统中的所有数据副本,在经过一段时间的同步后,最终能够达到一致的状态。
四、 分布式事务的主流解决方案(重点)
根据对一致性的要求(强一致 vs 最终一致),主流方案有以下几种:
1. 2PC / 3PC(两阶段/三阶段提交)—— 强一致性
- 原理:引入一个协调者(Coordinator)和多个参与者(Participant)。
- 阶段一(Prepare):协调者问所有参与者:“你们能提交吗?” 参与者执行事务但不提交,锁住资源,返回 Yes/No。
- 阶段二(Commit/Rollback):如果所有参与者都返回 Yes,协调者下发 Commit 指令;只要有一个返回 No,就下发 Rollback 指令。
- 缺点:同步阻塞(性能差)、单点故障(协调者挂了大家干瞪眼)、数据不一致(阶段二部分节点宕机)。
- 代表框架:Seata 的AT 模式(对 2PC 的改进,通过解析 SQL 生成 undo_log 实现自动回滚,无代码侵入)。
2. TCC(Try-Confirm-Cancel)—— 强/最终一致性
- 原理:业务层面的 2PC。将事务分为三个阶段,全部由业务代码实现:
- Try:资源预留和业务检查(如:冻结账户 100 元,但不实际扣除)。
- Confirm:真正执行业务(如:将冻结的 100 元实际扣除)。要求幂等。
- Cancel:取消执行,释放资源(如:解冻那 100 元)。要求幂等。
- 优点:无全局锁,资源锁定粒度由业务控制,性能比 2PC 好。
- 缺点:代码侵入性极强(每个接口要写 3 个方法)。
- 面试必问(TCC 的三大坑):
- 空回滚:Try 没执行(如网络超时),直接收到了 Cancel。Cancel 需要识别出这是空回滚,直接返回成功,不能报错。
- 悬挂:Cancel 先于 Try 到达并执行了,随后 Try 又到达并执行了,导致资源被永久冻结。Try 需要检查是否已经执行过 Cancel,如果是则拒绝执行。
- 幂等性:Confirm 和 Cancel 必须保证幂等,因为网络抖动可能导致它们被重复调用。
3. Saga(长事务)—— 最终一致性
- 原理:将一个长事务拆分为多个短小的本地事务。每个本地事务都有一个对应的补偿操作。
- 正向执行:T1 -> T2 -> T3。
- 如果 T2 失败,则执行 T1 的补偿操作 C1。(注意:不需要执行 T2 的补偿,因为它没成功)。
- 优点:适合业务流程长、跨越多个微服务的场景。
- 缺点:没有隔离性。在事务执行过程中,其他事务可能会看到“中间状态”的数据(脏读)。
4. 本地消息表 / MQ 事务消息 —— 最终一致性(最常用)
这是互联网大厂最常用的方案,用“最终一致性”换取“高可用性”。
方案 A:本地消息表(传统方案)
- 在业务数据库中创建一张“消息表”。
- 在同一个本地事务中,执行业务操作(如扣余额)并往“消息表”插入一条消息。
- 后台有一个定时任务/线程,不断扫描“消息表”,将消息发送到 MQ(如 Kafka/RabbitMQ)。
- 下游服务消费 MQ 消息,执行业务(如加积分),成功后通知上游删除消息表记录。
- 缺点:需要额外建表,定时任务有延迟,耦合了业务库。
方案 B:MQ 事务消息(优雅方案,如 RocketMQ)
- 生产者发送一条半消息(Half Message)到 MQ(此时消费者不可见)。
- MQ 返回半消息成功,生产者执行本地事务(如扣余额)。
- 根据本地事务结果,向 MQ 发送 Commit(提交)或 Rollback(回滚)指令。
- 如果 Commit,消费者就能消费该消息并执行下游业务(如加积分)。
- 如果生产者宕机没发 Commit/Rollback,MQ 会回查本地事务状态。
- 优点:无需本地消息表,解耦,性能高。
5. 最大努力通知 —— 最终一致性
- 原理:主要用于跨企业/跨系统的交互(如微信支付回调)。
- 实现:调用方在操作完成后,尽最大努力通知接收方。如果接收方没响应,调用方会按照阶梯时间(如 15s, 30s, 1m, 2h…)重试多次。同时提供对账接口,接收方可以定时拉取数据进行核对和补偿。
五、 方案对比与实战选型指南
| 解决方案 | 一致性类型 | 性能 | 代码侵入性 | 适用场景 | 代表框架/中间件 |
|---|---|---|---|---|---|
| 2PC (Seata AT) | 强一致 | 低 | 低(无侵入) | 内部系统,对一致性要求极高,并发不高 | Seata |
| TCC | 强/最终 | 较高 | 极高(需写3个接口) | 核心资金链路,对性能和一致性要求都高 | Seata, Hmily |
| Saga | 最终 | 高 | 中 | 业务流程长、参与方多、允许短暂不一致 | Seata, DTM |
| MQ 事务消息 | 最终 | 最高 | 低 | 互联网高并发场景,跨服务异步解耦 | RocketMQ |
| 本地消息表 | 最终 | 高 | 中 | 没有事务消息中间件,或需要强依赖关系型数据库 | 业务代码自研 |
| 最大努力通知 | 最终 | 高 | 低 | 跨公司/跨系统的通知(如支付回调、物流状态) | 业务代码自研 |
💡 架构师选型建议:
- 首选避免:能通过业务设计避免分布式事务是最好的(如:将强关联的业务放在同一个库/同一个服务中)。
- 能用异步就用异步:互联网 90% 的场景不需要强一致性。优先使用MQ 事务消息或本地消息表实现最终一致性。
- 核心资金用 TCC/AT:如果是金融、交易核心链路,必须保证强一致,再考虑使用 Seata 的 AT 或 TCC 模式。
六、 面试避坑与加分项
- 澄清概念:面试官问“分布式事务怎么保证一致性”时,不要只答强一致性。要明确指出:分布式系统基于 BASE 理论,通常追求的是最终一致性。
- Seata AT 模式的原理:如果被问到 Seata,一定要知道 AT 模式的核心是两阶段提交的演变+全局锁+undo_log(回滚日志)。它通过拦截 SQL,在业务执行前记录“前镜像”,执行后记录“后镜像”,回滚时利用“前镜像”还原数据。
- 结合“接口幂等”:分布式事务(尤其是 TCC、MQ 消费、重试机制)中,下游接口必须保证幂等性。你可以主动把“接口幂等”和“分布式事务”结合起来回答,会极大增加面试官的好感。
- 不要过度设计:在面试中,如果业务场景只是普通的电商下单,直接回答“使用 RocketMQ 事务消息保证最终一致性”即可,不要强行上 TCC,会被认为“过度设计、不懂业务”。
编程学习
技术分享
实战经验