3.1.2 一致性
由于异常的存在,分布式存储系统设计时往往会将数据冗余存储多份,每一份称为一个副本(replica/copy)。这样,当某一个节点出现故障时,可以从其他副本上读到数据。可以这么认为,副本是分布式存储系统容错技术的唯一手段。由于多个副本的存在,如何保证副本之间的一致性是整个分布式系统的理论核心。
可以从两个角度理解一致性:第一个角度是用户,或者说是客户端,即客户端读写操作是否符合某种特性;第二个角度是存储系统,即存储系统的多个副本之间是否一致,更新的顺序是否相同,等等。
首先定义如下场景,这个场景包含三个组成部分:
●存储系统:存储系统可以理解为一个黑盒子,它为我们提供了可用性和持久性的保证。
●客户端A:客户端A主要实现从存储系统write和read操作。
●客户端B和客户端C:客户端B和C是独立于A,并且B和C也相互独立的,它们同时也实现对存储系统的write和read操作。
从客户端的角度来看,一致性包含如下三种情况:
●强一致性:假如A先写入了一个值到存储系统,存储系统保证后续A,B,C的读取操作都将返回最新值。当然,如果写入操作“超时”,那么成功或者失败都是可能的,客户端A不应该做任何假设。
●弱一致性:假如A先写入了一个值到存储系统,存储系统不能保证后续A,B,C的读取操作是否能够读取到最新值。
●最终一致性:最终一致性是弱一致性的一种特例。假如A首先写入一个值到存储系统,存储系统保证如果后续没有写操作更新同样的值,A,B,C的读取操作“最终”都会读取到A写入的最新值。“最终”一致性有一个“不一致窗口”的概念,它特指从A写入值,到后续A,B,C读取到最新值的这段时间。“不一致性窗口”的大小依赖于以下的几个因素:交互延迟,系统的负载,以及复制协议要求同步的副本数。
最终一致性描述比较粗略,其他常见的变体如下:
●读写(Read-your-writes)一致性:如果客户端A写入了最新的值,那么A的后续操作都会读取到最新值。但是其他用户(比如B或者C)可能要过一会才能看到。
●会话(Session)一致性:要求客户端和存储系统交互的整个会话期间保证读写一致性。如果原有会话因为某种原因失效而创建了新的会话,原有会话和新会话之间的操作不保证读写一致性。
●单调读(Monotonic read)一致性:如果客户端A已经读取了对象的某个值,那么后续操作将不会读取到更早的值。
●单调写(Monotonic write)一致性:客户端A的写操作按顺序完成,这就意味着,对于同一个客户端的操作,存储系统的多个副本需要按照与客户端相同的顺序完成。
从存储系统的角度看,一致性主要包含如下几个方面:
●副本一致性:存储系统的多个副本之间的数据是否一致,不一致的时间窗口等;
●更新顺序一致性:存储系统的多个副本之间是否按照相同的顺序执行更新操作。
一般来说,存储系统可以支持强一致性,也可以为了性能考虑只支持最终一致性。从客户端的角度看,一般要求存储系统能够支持读写一致性,会话一致性,单调读,单调写等特性,否则,使用比较麻烦,适用的场景也比较有限。