栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

分布式系统一致性模型

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

分布式系统一致性模型

https://zhuanlan.zhihu.com/p/48782892
评论中所提到《survey on consistency condition》分布式一致性,内存一致性,事务隔离性共性的问题但是解决方案多种多样,可以联系在一起思考
一致性模型的研究由来已久,在基于共享内存的多核CPU并行计算中,科学家就已经开始对一致性模型开始研究,然后将一致性模型顺理成章的推广到基于网络通信的多节点协同系统中。

一致性模型

一致性模型:给定一些涉及操作与状态的规则,随着操作的演进,系统将一直遵循这些规则。
一致性模型:分布式系统对外界承诺的一个契约,外界按照契约的规定与分布式系统发生交互,就能得到契约中承诺的观测结果。
一致性模型是所有被允许的操作记录的集合。当我们运行一个程序,经过一系列集合中允许的操作,特定的执行结果总是一致的。如果程序意外地执行了非集合中的操作,我们就称执行记录是非一致的。如果任意可能的执行操作都在这个被允许的操作集合内,那么系统就满足一致性模型。我们希望实际的系统是满足这样“直观正确”的一致性模型的,这样我们才能写出可预测的程序。(所有程序全都返回0,你也可以说他是一种一致性模型,只是毫无意义)

数据复制导致了一致性的问题,为了保持副本的一致性可能会严重地影响性能,唯一的解决办法就是放松一致性的要求。通过一致性模型我们可以理解和推理在分布式系统中数据复制需要考虑的问题和基本假设,便于结合具体的业务场景做权衡。每种模型都有效地限制了对一个数据项执行度操作应返回的值。通常来说限制越少的模型越容易应用,但一致性的保证就越弱。

分布式系统的事件顺序

在单体系统中,我们通过时间戳就可以确定事件发生顺序。
但是跨节点的事件e.g. A先向C发送事件ac,再向B发送事件ab,B收到ab后向C发送事件bc,结果事件bc先到达C,拿到了更早的时间戳
操作并不是顺时完成的,尤其是在分布式系统,加上网络通信的延迟,会放大这些耗时。即使读操作比写操作指令更早发给其他机器,但是消息传播的快慢会导致预期外的事件顺序发生。当然我们是允许一些歧义的顺序发生,但不是所有。

线性一致性Linearizability

线性化是强大的武器。一旦一个操作完成,它或它之后的某一状态将对所有参与者可见(volitail变量)。因为每个操作一定发生在它的完成时间之前,且任何之后被调用的操作一定发生在调用时间之后,也就是在原操作本身之后。 一旦我们成功写入b,每个之后调用的读请求都可以读到b,如果有更多的写操作发生的话,也可以是b之后的某个值。
由于这些强约束条件的存在,可线性化的系统变得更容易推理,这也是很多并发编程模型构建的时候选择它作为基础的原因。Javascript中的所有变量都是(独立地)可线性化的,其他的还有Java中的volatile变量,Clojure中的atoms,Erlang中独立的process。大多数编程语言都实现了互斥量和信号量,它们也是可线性化的。强约束的假设通常会产生强约束的保证。

cpu的一致性模型(供对比理解)

在内存一致性模型的学习中,我们知道通过MESI实现了可见性,在A线程改变某volitail变量后,B线程是可见的(与分布式系统相比对于A提出写请求后,读请求在B上,结果可能依然是旧值,cpu是线性一致性模型立马可见),通过原子性操作cas即可完成一致性模型。可参考我的相关博客Java内存模型与线程安全。
此外直接加锁也可以。对应上面一致性模型的定义,在多线程相加到2000的这个经典例子中,如果未通过锁或者volitail、cas操作,其所得结果是不可预测的,可能是小于2000 的任意值,系统并没有遵守这个规则,也就是说不满足一致性模型。

顺序一致性Sequential consistency

顺序一致性放松了对一致性的要求:
1.不要求操作按照真实的时间序发生,允许进程在时间维度发生偏移。
2.不同进程间的操作执行先后顺序也没有强制要求,但必须是原子的。
3.单个进程内的操作顺序必须和编码时的顺序一致,任意进程中的操作必须按照进程中定义的顺序发生。
eg:当一个用户上传一段视频到Youtube,Youtube把视频放入一个处理队列,并立刻返回一个此视频的网页。我们并不能立刻看到视频,上传的视频会在被充分处理后的几分钟内生效。队列会以入队的顺序同步地(取决于队列的具体实现)删除队列中的项。

zookeeper如何保证事件顺序一致性

https://time.geekbang.org/column/article/239261
zookeeper数据写入过程如下:
第一阶段:每次的数据写入事件作为提案广播给所有 Follower 结点;可以写入的结点返回确认信息 ACK;
第二阶段:Leader 收到一半以上的 ACK 信息后确认写入可以生效,向所有结点广播 COMMIT 将提案生效。

zookeeper保证最终一致性,也叫顺序一致性,即每个结点的数据都是严格按事务的发起顺序生效的。并不需要leader向客户端返回写入成功后,用户再读取别的follower节点时一定读到最新数据,可能follower还没有写入。
虽然并没有线性一致性那样要求那样立马可见,但是各个节点必须严格按照事务的发起顺序生效。
读事件是不需要的,并不会影响数据的一致性,而写事件需要通过leader节点,因此zookeeper由leader节点生成zxid(事务id)并随着提案一起广播。
ZXID 由两部分组成:
任期:完成本次选举后,直到下次选举前,由同一 Leader 负责协调写入,高32位;
事务计数器:单调递增,每生效一次写入,计数器加一,低32位。
ZXID 的低 32 位是计数器,所以同一任期内,ZXID 是连续的,每个结点又都保存着自身最新生效的 ZXID,通过对比新提案的 ZXID 与自身最新 ZXID 是否相差“1”,来保证事务严格按照顺序生效的。

因果一致性Casual consistency

我们不必对一个进程中的每个操作都施加顺序约束。只有因果相关的操作必须按顺序发生。
因果一致性比同一进程下对每个操作严格排序的一致性(即顺序一致性)来的更宽松——属于同一进程但不同因果关系链的操作能以相对的顺序执行(也就是说按因果关系隔离,无因果关系的操作可以并发执行),这能防止许多不直观的行为发生。
因果一致性更进一步弱化了顺序一致性中对读写操作顺序的约束,仅保证有因果关系的读写操作有序,没有因果关系的读写操作(并发事件)则不做保证。

最终一致性Eventual Consistency

最终一致性是更加弱化的一致性模型,因果一致性起码还保证了有因果关系的数据不同进程读取到的值保证是一样的,而最终一致性只保证所有副本的数据最终在某个时刻会保持一致。由于最终一致性对数据一致性的要求比较低,在对性能要求高的场景中是经常使用的一致性模型。

以客户端为中心的一致性Client-centric Consistency

前面我们讨论的一致性模型都是针对数据存储的多副本之间如何做到一致性,考虑这么一种场景:在最终一致性的模型中,如果客户端在数据不同步的时间窗口内访问不同的副本的同一个数据,会出现读取同一个数据却得到不同的值的情况。
以客户端为中心的一致性为单一客户端提供一致性保证,保证该客户端对数据存储的访问的一致性,但是它不为不同客户端的并发访问提供任何一致性保证。 举个例子:客户端 A 在副本 M 上读取 x 的最新值为 1,假设副本 M 挂了,客户端 A 连接到副本 N 上,此时副本 N 上面的 x 值为旧版本的 0,那么一致性模型会保证客户端 A 读取到的 x 的值为 1,而不是旧版本的 0。一种可行的方案就是给数据 x 加版本标记,同时客户端 A 会缓存 x 的值,通过比较版本来识别数据的新旧,保证客户端不会读取到旧的值。

以客户端为中心的一致性包含了四种子模型:

  1. 单调读一致性(Monotonic-read Consistency):如果一个进程读取数据项 x 的值,那么该进程对于 x 后续的所有读操作要么读取到第一次读取的值要么读取到更新的值。即保证客户端不会读取到旧值。
  2. 单调写一致性(Monotonic-write Consistency):一个进程对数据项 x 的写操作必须在该进程对 x 执行任何后续写操作之前完成。即保证客户端的写操作是串行的。
  3. 读写一致性(Read-your-writes Consistency):一个进程对数据项 x 执行一次写操作的结果总是会被该进程对 x 执行的后续读操作看见。即保证客户端能读到自己最新写入的值。
  4. 写读一致性(Writes-follow-reads Consistency):同一个进程对数据项 x 执行的读操作之后的写操作,保证发生在与 x 读取值相同或比之更新的值上。即保证客户端对一个数据项的写操作是基于该客户端最新读取的值。
串行一致性Serializable consistency

串行一致性是数据库领域的概念,是针对事务而言的,描述对一组事务的执行效果等同于某种串行的执行,没有ordering的概念,而线性一致性来自并行计算领域,描述了针对某种数据结构的操作所表现出的顺序特征。
相较于并行计算,数据库的旧值是可以有意义的,参考数据库事务方面的隔离级别。
大多数SQL数据库宣称的串行一致性等级比实际的更弱,比如可重复读,游标稳定性,或是快照隔离性。(ACID)

一致性模型的代价Consistency comes with costs

一个分布式系统里面,节点组成的网络本来应该是连通的。然而可能因为一些故障,使得有些节点之间不连通了,整个网络就分成了几块区域。数据就散布在了这些不连通的区域中。这就叫分区。

当你一个数据项只在一个节点中保存,那么分区出现后,和这个节点不连通的部分就访问不到这个数据了。这时分区就是无法容忍的。

提高分区容忍性的办法就是一个数据项复制到多个节点上,那么出现分区之后,这一数据项就可能分布到各个区里。容忍性就提高了。

然而,要把数据复制到多个节点,就会带来一致性的问题,就是多个节点上面的数据可能是不一致的。要保证一致,每次写操作就都要等待全部节点写成功,而这等待又会带来可用性的问题。

总的来说就是,数据存在的节点越多,分区容忍性越高,但要复制更新的数据就越多,一致性就越难保证。为了保证一致性,更新所有节点数据所需要的时间就越长,可用性就会降低。
CA without P:如果不要求P(不允许分区),则C(强一致性)和A(可用性)是可以保证的。但其实分区不是你想不想的问题,而是始终会存在,因此CA的系统更多的是允许分区后各子系统依然保持CA。
CP without A:如果不要求A(可用),相当于每个请求都需要在Server之间强一致,而P(分区)会导致同步时间无限延长,如此CP也是可以保证的。很多传统的数据库分布式事务都属于这种模式。
AP without C:要高可用并允许分区,则需放弃一致性。一旦分区发生,节点之间可能会失去联系,为了高可用,每个节点只能用本地数据提供服务,而这样会导致全局数据的不一致性。现在众多的NoSQL都属于此类。

  1. 一致性(Consistency)意味着线性化,具体说,可以是一个可线性化的寄存器。寄存器可以等效为集合,列表,映射,关系型数据库等等,因此该理论可以被拓展到各种可线性化的系统。
  2. 可用性(Availability)意味着向非故障节点发出的每个请求都将成功完成。因为网络分区可以持续任意长的时间,因此节点不能简单地把响应推迟到分区结束。
  3. 分区容错性(Partition tolerance)意味着分区很可能发生。当网络可靠的时候,提供一致性和可用性将变得十分简单,但当网络不可靠时,同时提供一致性和可用性将变得几乎不可能。然而网络总不是完美可靠的,所以我们不能选择CA。所有实际可用的商用分布式系统至多能提供AP或CP的保证。
    CAP理论只声称我们不能构建一个完全可用的线性化系统。如果我们要求完全可用,那就能提供单调读,单调写,读的提交,单调且原子的视角等等。这些一致性模型是由如Riak和Cassandra这样的分布式存储系统,低隔离性设置的ANSI SQL数据库提供的。这些一致性模型并没有保证线性顺序,而是在批处理任务或网络场景下提供部分顺序保证。只能保证部分顺序是因为它们准许更丰富的记录。
    更强约束的一致性模型需要更多的协调——需要更多的消息交互,确保操作在正确的顺序发生。这不仅意味着更低的可用性,还会被导致更高的延迟。这也是为什么现代CPU内存模型默认不是线性化的,除非显示指定。(x86-64架构的CPU通常以Sequential consistency作为默认的memory order),现代CPU会在核之间重排内存操作,甚至更糟糕。虽然(程序的行为)更难以推理,但带来的性能提升是惊人的。在地理位置上零落的分布式系统,数据中心通常有几百毫秒的延迟,通常和CPU的场景类似,代价也相似。
分布式系统:时间、时钟和事件序列

如果a和b属于同个进程,事件a先于事件b执行,那么a->b
如果a和b属于不同进程,a表示发送消息,b表示接收到了该消息,那么a->b
事件具有传递性,a->b,b->c,则a->c
在分布式系统中,只有两个发生关联的事件(有因果关系),我们才会去关心两者的先来后到关系。对于并发事件,他们两个谁先发生,谁后发生,其实我们并不关心。偏序就是用来定义两个因果事件的发生次序,即‘happens before’。而对于并发事件(没有因果关系),并不能决定其先后,所以说这种‘happens before’的关系,是一种偏序关系。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/299320.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号