计算机中的各种“一致性”

计算机中的各种“一致性”

Written with Claude.

0x00 引子

我曾希望我的多活应用在进行配置更新时,能做到对外的一致性——某一时刻之后,所有节点均以新配置提供服务;如果有节点更新失败,则所有节点都继续沿用旧配置。我以为这只是一个系统的某种“特性”,实现起来应该不难。

查阅资料后,我发现这个需求牵扯到三个相互独立却又彼此依赖的概念:

  • 线性一致性(Linearizability):保证外部观察者看到的行为符合“某时刻切换”的语义
  • 原子提交(Atomic Commit):保证所有节点要么全部更新成功,要么全部回滚
  • 共识(Consensus):让分布式节点对“是否提交”达成统一决定

这让我意识到,“一致性”在计算机不同领域里的含义差异极大。本文试图梳理这些含义,厘清它们之间的关系。


0x01 一致性在哪里出现?

“一致性”这个词在计算机领域至少出现在以下四个语境中:

1. 硬件:CPU 缓存一致性

现代多核 CPU 中,每个核心都有自己的 L1/L2 缓存。当多个核心同时读写同一内存地址时,如何保证各核心缓存中的数据不产生矛盾?这是**缓存一致性(Cache Coherence)**问题,通常由硬件协议(如 MESI)解决。

MESI 协议为每个缓存行定义四种状态:Modified、Exclusive、Shared、Invalid。当一个核心写入数据时,协议会将其他核心中该行的状态标记为 Invalid,从而强制它们下次从内存或发出写请求的核心处重新获取数据。

与缓存一致性密切相关的是**内存序(Memory Ordering)**问题。即使缓存一致,编译器和CPU 的乱序执行仍可能导致另一个线程看到“不合逻辑”的操作顺序。这催生了各语言的内存模型规范(JMM、Go Memory Model、C++ Memory Model),以及 volatilefenceatomic 等语言原语。

伪共享(False Sharing)是一个经典陷阱:两个不相关的变量恰好位于同一缓存行,导致频繁的缓存失效,严重影响性能。

延伸阅读


2. 数据库:ACID 中的 C

数据库事务的 ACID 中,C 代表 Consistency(一致性),但这里的含义与分布式系统中的“一致性”完全不同。ACID 的 C 是指:事务执行前后,数据库必须满足所有预定义的业务约束(如账户余额不能为负、外键引用必须有效)。

换句话说,这里的“一致性”是业务语义层面的正确性,由应用开发者定义,由数据库的AID(原子性、隔离性、持久性)保证。C 不是独立的机制,而是 AID 的结果。

单机事务的隔离性本身又是一个复杂的话题,涉及不同的隔离级别(读未提交、读已提交、可重复读、串行化)和实现机制(锁、MVCC)。不同的隔离级别对应不同的并发异常(脏读、不可重复读、幻读),这也可以视为一种“一致性模型”。


3. 分布式系统:一致性模型与共识

这是本文的重点。分布式系统中的“一致性”通常指多个节点在读写共享数据时表现出的行为规范,有一个从强到弱的谱系:

  • 线性一致性(Linearizability):最强。所有操作看起来像是在某个单一时间点上原子完成的,且顺序与真实时间吻合。代价是需要协调,延迟高。
  • 顺序一致性(Sequential Consistency):所有操作有一个一致的全局顺序,但这个顺序不必与真实时间完全吻合。
  • 因果一致性(Causal Consistency):只要求有因果关系的操作保持顺序。
  • 最终一致性(Eventual Consistency):最弱。只保证在没有新写入的情况下,所有副本最终会收敛到相同值。DNS 是经典例子。

一致性强度越强,可用性和性能的代价越大,这正是 CAP 定理PACELC 模型所描述的权衡。

与一致性模型相关但不同的是**共识(Consensus)**问题:让分布式节点对某个值(如“是否提交事务”、“谁是 leader”)达成统一决定,且即使部分节点宕机也能正常工作。Paxos 和Raft 是解决共识问题的两种经典算法。

Paxos 先提出(理论更优雅),Raft 后来(工程更易理解)。两者的核心思想一致:通过多数派(quorum)投票来容忍少数节点故障。

延伸阅读


4. 分布式数据库:两种“一致性”的交汇

分布式数据库(如 TiDB、CockroachDB、Spanner)同时面对上述两个维度的问题:

  • 数据库角度,它要提供 ACID 事务语义
  • 分布式角度,它需要在多个节点间保证线性一致性或其他一致性模型

分布式事务就是在这个交汇处诞生的:如何让跨节点的事务也满足 ACID?常见方案有:

  • 2PC(两阶段提交):经典方案,有协调者单点问题,且阻塞
  • Saga:将长事务拆解为一系列可补偿的短事务,适合业务层面的最终一致性
  • Calvin / Deterministic DB:通过提前确定事务顺序(借助共识算法)来避免传统锁

延伸阅读


5. 应用层:缓存与数据库的一致性

这是最贴近日常业务开发的场景。当应用同时使用 Redis 等缓存和 MySQL 等数据库时,如何保证两者数据不出现“缓存有、DB 没有”或“缓存旧、DB 新”的不一致?

常见策略:

  • Cache Aside(旁路缓存):先更新 DB,再删除(而非更新)缓存,利用缓存重建保证最终一致
  • Write Through:同步写穿,保证强一致,但写延迟高
  • Write Behind(Write Back):异步写回,性能好,但有数据丢失风险

在并发场景下,即使是“先更新 DB 再删除缓存”也存在极端情况下的竞态,需要结合版本号、消息队列(Canal + MQ)等方案进一步保障。


0x02 几个关键问题

Q:ACID 的 C 和分布式的一致性有什么关系?

几乎没有。ACID 的 C 是业务正确性,由单机事务机制保证;分布式的一致性是多副本数据的可见性规范,由复制协议保证。两者用了同一个词,但含义完全不同。DDIA 的作者 Kleppmann认为 ACID 的 C 放在这里其实是“凑首字母”。

Q:共识和一致性是同一回事吗?

不是。一致性(Consistency)描述系统对外呈现的行为规范;共识(Consensus)是分布式节点之间达成协议的过程。共识是实现强一致性的一种手段,但不是唯一手段(如Zab、Viewstamped Replication 也可以实现线性一致性)。

Q:线性一致性和串行化(Serializability)有什么区别?

串行化是数据库事务隔离的最强级别(操作顺序等价于某种串行执行);线性一致性是分布式系统单个操作的实时性要求。两者结合称为严格串行化(Strict Serializability),是分布式数据库能提供的最强保证,也是代价最高的。


0x03 总结

语境 术语 核心问题 典型机制
CPU 硬件 缓存一致性 多核缓存数据矛盾 MESI 协议
编程语言 内存模型 指令重排序的可见范围 fence / atomic
单机数据库 ACID-C 业务约束不被破坏 约束检查
分布式系统 一致性模型 多副本数据的可见规范 复制协议
分布式协调 共识 节点间统一决策 Paxos / Raft
分布式数据库 分布式事务 跨节点 ACID 2PC / Saga
应用缓存 缓存一致性 缓存与 DB 数据同步 Cache Aside

参考资料


最后修改于 2026-03-22