1 问题背景
讨论分布式事务时,“一致性”几乎是绕不开的词。但这个词也最容易制造误解:有人把分布式事务等同于强一致性,有人把 Raft 也归到“一致性方案”里,于是 2PC、TCC、SAGA、Raft、CAP、BASE 被放在同一层讨论。
这里真正需要先厘清的是:一致性不是一个单一概念,而是一组发生在不同层次的问题。
分布式事务通常关注的是业务操作之间的正确性,例如“订单创建、库存扣减、余额扣款”要么同时成立,要么不能留下半成品。Raft 关注的是同一份状态在多个副本之间如何复制、提交和恢复,例如一个配置项、一个分片日志、一个协调器元数据在多台机器上如何保持同一顺序。
它们都在解决“一致”,但不是同一个“一致”。
2 核心结论
可以先给出一个判断:
分布式事务解决的是跨资源、跨操作的业务一致性;Raft 解决的是同一状态在多个副本之间的复制一致性。
这两类一致性常常同时出现在一个系统里,但职责不同:
- 分布式事务处理“不同数据之间的关系是否正确”。
- 共识协议处理“同一份数据的多个副本是否按相同顺序达成相同结果”。
- CAP 中的 C、ACID 中的 C、线性一致性、最终一致性并不是同一个概念,只是中文里都被翻译成“一致性”。
如果把这些概念混在一起,工程判断很容易失焦。比如,一个系统使用了 Raft,并不等于它天然支持跨表、跨库、跨服务事务;一个系统使用了 TCC,也不意味着它底层副本复制一定具备线性一致性。
3 “一致性”至少要分三层看
3.1 业务约束一致性
业务约束一致性关注的是业务事实是否成立。
例如转账场景中,A 账户扣 100 元,B 账户加 100 元,这两个动作不是两个孤立写入,而是同一个业务事实的两个侧面。如果 A 已扣款、B 未到账,系统就进入了业务不一致状态。
这类问题通常由事务机制、幂等设计、补偿流程、对账任务共同处理。它关心的不是某个副本是否与另一个副本相同,而是多个业务对象之间的约束是否被破坏。
在单体数据库里,本地事务可以用 ACID 保证这类约束。但当订单、库存、支付、积分被拆到不同服务和不同数据库之后,本地事务边界被打碎,业务约束就需要跨资源协调。这就是分布式事务出现的根本原因。
3.2 事务原子性一致
很多时候我们说“分布式事务保证一致性”,更准确地说,是它试图保证一组分布式操作具备类似事务的原子效果:要么整体成功,要么整体失败,至少最终不要长期停留在中间状态。
2PC、TCC、SAGA、本地消息表、事务消息都属于这一层的不同方案。它们的差异不只是技术实现不同,更是对一致性强度、可用性、性能和业务侵入程度的取舍不同。
- 2PC 更接近刚性事务,强调统一提交和统一回滚,但对参与者、协调者和资源锁定要求更高。
- TCC 把业务拆成 Try、Confirm、Cancel 三段,用业务预留和补偿换取可控的一致性。
- SAGA 把长事务拆成多个本地事务,每一步失败后通过补偿动作向前修复,而不是回到数据库层面的原子回滚。
- 本地消息表和事务消息常用于异步最终一致,通过“本地事务 + 可靠消息 + 消费幂等”降低跨服务同步提交的代价。
这些方案都不是免费午餐。越想获得接近本地事务的体验,就越要付出锁等待、协议复杂度、故障恢复和吞吐下降的成本。
3.3 复制一致性
复制一致性关注的是另一类问题:同一个逻辑状态有多个物理副本时,如何保证它们不会各说各话。
例如一个配置中心有 3 个节点,客户端写入 feature_x=true。如果节点 A 认为写入成功,节点 B 还停留在旧值,节点 C 又接受了另一个写入,系统就需要一个规则来决定:谁是主,哪个写入先发生,什么条件下算提交,故障后如何恢复。
Raft、Paxos、ZAB 这类共识协议解决的就是这个问题。它们通常通过日志复制、任期、选主、多数派提交等机制,让多个副本对一串操作顺序达成一致。
Raft 的核心并不是“回滚业务”,而是让多个节点复制同一条日志,并在多数派确认后提交。一旦日志提交,它代表集群已经对这个顺序形成共识。后续如果要修正,只能追加新的操作,而不是像事务补偿那样执行一个业务 Cancel。
4 Raft 解决的是哪一类问题
Raft 的典型模型是复制状态机。
客户端不是随便写任意节点,而是把写请求交给 Leader。Leader 把请求转成日志条目,复制给 Follower。当多数派确认这条日志后,Leader 才把它提交,并把日志应用到状态机。只要日志顺序一致,所有节点按同样顺序执行同样操作,就会得到同样结果。
这个过程可以简化为:
sequenceDiagram
participant Client as Client
participant Leader as Raft Leader
participant F1 as Follower 1
participant F2 as Follower 2
participant SM as State Machine
Client->>Leader: 写请求
Leader->>Leader: 追加本地日志
Leader->>F1: 复制日志
Leader->>F2: 复制日志
F1-->>Leader: ACK
F2-->>Leader: ACK
Leader->>Leader: 多数派确认,提交日志
Leader->>SM: 应用到状态机
Leader-->>Client: 返回成功这张图里没有订单、库存、支付,也没有 Try、Confirm、Cancel。Raft 不关心一笔业务交易应该如何补偿,它关心的是“集群对某个操作顺序是否达成一致”。
因此,Raft 更像是构建可靠状态服务的基础设施。etcd、Consul、许多分布式数据库的元数据管理或分片复制,都会用共识协议保证内部状态不因节点故障而分裂。
5 分布式事务解决的是哪一类问题
分布式事务站在更靠近业务的一层。它面对的问题是:一个业务动作需要修改多个独立资源,而这些资源没有天然共享的本地事务边界。
以电商下单为例,一次下单可能涉及:
- 订单服务创建订单。
- 库存服务扣减或冻结库存。
- 支付服务扣减余额或发起支付。
- 优惠券服务核销优惠。
- 积分服务增加或冻结积分。
如果这些动作落在不同数据库中,任何一步失败都可能造成中间态。分布式事务协议要解决的,不是“订单库的三个副本是否一致”,而是“订单、库存、支付、优惠券这些业务对象之间是否形成一个完整事实”。
这里可以用一个简化的 TCC 过程理解:
flowchart TD
A[发起下单] --> B[Try: 创建待确认订单]
B --> C[Try: 冻结库存]
C --> D[Try: 冻结余额或支付额度]
D --> E{所有 Try 成功?}
E -->|是| F[Confirm: 确认订单、扣减库存、完成支付]
E -->|否| G[Cancel: 取消订单、释放库存、释放额度]TCC 的重点是把业务资源显式拆成“预留”和“确认/取消”。它没有把分布式世界伪装成本地事务,而是要求业务系统承认中间状态,并把这些中间状态设计成可恢复、可幂等、可补偿。
这也是柔性事务和刚性事务的关键差异:柔性事务不追求每一瞬间都完全一致,而是要求系统具备从临时不一致状态收敛到正确状态的能力。
6 二者如何组合在一个系统里
一个成熟的分布式系统往往同时使用复制一致性和事务一致性,只是它们位于不同层次。
以分布式数据库为例,可以粗略拆成两层:

底层每个分片内部可能通过 Raft 保证多副本复制一致:写入哪个日志、谁是 Leader、哪些副本确认后算提交。
上层分布式事务层负责协调跨分片写入:一个事务同时修改分片 A 和分片 B 时,如何保证它们作为一个整体提交或中止。
换句话说,Raft 可以保证“每个参与者内部可靠”,但不能自动保证“多个参与者之间原子”。分布式事务可以协调多个参与者,但它通常也需要依赖可靠存储、可靠协调器、可靠日志来记录事务状态。Raft 可能正是这些可靠组件的底层实现。
7 CAP、BASE 与最终一致性的位置
谈一致性时还会遇到 CAP 和 BASE。它们不是具体协议,而是帮助我们理解分布式系统取舍的理论框架。
CAP 讨论的是在网络分区发生时,系统无法同时保证一致性和可用性。这里的一致性更接近复制一致性,通常指客户端读到的结果像来自同一个最新副本。
BASE 则是一种工程取向:基本可用、软状态、最终一致。它承认在大规模分布式系统里,很多业务没必要为所有操作支付强一致成本,可以接受短时间中间态,然后通过消息、重试、补偿、对账让状态最终收敛。
这并不是说最终一致性更低级。恰恰相反,很多高并发业务系统会主动选择最终一致性,因为它更符合业务事实:
- 发帖后粉丝流延迟几秒可接受。
- 支付成功后积分稍后到账可接受。
- 订单状态和物流状态短暂不同步可接受。
- 账户余额、资金划拨等核心资产场景通常不可接受长期不一致。
一致性强度不是越高越好,而是要和业务风险匹配。错误地使用强一致方案,可能把系统拖入低吞吐、高耦合、难恢复的状态;错误地使用最终一致方案,则可能把资金、库存、权益类数据暴露在不可控风险中。
8 工程选型时先问哪些问题
在实际设计中,不应该先问“要不要上分布式事务”,而应该先拆问题。
第一,业务是否真的要求原子一致。
- 如果失败后可以异步补偿,用户可以等待状态收敛,通常不必直接选择刚性分布式事务。
第二,中间状态是否可见、可解释、可恢复。
- 柔性事务不是“失败了以后再说”,而是要提前定义清楚待确认、处理中、补偿中、失败待人工处理等状态。
第三,操作是否具备幂等性。
- 消息重试、事务恢复、Cancel/Confirm 重入都会反复触发同一业务动作。如果没有幂等约束,最终一致性会变成重复扣款、重复发货、重复发券。
第四,是否存在全局唯一顺序要求。
- 如果问题是多副本日志顺序、Leader 选举、配置变更、分布式锁,那么 Raft 这类共识协议更接近问题本身。如果问题是跨服务业务事实完整性,分布式事务或可靠消息更合适。
第五,故障恢复路径是否可观测。
- 分布式事务真正难的往往不是正常提交,而是协调器宕机、参与者超时、消息重复、网络分区、Confirm 成功但响应丢失、Cancel 失败等异常路径。没有事务日志、状态机、告警、对账和人工兜底,一致性方案很难在生产环境成立。
9 常见误区
9.1 用了 Raft 就有分布式事务
Raft 能让一个集群内部对日志顺序达成共识,但它不会自动理解你的订单、库存、支付之间的业务约束。它可以成为事务协调器、元数据服务、锁服务的底层基础,但不能替代业务事务协议。
9.2 用了消息队列就实现了最终一致性
消息队列只解决“事件能否可靠传递”的一部分问题。最终一致还需要本地事务与消息发送的原子关系、消费者幂等、失败重试、死信处理、对账修复和状态可追踪。只把请求扔进 MQ,并不等于系统具备一致性收敛能力。
9.3 强一致一定优于最终一致
强一致提供更简单的外部语义,但成本很高。跨服务同步等待会放大尾延迟,资源锁定会降低吞吐,协调器故障会扩大影响面。对低风险、高吞吐、可补偿的业务,最终一致往往是更合理的工程选择。
9.4 最终一致就是可以不一致
最终一致不是放弃一致性,而是把一致性的达成从同步路径移到异步路径。它要求系统最终必须收敛,并且收敛过程要有明确的状态、重试、补偿和兜底机制。
10 总结
分布式事务、Raft 和一致性之间的关系,可以收束为三句话:
第一,一致性是目标集合,不是单一技术名词。讨论一致性时,要先说明是业务约束一致、事务原子一致,还是副本复制一致。
第二,分布式事务解决跨资源业务操作的一致性问题。它关注的是多个独立资源之间是否形成一个完整业务事实,常见方案包括 2PC、TCC、SAGA、本地消息表和事务消息。
第三,Raft 解决多副本状态复制的一致性问题。它通过 Leader、日志复制、多数派提交和选主机制,让多个节点对同一串操作顺序达成共识。
在真实系统里,它们不是互斥关系,而是分层协作关系。底层可以用 Raft 保证状态服务、协调器或数据分片的高可用和复制一致;上层再用分布式事务、可靠消息或补偿机制保证业务事实最终成立。
理解了这个分层,再讨论 CAP、BASE、强一致、最终一致,就不会停留在术语混战里。
工程设计真正要回答的是:这条业务链路允许多长时间的不一致,失败后能否补偿,状态是否可观测,风险是否可兜底,以及为了更强的一致性,我们愿意付出多少性能和复杂度成本。