一致性这个中文术语在计算机的不同领域有不同的含义,对应的英文术语也是不一样的:Consistency, Consensus, Coherence等等。
就这三个术语而言,其区别可以简单的如下理解:
不过Consistency含义比较复杂,一些理解也都有争议,读者如有不同看法,欢迎留言讨论。
本文要说分布式系统一致性模型,是指Consistency Model。实际上,Consistency Model的问题不只是出现在分布式数据库等这类分布式系统,在多核下内存模型中也有类似的问题,或者说,这里的分布式系统是Lamport在分布式时钟事件序论文中描述的广义的分布式系统。有些一致性名字,如cache consistency,虽然是针对多核内存模型,但是同样可以对应到分布式存储系统中来。
需要说明的是,本文描述的只是比较简单的模型,即使在最近几年,都一直有学术论文就其中某些一致性展开了进一步的研究。读者如有更多相关资料,欢迎在评论中补充。如本文中描述有错误的地方,烦请读者批评指出。
参考了若干材料,基本都是从两个角度看一致性模型:data-centric consistency model和client-centric(user-centric) models。前者是从系统中数据的状态角度出发探讨一致性模型,后者是从client的角度出发探讨的。我个人觉得前者会更重要一些,适用性也更广泛一些,梳理起来难度也更大些。后者引入了session的概念,角度也有局限。本文主要着眼于前者的解释。
Data-Centric Consistency Models
1. Strict Consistency
任何读操作都能读取到最新的修改,换句话说,要求任何写操作都立刻同步到其他所有进程。这里强调的是绝对时钟上的立刻同步,而任何信息的同步都需要延迟,因此在分布式系统下无法严格实现。
不考虑系统的性能,一种可能的替代办法就是:让整个系统只在单个节点的单个线程运行。或者,在系统粒度上,读写操作操作被读写锁保护起来(另一种形式的单点运行)。
可以看到,在时间轴上,一旦有进程写了x,则其他进程立刻就能读到最新的值。
2. Sequential Consistency
Sequential Consistency摒弃了绝对时钟的要求,而是使用分布式逻辑时钟。Sequential Consistency将所有事件定一个全序,且单个进程内的多个操作满足program order,每个进程看到的都是这个全序。
Lamport给出了一种实现方式:首先,每个进程内的读写操作按顺序issue;其次,每个进程的每个操作都被全局的一个FIFO队列定序。在这种实现方式下,同一个进程的几个操作在FIFO的顺序一定是program order,不同进程的两个操作顺序是不确定的,可能违背两个操作的绝对时间序。
如图,w1,w2,w3实际发生顺序是1<2<3,但是被定序后,可能是3<2<1。只要每个进程看到的定序结果是一致的,系统就是满足Sequential Consistency的。
3. Linearizability (atomic consistency) & External Consistency
Linearizability比Sequential Consistency更严格。Linearizability在Sequential Consistency的约束基础上,额外增加了如下的约束:如果操作A开始时间晚于B结束时间,则在Sequential Consistency定序时,要求B在A前。
直观上,Linearizability要求的是,操作生效时间是在操作发起到操作返回之间的某个时刻。
External Consistency这个概念好像只有数据库有(数据库可以视为多个并发事务操作共享的一组数据元素的分布式系统),它的定义方式跟Serializable是相似的。Serializable定义的一个合法的全序同时满足:各自事务内的读写操作的偏序、若干事务的偏序。External Consistency定义的一个合法全序同时满足:各自事务内的读写操作的偏序、不同事务Begin和Commit事件产生的偏序。
如果一个事务T2的开始事件晚于T1的结束时间,那么要求读取时,要么只读取到T1的结果,要么读取到T1和T2的结果,不允许出现只读取到T2的结果。在Google Spanner支持external consistency,感兴趣的读者可以参考Spanner解读了解Spanner是如何实现支持这个一致性的。
查阅了若干资料均表示,External Consistency与Linearizability是等价的,只是使用的场景不一样(注:Google在官方博客有段文字说明:Cloud Spanner provides external consistency, which is a stronger property than linearizability, because linearizability does not say anything about the behavior of transactions。我理解算是另外一种角度吧)。External Consistency名字是怎么来的?我猜测:如果一个系统的通信完全只依赖系统内部数据的读写,那么external consistency和sequential consistency是等价的。但是当系统的通信包括了外部的某些方式,即系统内部数据的关系实际还依赖系统外部定义的偏序关系,那么,二者就不再等价,external consistency就能排除sequential consistency下产生的异常现象Anomalous Behaviour。我个人认为,这个一致性可以视为带外部因果关系的sequential consistency。
Linearizability对于系统上层的业务来说,是很重要的。否则对于业务来说,就很难推断系统状态,比如我刚操作完数据d,然后把finish_flag标记为true,如果不保证linearizability,其他读者就无法依据finish_flag来安全地访问数据d。
4. Causal Consistency
因果关系,就是Lamport在Lamport在分布式时钟事件序论文中描述的happen-before关系及其传递闭包,即如下三种因果:
Causal Consistency要求,如果两个事件有因果关系,则要求这两个事件的先后顺序满足因果序,并且所有进程看到的这两个事件的顺序都是满足这个因果序的。
Causal Consistency相比Sequential Consistency来说,仅要求有因果关系的事件顺序对所有进程看到的一致,没有因果关系的事件顺序对于所有进程可以不一致。
如图示,P2把x从1改成2,因此读取操作不允许出现R(x)2,R(x)1的现象。但是图中,y操作没有因果序,所以P3读到R(y)2,R(y)1和P4读到R(y)1,R(y)2的在Causal Consistency是允许的。这一现象,在Sequential Consistency是不允许的。不严格地说,Causal Consistency弱于Sequential Consistency。
5. FIFO Consistency (also known as PRAM consistency)
FIFO Consistency要求同一个进程的program order下的几个操作被其他所有进程看到的顺序是相同的。但是不同进程的几个操作被其他所有进程看到的顺序可能是不同的(即使有因果关系)。
6. Cache Consistency
FIFO Consistency要求同一个进程几个操作被其他进程看到一致的顺序。类似地, Cache Consistency要求所进程涉及到的同一个地址的几个操作,要被所有进程看到一致的顺序。FIFO Consistency和Cache Consistency相比,是两个维度的约束,这两个一致性是incomparable的。
如图,P3,P4看到的x,y分别是一致的。但是x,y并不是一致的。
7. Processor Consistency
Processor Consistency是Cache Consistency + FIFO Consistency的结合,约束更多,因此这个一致性比另外两个都更强。
8. Eventual Consistency
最终一致性:存储系统保证如果没有新的写操作,那么最终,所有的读操作都能读到一致的数据,这里强调对一个数据项的修改最终会收敛。
这个一致性的概念也不是特别清晰,从1975年开始,不同时间段有不同研究者在定义使用,而且近几年一直有人研究,感兴趣的读者可以参考http://www.bailis.org/这个教授的一些论文。
最终一致性在设计领域也有一些争议,可以参考这里http://queue.acm.org /detail.cfm?id=2610533。弱一致性确实带给业务一些不方便,这可能也是近几年强一致性的关系型数据库又复苏的一个原因。
Client-Centric(User-Centric) Models
client-centric model,也叫user-centric model。在实际应用中,从分布式系统的客户端角度,对系统的读写操作通常都依赖session这样的概念,session完全隶属于某一个进程。该模型下有如下几个一致性:Read Your Writes、Monotonic Reads、Monotonic Writes、Writs Follow Reads。