一致性模型详解
1.1 前言与分层
一致性模型定义了系统对读写操作的可见性保证,是程序员理解分布式系统行为的核心工具。需要注意区分两个不同层面的”一致性”:
| 层面 | 含义 | 常见术语 |
|---|---|---|
| 数据一致性 | 副本之间的数据是否相同 | MySQL 主从、Redis Cluster |
| 事务隔离性 | 并发事务之间的可见性 | ACID 中的 I(Isolation) |
| 一致性模型 | 对读写操作顺序的承诺 | 线性一致、因果一致 |
本章讨论的是第三层——读写操作顺序的承诺。
1.2 强一致性 / 弱一致性 / 最终一致性
强一致性(Strong Consistency / Linearizability):又称线性一致性,是所有一致性模型中最严格的一种。它要求:
- 任意时刻,所有节点看到的数据完全相同
- 写操作完成后,所有后续的读操作都必须能看到该写入(或更新的写入)
- 所有操作看起来就像按照某种全局顺序执行
线性一致性对系统性能影响极大,通常需要同步复制和全局时钟。Paxos/Raft 等共识算法是实现线性一致性的核心技术,详见本页第七章「分布式一致性/共识算法」。
弱一致性(Weak Consistency):不保证读操作能立即看到最新写入,系统只保证”最终”会一致。弱一致性没有严格定义,实际上它是除强一致性之外所有模型的统称。
最终一致性(Eventual Consistency):弱一致性的一种具体形式,保证如果没有新的写操作,最终所有副本都会收敛到相同的值。它并不承诺收敛的时间。DNS 系统是最终一致性的经典实例。
1.3 顺序一致性(Sequential Consistency)
顺序一致性由 Lamport 定义:操作结果与所有处理器按某种顺序执行各自主机上的操作一致,且同一处理器上的操作保持程序顺序。顺序一致性强于因果一致性,但弱于线性一致性——它不要求操作的全局顺序与物理时间一致。
1 | // 线性一致性与顺序一致性的区别 |
1.4 因果一致性(Causal Consistency)
因果一致性保证:有因果关系的操作在所有节点上看到的顺序一致,但无因果关系的并发操作可以任意排序。因果关系定义为”happens-before”关系——如果操作 A 可能影响操作 B,则 A happens-before B。
微信朋友圈的评论回复就是典型的因果一致性场景:回复必须出现在被回复的评论之后。但两个互不相关的评论(没有因果关系)可以在不同用户的手机上以不同顺序显示。
实现因果一致性通常需要向量时钟来追踪操作之间的 happens-before 关系。
1.5 读己之写(Read Your Writes)
读己之写:保证用户总能看到自己之前的写入。这是一种非常实用的一致性保证——用户更新了头像后立即刷新页面,必须看到新头像,不能看到旧头像。
实现方式:
- 将写入操作路由到主节点,读取也路由到同一节点
- 使用 version 或 timestamp 确保从库追上了写操作的位点
- 对同一个用户的操作路由到同一个分区/节点
1.6 单调读(Monotonic Reads)
单调读:保证客户端不会读到比之前更旧的数据。即如果已经读到了版本 N,后续读取的版本必须 ≥ N。
典型问题场景:用户看了朋友圈,切换到后台后再切回——如果不保证单调读,可能出现”一条动态消失了”的现象。这是因为两次读请求被路由到了不同步程度不同的节点。
实现方式:基于用户 ID 做会话粘滞,将同一用户的路由到同一节点。
1.7 应用场景速查
| 一致性模型 | 严格度 | 典型应用 |
|---|---|---|
| 线性一致性 | 最高 | 分布式锁、选主、交易扣款 |
| 顺序一致性 | 高 | 分布式文件系统元数据 |
| 因果一致性 | 中 | 社交评论、协同编辑 |
| 读己之写 | 中 | 用户个人信息修改 |
| 单调读 | 中低 | 时间线类应用 |
| 最终一致性 | 低 | DNS、CDN、缓存同步 |