一致性模型
前言
在分布式系统中,一致性模型(consistency model)定义了多个进程或节点对共享数据进行读写操作时应满足的约束与行为保证。一致性模型不仅关乎系统的正确性,也直接影响系统的性能、可用性与可伸缩性。
本文从弱到强介绍主要的一致性模型,并分析它们的权衡、适用场景与在实际系统中的应用。
一致性模型的分类与强弱关系
分布式系统中常见的一致性模型可按强度从强到弱排序如下:
$$\text{线性一致性} \supset \text{顺序一致性} \supset \text{因果一致性} \supset \text{会话一致性} \supset \text{最终一致性}$$
- 强一致性:对所有客户端而言,系统展现出单一且不可变的视图,任何时刻读取数据都能得到最新的已提交值。
- 弱一致性:不保证实时可见性,允许短暂的不一致,但在某些条件或时间范围后达成一致。
线性一致性(Linearizability)
定义
线性一致性是最强的一致性保证。它要求:
- 每个操作(读或写)在系统中的执行时间点位于其调用时间与返回时间之间(happens-before 偏序)。
- 所有操作按全局时间顺序串行执行,就像在单一服务器上运行一样。
- 任何读操作都能看到最近提交的所有写操作的结果。
换句话说,从外部观察者的视角,分布式系统表现得像一个没有复制的系统。
正式描述
对于任意两个操作 $op_1$ 和 $op_2$,若 $op_1$ 的返回时间早于 $op_2$ 的调用时间,则存在一个全局时间顺序使得 $op_1$ 先于 $op_2$ 被执行。
例子
1 | |
在线性一致的系统中,客户端 B 的读操作必然返回客户端 A 写入的最新值 1。
典型实现
- 中央锁定系统:所有写操作通过单一节点的锁控制。
- 主从同步复制:写操作必须等待主从都确认。
- Paxos/Raft(强模式):使用强一致共识协议,确保所有副本保持同步。
成本
线性一致性要求强同步协调,导致:
- 高延迟(需等待跨节点确认)。
- 低吞吐(并发受限)。
- 分区场景下无法保证可用性(CAP 定理)。
顺序一致性(Sequential Consistency)
定义
顺序一致性是由 Lamport 提出的。它要求:
- 所有操作结果必须与某个串行执行顺序一致。
- 各个进程内部的操作顺序保持原序(程序顺序)。
- 但不要求全局时间顺序,即不同进程的操作可能交错。
关键区别:顺序一致性比线性一致性弱,因为它只关心逻辑上的串行等价性,不绑定到实时的发生顺序。
正式描述
存在一个全局执行顺序 $\sigma$,使得:
- 每个进程 $i$ 的操作在 $\sigma$ 中的相对顺序与程序顺序相同。
- 所有操作按 $\sigma$ 串行执行的结果与实际系统的行为一致。
例子
1 | |
在顺序一致的系统中,虽然进程 C 能看到进程 B 的写(因为它发生在 C 的读之前),但不一定看到进程 A 的写(因为没有明确的因果顺序)。
与线性一致性的区别
| 特性 | 线性一致性 | 顺序一致性 |
|---|---|---|
| 绑定实时顺序 | 是 | 否 |
| 程序顺序 | 是 | 是 |
| 性能 | 低 | 高于线性一致性 |
| 难度 | 难 | 相对容易 |
典型实现
- GPU 内存模型(某些时期)。
- 弱同步复制系统:没有全局时间戳,只保证本地顺序。
因果一致性(Causal Consistency)
定义
因果一致性由 Lamport 的逻辑时钟与向量时钟理论驱动。它要求:
- 存在因果关系的操作必须按该顺序被所有进程看到。
- 没有因果关系的操作(并发操作)可以被不同进程以不同顺序观察。
核心思想:尊重因果依赖,允许并发无关操作的乱序。
正式描述
对任意两个操作 $op_1$ 和 $op_2$:
- 若 $op_1 \to op_2$(因果关系),则所有进程必须先看到 $op_1$ 再看到 $op_2$。
- 若 $op_1 \parallel op_2$(并发),则不同进程可以以不同顺序观察它们。
例子
1 | |
但若两个写操作并发(没有因果关系),不同进程可能看到不同的相对顺序。
实现技术
- 向量时钟(Vector Clock):每个进程维护向量时戳,追踪因果依赖。
- 版本向量(Version Vector):记录各进程对数据的修改版本。
- Dynamo/Cassandra 中的版本向量机制。
与顺序一致性的区别
| 特性 | 顺序一致性 | 因果一致性 |
|---|---|---|
| 全局串行顺序 | 是 | 否 |
| 因果关系保护 | 是 | 是 |
| 并发操作乱序 | 不允许 | 允许 |
| 性能 | 中等 | 更高 |
会话一致性(Session Consistency)
定义
会话一致性在客户端维度加入时间限制,要求:
- 在同一会话内,读写操作满足顺序一致性或线性一致性。
- 不同会话的操作可能看到不同版本的数据。
- 进程与同一副本通信时效果最好(减少远程副本间的同步成本)。
关键概念
- 会话(Session):由单一客户端与系统的交互序列定义,通常绑定到用户登录或连接 ID。
- 会话令牌(Session Token):服务端返回给客户端,用于追踪其因果历史。
例子
1 | |
实现思路
- 会话令牌法:客户端携带上一次操作的时间戳或版本号,服务端据此确保返回至少包含该版本或更新的数据。
- 粘性会话(Sticky Session):客户端尽量路由到同一副本,减少跨副本同步需求。
应用场景
- Web 应用:用户登录后对自己的数据修改立即可见。
- 移动应用:用户在设备上的操作顺序保证,但其他用户可能见到延迟。
最终一致性(Eventual Consistency)
定义
最终一致性是最弱的一致性保证,仅承诺:
- 如果一段时间内没有新的写操作,所有读操作最终会返回同一值。
- 不保证读操作何时能看到写操作的结果。
- 不同副本间可能暂时不一致。
这是一个活性保证而非安全保证。
正式描述
对于任一数据项 $x$,定义最后一次写操作为 $W_{\text{last}}$。如果在 $t$ 时刻后没有新的写操作,则存在延迟 $\Delta$,使得所有在 $t + \Delta$ 之后的读操作都能观察到 $W_{\text{last}}$ 的结果。
例子(DNS 与 缓存)
1 | |
变体
- 强最终一致性(Strong Eventual Consistency, SEC):要求无冲突的操作最终收敛到同一结果,常用于 CRDT(无冲突副本数据类型)。
- 读你所写一致性(Read-Your-Write):客户端的读操作至少能看到自己之前的写,是最终一致性的改进。
应用场景
- 社交媒体:点赞、转发等统计可能不实时准确。
- 分布式缓存(Redis、Memcached)。
- DNS 系统。
- NoSQL 数据库(Cassandra、DynamoDB 的默认配置)。
优点与成本
| 方面 | 优点 | 成本 |
|---|---|---|
| 延迟 | 极低 | 用户可能见到过时数据 |
| 吞吐 | 极高 | 需处理冲突与版本管理 |
| 可用性 | 高 | 数据暂时不一致 |
| 复杂性 | 低(理论) | 高(工程应对冲突) |
读写一致性(Read-Your-Write Consistency)
定义
读写一致性是对最终一致性的改进,承诺:
- 客户端对数据的写操作完成后,该客户端的后续读操作必然看到该写的结果。
- 其他客户端的读取可能见到旧版本。
实现方式
- 客户端记忆:记录该客户端对每个数据项的最后写时间戳,确保读操作从包含该时间戳或更新版本的副本获取。
- 会话记号:类似会话一致性,使用版本向量或时间戳。
例子
1 | |
与会话一致性的关系
读写一致性是会话一致性的特例,侧重于用户对自己操作的可见性保证。
一致性模型的权衡与选择
强度 vs 性能权衡
| 模型 | 一致性强度 | 延迟 | 吞吐 | 可用性 |
|---|---|---|---|---|
| 线性一致性 | 极强 | 高 | 低 | 低(分区不可用) |
| 顺序一致性 | 强 | 中 | 中 | 中 |
| 因果一致性 | 中 | 低 | 高 | 高 |
| 会话一致性 | 弱 | 低 | 高 | 高 |
| 最终一致性 | 极弱 | 极低 | 极高 | 极高 |
选择指南
- 金融交易系统:线性一致性或强顺序一致性(必须正确)。
- 分布式缓存:因果一致性或会话一致性(可接受延迟,但不能太离谱)。
- 社交媒体、推荐系统:最终一致性(用户容忍短期不准确)。
- 用户个人资料:读写一致性(用户对自己的改动立即可见)。
CAP 定理的关联
- 线性一致性 对应 CAP 中的强一致性,不能同时保证分区下的可用性。
- 最终一致性 选择可用性与分区容忍性,放弃一致性。
- 实际系统通常针对不同数据类型或操作采用混合策略。
工程启示与最佳实践
分层一致性:为不同数据层级应用不同模型。
- 关键数据(账户余额):线性一致性。
- 热数据(缓存):因果或最终一致性。
- 元数据(计数器):最终一致性。
监控与冲突解决:使用版本向量或时间戳检测并解决冲突。
用户感知:向应用层和用户明确通信可能的不一致性,例如”点赞数可能延迟更新”。
混合策略:同一系统中对不同数据采用不同一致性保证(例如 Spanner 对关键事务用强一致性,对分析查询用最终一致性)。
参考资料
- Lamport, L. (1979). How to Make a Multiprocessor Computer That Correctly Executes Multiprocess Programs. IEEE Transactions on Computers, 28(9), 690–691.
- Attiya, H., Welch, J. L. (2004). Distributed Computing: Fundamentals, Simulations and Advanced Topics. Wiley.
- Kleppmann, M. (2017). Designing Data-Intensive Applications. O’Reilly.
- DeCandia, G., et al. (2007). Dynamo: Amazon’s Highly Available Key-value Store. SOSP.
- Corbett, J. C., et al. (2012). Spanner: Google’s Globally Distributed Database. OSDI.