分布式系统中一个重要的问题就是数据复制,数据复制通常是为了加强系统的可用性或提升性能。而实现数据复制的一个主要难题就是保持各个副本的一致性。本文首先讨论数据复制的场景中一致性模型如此重要的缘由,而后讨论一致性模型的含义,最后分析经常使用的一致性模型。数据库
**数据复制主要的目的有两个:可用性和性能。**首先数据复制能够提升系统的可用性。在保持多副本的状况,有一个副本不可用,系统切换到其余副本就会恢复。经常使用的 MySQL 主备同步方案就是一个典型的例子。另外一方面,数据复制可以提供系统的性能。当分布式系统须要在服务器数量和地理区域上进行扩展时,数据复制是一个至关重要的手段。有了多个数据副本,就能将请求分流;在多个区域提供服务时,也能经过就近原则提升客户端访问数据的效率。经常使用的 CDN 技术就是一个典型的例子。 可是数据复制是要付出代价的。**数据复制带来了多副本数据一致性的问题。**一个副本的数据更新以后,其余副本必需要保持同步,不然数据不一致就可能致使业务出现问题。所以,每次更新数据对全部副本进行修改的时间以及方式决定了复制代价的大小。全局同步与性能其实是矛盾的,而为了提升性能,每每会采用放宽一致性要求的方法。所以,咱们须要用一致性模型来理解和推理在分布式系统中数据复制须要考虑的问题和基本假设。缓存
首先咱们要定义一下一致性模型的术语:服务器
下面是一致性模型的定义: 一致性模型本质上是进程与数据存储的约定:若是进程遵循某些规则,那么进程对数据的读写操做都是可预期的。并发
上面的定义可能比较抽象,咱们用常见的强一致性模型来通俗的解释一下:**在线性一致性模型中,进程对一个数据项的读操做,它期待数据存储返回的是该数据在最后一次写操做以后的结果。**这在单机系统里面很容易实现,在 MySQL 中只要使用加锁读的方式就能保证读取到数据在最后一次写操做以后的结果。但在分布式系统中,由于没有全局时钟,致使要精肯定义哪次写操做是最后一次写操做是很是困难的事情,所以产生了一系列的一致性模型。**每种模型都有效限制了在对一个数据项执行读操做所应该返回的值。**举个例子:假设记录值 X 在节点 M 和 N 上都有副本,当客户端 A 修改了副本 M 上 X 的值,一段时间以后,客户端 B 从 N 上读取 X 的值,此时一致性模型会决定客户端 B 是否可以读取到 A 写入的值。分布式
一致性模型主要能够分为两类:可以保证全部进程对数据的读写顺序都保持一致的一致性模型称为强一致性模型,而不能保证的一致性模型称为弱一致性模型。post
线性一致性也叫严格一致性(Strict Consistency)或者原子一致性(Atomic Consistency),它的条件是:性能
线性一致性是对一致性要求最高的一致性模型,就现有技术是不可能实现的。由于它要求全部操做都实时同步,在分布式系统中要作到全局彻底一致时钟现有技术是作不到的。首先通讯是必然有延迟的,一旦有延迟,时钟的同步就无法作到一致。固然不排除之后新的技术可以作到,但目前而言线性一致性是没法实现的。.net
顺序一致性是 Lamport(1979)在解决多处理器系统共享存储器时首次提出来的。参考我以前写的文章《分布式系统:Lamport 逻辑时钟》。它的条件是:排序
首先咱们先来分析一下线性一致性和顺序一致性的相同点在哪里。他们都可以保证全部进程对数据的读写顺序保持一致。线性一致性的实现很简单,就按照全局时钟(能够简单理解为物理时钟)为参考系,全部进程都按照全局时钟的时间戳来区分事件的前后,那么必然全部进程看到的数据读写操做顺序必定是同样的,由于它们的参考系是同样的。而顺序一致性使用的是逻辑时钟来做为分布式系统中的全局时钟,进而全部进程也有了一个统一的参考系对读写操做进行排序,所以全部进程看到的数据读写操做顺序也是同样的。进程
那么线性一致性和顺序一致性的区别在哪里呢?经过上面的分析能够发现,**顺序一致性虽然经过逻辑时钟保证全部进程保持一致的读写操做顺序,但这些读写操做的顺序跟实际上发生的顺序并不必定一致。**而线性一致性是严格保证跟实际发生的顺序一致的。
因果一致性是一种弱化的顺序一致性模型,由于它将具备潜在因果关系的事件和没有因果关系的事件区分开了。那么什么是因果关系?若是事件 B 是由事件 A 引发的或者受事件 A 的影响,那么这两个事件就具备因果关系。 举个分布式数据库的示例,假设进程 P1 对数据项 x 进行了写操做,而后进程 P2 先读取了 x,而后对 y 进行了写操做,那么对 x 的读操做和对 y 的写操做就具备潜在的因果关系,由于 y 的计算可能依赖于 P2 读取到 x 的值(也就是 P1 写的值)。 另外一方面,若是两个进程同时对两个不一样的数据项进行写操做,那么这两个事件就不具有因果关系。无因果关系的操做称为并发操做。这里只是简单陈述了一下,深刻的分析见我以前写的文章《分布式系统:向量时钟》。 因果一致性的条件包括:
下面咱们来分析一下为何说因果一致性是一种弱化的顺序一致性模型。顺序一致性虽然不保证事件发生的顺序跟实际发生的保持一致,可是它可以保证全部进程看到的读写操做顺序是同样的。而**因果一致性更进一步弱化了顺序一致性中对读写操做顺序的约束,仅保证有因果关系的读写操做有序,没有因果关系的读写操做(并发事件)则不作保证。**也就是说若是是无因果关系的数据操做不一样进程看到的值是有多是不同,而有因果关系的数据操做不一样进程看到的值保证是同样的。
最终一致性是更加弱化的一致性模型,因果一致性起码还保证了有因果关系的数据不一样进程读取到的值保证是同样的,而最终一致性只保证全部副本的数据最终在某个时刻会保持一致。 从某种意义上讲,最终一致性保证的数据在某个时刻会最终保持一致就像是在说:“人总有一天会死”同样。实际上咱们更加关心的是:
因为最终一致性对数据一致性的要求比较低,在对性能要求高的场景中是常用的一致性模型。
前面咱们讨论的一致性模型都是针对数据存储的多副本之间如何作到一致性,考虑这么一种场景:在最终一致性的模型中,若是客户端在数据不一样步的时间窗口内访问不一样的副本的同一个数据,会出现读取同一个数据却获得不一样的值的状况。为了解决这个问题,有人提出了以客户端为中心的一致性模型。以客户端为中心的一致性为单一客户端提供一致性保证,保证该客户端对数据存储的访问的一致性,可是它不为不一样客户端的并发访问提供任何一致性保证。 举个例子:客户端 A 在副本 M 上读取 x 的最新值为 1,假设副本 M 挂了,客户端 A 链接到副本 N 上,此时副本 N 上面的 x 值为旧版本的 0,那么一致性模型会保证客户端 A 读取到的 x 的值为 1,而不是旧版本的 0。一种可行的方案就是给数据 x 加版本标记,同时客户端 A 会缓存 x 的值,经过比较版原本识别数据的新旧,保证客户端不会读取到旧的值。
以客户端为中心的一致性包含了四种子模型:
数据复制致使了一致性的问题,为了保持副本的一致性可能会严重地影响性能,惟一的解决办法就是放松一致性的要求。经过一致性模型咱们能够理解和推理在分布式系统中数据复制须要考虑的问题和基本假设,便于结合具体的业务场景作权衡。每种模型都有效地限制了对一个数据项执行度操做应返回的值。一般来讲限制越少的模型越容易应用,但一致性的保证就越弱。
《分布式系统原理与范型》