一致性模型详解

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
2
3
4
5
6
7
8
// 线性一致性与顺序一致性的区别
// 线性一致性:操作顺序 = 物理时间顺序
P1: write(x=1) write(x=2)
P2: read(x)→2

// 顺序一致性:允许重排,但保证单处理器的程序顺序
P1: write(x=1) write(x=2)
P2: read(x)→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、缓存同步