Skip to main content

一致性

📅 2026-04-17 ✏️ 2026-04-17 CS
No related notes

1 · 一致性

1.1 · 一致性模型层次

参考: https://jepsen.io/consistency/models 层次图源自 Bailis et al.Viotti & Vukolić

一致性模型定义了系统允许哪些操作历史(histories)。模型 A 蕴含 模型 B (A ⊂ B),意味着 A 更强、更具约束性。

1.1.1 · 全景图

                    Strict Serializable
                    /                \
          Serializable           Linearizable
          /        \                  |
  Snapshot       Repeatable       Sequential
  Isolation        Read               |
       \          /                 Causal
    Monotonic Atomic View         /   |   \
          |                  WFR  PRAM  ...
     Cursor Stability            / |  \
          \                   MR  MW  RYW
        Read Committed
             |
       Read Uncommitted
  • 左侧: 事务(多对象)模型族
  • 右侧: 单对象模型族
  • Strict Serializable 是两族的交汇点,同时满足事务全序 + 实时约束

1.1.2 · 可用性与一致性的权衡

可用性级别可达到的最强模型
完全可用(Total)Read Committed, Monotonic Atomic View 及更弱模型
粘性可用(Sticky)Read Your Writes, PRAM 及更弱模型
不可用Sequential, Linearizable, Serializable 及更强

CAP 定理的精炼: 在异步网络分区下,Sequential 及以上的模型都无法保证全可用


1.2 · 核心模型详解

1.2.1 · Strict Serializable (严格可串行化)#

操作看起来按某个全序执行,且该全序与实时顺序一致。

  • 事务模型 + 多对象 + 实时约束
  • = Serializable 的全序 + Linearizable 的实时约束
  • 可以把整个数据库视为一个 Linearizable 对象
  • 典型系统: Spanner, CockroachDB (默认), FaunaDB

1.2.2 · Serializable (可串行化)#

事务看起来按某个全序执行,但不要求与实时顺序一致

  • 事务 T1 完成写 w 后,T2 开始读 r,r 不一定能看到 w
  • 甚至进程可能看不到自己上一个事务的写(跨事务)
  • 禁止的异象: P0(脏写)、P1(脏读)、P2(不可重复读)、P3(幻读)
  • 典型系统: PostgreSQL (SSI), MySQL (InnoDB with SERIALIZABLE)

1.2.3 · Snapshot Isolation (快照隔离)#

每个事务在一个一致快照上操作;提交时检测写冲突(first-committer-wins)。

  • 不要求全序,只要求偏序
  • 允许 Write Skew: 两个事务读重叠数据、写不相交数据后都提交成功
  • SI ≠ Serializable: SI 弱于 Serializable (在广义定义下)
  • 典型系统: PostgreSQL (默认 REPEATABLE READ 实际是 SI), Oracle, TiDB

1.2.4 · Linearizable (线性一致性)#

单对象操作看起来原子执行,且与实时顺序一致。

  • 如果操作 A 在操作 B 开始前完成,则 B 必须逻辑上在 A 之后生效
  • 单对象模型:作用域取决于系统 (单 key / 单表 / 单库)
  • 跨对象需要 Strict Serializable
  • 典型系统: etcd, ZooKeeper (写操作), 单主 Redis

1.2.5 · Sequential (顺序一致性)#

操作看起来按某个全序执行,且与每个进程内部的顺序一致,但不要求与实时顺序一致。

  • 进程可以读到任意旧的状态
  • 但一旦进程 A 观察到进程 B 的某个操作,A 就不会再观察到 B 更早的状态
  • 典型系统: ZooKeeper (读操作+sync)

1.2.6 · Causal (因果一致性)#

因果相关的操作在所有进程上以相同顺序出现;因果无关的操作可以以不同顺序出现。

  • 粘性可用: 网络分区时,只要客户端粘在同一节点就能继续
  • Real-Time Causal 是异步网络全可用系统中可达到的最强模型
  • 典型系统: MongoDB (causal sessions), Cassandra (轻量事务外)

1.2.7 · Read Committed (已提交读)#

事务只能看到已提交的数据(禁止脏读)。

  • 禁止: P0(脏写), P1(脏读)
  • 允许: P2(不可重复读), P3(幻读)
  • 完全可用
  • 典型系统: PostgreSQL (默认), MySQL (InnoDB 默认), Oracle

1.3 · SCQA 场景分析#

1.3.1 · 场景一: 分布式数据库 — 银行转账

  • S(场景): 用户 A 向用户 B 转账 100 元,涉及两个账户的读写
  • C(冲突): 转账事务中 A 的扣款和 B 的加款必须原子完成;并发转账可能导致余额不一致
  • Q(问题): 如何保证跨账户操作的原子性和正确性?
  • A(回答): 需要 SerializableStrict Serializable。Snapshot Isolation 可能出现 Write Skew (如两个并发事务各检查总余额充足后都扣款,实际透支)。银行场景通常选择 Serializable + 悲观锁 / SSI

1.3.2 · 场景二: 分布式缓存 — 配置更新

  • S(场景): 运维更新配置项后,通过侧信道通知业务服务读取新配置
  • C(冲突): 业务服务可能从副本读到旧配置 (Sequential 允许读旧数据)
  • Q(问题): 如何保证 “写完就能读到”?
  • A(回答): 需要 Linearizable。Sequential 不够,因为它不保证实时约束。可通过共识协议 (Raft/Paxos) 实现线性一致读,或使用 ReadIndex / LeaseRead 优化延迟

1.3.3 · 场景三: 社交网络 — 消息时间线

  • S(场景): 用户 A 发帖 “午饭?“,B 回复 “好”,C 回复 “不行”
  • C(冲突): 不同用户可能看到不同的回复顺序,但不应在问题之前看到回复
  • Q(问题): 如何保证因果关系(先有问题才有回复),同时允许高可用?
  • A(回答): Causal Consistency 是最佳选择。它保证因果相关操作的顺序,允许因果无关操作的顺序不同,且可以做到粘性可用。典型实现: 向量时钟 / 混合逻辑时钟(HLC)

1.3.4 · 场景四: 电商库存 — 秒杀扣减

  • S(场景): 100 个用户并发秒杀 1 件商品
  • C(冲突): Read Committed 下多个事务都读到库存=1,都成功扣减,超卖
  • Q(问题): 如何防止超卖且保持高性能?
  • A(回答): 方案梯度:
    1. Serializable: 最安全但性能差
    2. SI + 显式锁 (SELECT FOR UPDATE): 在快照隔离上通过悲观锁消除 Write Skew
    3. Linearizable 计数器 (Redis DECR): 用单对象线性一致性保证原子扣减,性能最优

1.3.5 · 场景五: 微服务 — 事件驱动架构

  • S(场景): 订单服务写入订单后发布事件,库存服务消费事件扣减库存
  • C(冲突): 订单写入成功但事件发布失败 → 数据不一致;或事件重复消费
  • Q(问题): 跨服务如何保证数据一致性?
  • A(回答): 跨服务无法直接使用分布式事务(性能/可用性代价太大)。通常采用最终一致性:
    • Transactional Outbox: 订单和事件写入同一个本地事务 (保证本地 Serializable)
    • 幂等消费 + 至少一次投递
    • Saga 模式用补偿操作处理失败

1.4 · 如何选择一致性模型

需要跨对象事务的实时全序?  ──是──→  Strict Serializable
  │否
需要跨对象事务全序?  ──是──→  Serializable
  │否
需要单对象实时全序?  ──是──→  Linearizable
  │否
需要全局全序(允许旧读)?  ──是──→  Sequential
  │否
需要因果顺序?  ──是──→  Causal
  │否
需要避免脏读?  ──是──→  Read Committed
  │否
Read Uncommitted (几乎没有保证)

核心权衡: 一致性越强 → 延迟越高、可用性越低、吞吐越低


1.5 · 参考资料

  • Jepsen Consistency Models
  • Herlihy & Wing, Linearizability: A Correctness Condition for Concurrent Objects, 1990
  • Lamport, How to Make a Multiprocessor Computer That Correctly Executes Multiprocess Programs, 1979
  • Berenson et al., A Critique of ANSI SQL Isolation Levels, 1995
  • Bailis et al., Highly Available Transactions: Virtues and Limitations, VLDB 2014
  • Viotti & Vukolić, Consistency in Non-Transactional Distributed Storage Systems, ACM Computing Surveys, 2016
  • Cerone et al., A Framework for Transactional Consistency Models with Atomic Visibility, CONCUR 2015
  • Adya, Weak Consistency: A Generalized Theory and Optimistic Implementations for Distributed Transactions, MIT, 1999