分布式系统的事务处理

当咱们在生产线上用一台服务器来提供数据服务的时候,我会遇到以下的两个问题:html

1)一台服务器的性能不足以提供足够的能力服务于全部的网络请求。算法

2)咱们老是惧怕咱们的这台服务器停机,形成服务不可用或是数据丢失。数据库

因而咱们不得不对咱们的服务器进行扩展,加入更多的机器来分担性能上的问题,以及来解决单点故障问题。 一般,咱们会经过两种手段来扩展咱们的数据服务:安全

1)数据分区:就是把数据分块放在不一样的服务器上(如:uid % 16,一致性哈希等)。服务器

2)数据镜像:让全部的服务器都有相同的数据,提供至关的服务。网络

对于第一种状况,咱们没法解决数据丢失的问题,单台服务器出问题时,会有部分数据丢失。因此,数据服务的高可用性只能经过第二种方法来完成——数据的冗余存储(通常工业界认为比较安全的备份数应该是3份,如:Hadoop和Dynamo)。 可是,加入更多的机器,会让咱们的数据服务变得很复杂,尤为是跨服务器的事务处理,也就是跨服务器的数据一致性。这个是一个很难的问题。 让咱们用最经典的Use Case:“A账号向B账号汇钱”来讲明一下,熟悉RDBMS事务的都知道从账号A到账号B须要6个操做:并发

  1. 从A账号中把余额读出来。
  2. 对A账号作减法操做。
  3. 把结果写回A账号中。
  4. 从B账号中把余额读出来。
  5. 对B账号作加法操做。
  6. 把结果写回B账号中。

为了数据的一致性,这6件事,要么都成功作完,要么都不成功,并且这个操做的过程当中,对A、B账号的其它访问必需锁死,所谓锁死就是要排除其它的读写操做,否则会有脏数据的问题,这就是事务。那么,咱们在加入了更多的机器后,这个事情会变得复杂起来:app

 

1)在数据分区的方案中:若是A账号和B账号的数据不在同一台服务器上怎么办?咱们须要一个跨机器的事务处理。也就是说,若是A的扣钱成功了,但B的加钱不成功,咱们还要把A的操做给回滚回去。这在跨机器的状况下,就变得比较复杂了。框架

2)在数据镜像的方案中:A账号和B账号间的汇款是能够在一台机器上完成的,可是别忘了咱们有多台机器存在A账号和B账号的副本。若是对A账号的汇钱有两个并发操做(要汇给B和C),这两个操做发生在不一样的两台服务器上怎么办?也就是说,在数据镜像中,在不一样的服务器上对同一个数据的写操做怎么保证其一致性,保证数据不冲突?异步

同时,咱们还要考虑性能的因素,若是不考虑性能的话,事务获得保证并不困难,系统慢一点就好了。除了考虑性能外,咱们还要考虑可用性,也就是说,一台机器没了,数据不丢失,服务可由别的机器继续提供。 因而,咱们须要重点考虑下面的这么几个状况:

1)容灾:数据不丢、结点的Failover

2)数据的一致性:事务处理

3)性能:吞吐量 、 响应时间

前面说过,要解决数据不丢,只能经过数据冗余的方法,就算是数据分区,每一个区也须要进行数据冗余处理。这就是数据副本:当出现某个节点的数据丢失时能够从副本读到,数据副本是分布式系统解决数据丢失异常的惟一手段。因此,在这篇文章中,简单起见,咱们只讨论在数据冗余状况下考虑数据的一致性和性能的问题。简单说来:

1)要想让数据有高可用性,就得写多份数据。

2)写多份的问题会致使数据一致性的问题。

3)数据一致性的问题又会引起性能问题

这就是软件开发,按下了葫芦起了瓢。

一致性模型

提及数据一致性来讲,简单说有三种类型(固然,若是细分的话,还有不少一致性模型,如:顺序一致性,FIFO一致性,会话一致性,单读一致性,单写一致性,但为了本文的简单易读,我只说下面三种):

1)Weak 弱一致性:当你写入一个新值后,读操做在数据副本上可能读出来,也可能读不出来。好比:某些cache系统,网络游戏其它玩家的数据和你没什么关系,VOIP这样的系统,或是百度搜索引擎(呵呵)。

2)Eventually 最终一致性:当你写入一个新值后,有可能读不出来,但在某个时间窗口以后保证最终能读出来。好比:DNS,电子邮件、Amazon S3,Google搜索引擎这样的系统。

3)Strong 强一致性:新的数据一旦写入,在任意副本任意时刻都能读到新值。好比:文件系统,RDBMS,Azure Table都是强一致性的。

从这三种一致型的模型上来讲,咱们能够看到,Weak和Eventually通常来讲是异步冗余的,而Strong通常来讲是同步冗余的,异步的一般意味着更好的性能,但也意味着更复杂的状态控制。同步意味着简单,但也意味着性能降低。 好,让咱们由浅入深,一步一步地来看有哪些技术:

Master-Slave

首先是Master-Slave结构,对于这种加构,Slave通常是Master的备份。在这样的系统中,通常是以下设计的:

1)读写请求都由Master负责。

2)写请求写到Master上后,由Master同步到Slave上。

从Master同步到Slave上,你可使用异步,也可使用同步,可使用Master来push,也可使用Slave来pull。 一般来讲是Slave来周期性的pull,因此,是最终一致性。这个设计的问题是,若是Master在pull周期内垮掉了,那么会致使这个时间片内的数据丢失。若是你不想让数据丢掉,Slave只能成为Read-Only的方式等Master恢复。

固然,若是你能够容忍数据丢掉的话,你能够立刻让Slave代替Master工做(对于只负责计算的结点来讲,没有数据一致性和数据丢失的问题,Master-Slave的方式就能够解决单点问题了) 固然,Master Slave也能够是强一致性的, 好比:当咱们写Master的时候,Master负责先写本身,等成功后,再写Slave,二者都成功后返回成功,整个过程是同步的,若是写Slave失败了,那么两种方法,一种是标记Slave不可用报错并继续服务(等Slave恢复后同步Master的数据,能够有多个Slave,这样少一个,还有备份,就像前面说的写三份那样),另外一种是回滚本身并返回写失败。(注:通常不先写Slave,由于若是写Master本身失败后,还要回滚Slave,此时若是回滚Slave失败,就得手工订正数据了)你能够看到,若是Master-Slave须要作成强一致性有多复杂。

Master-Master

Master-Master,又叫Multi-master,是指一个系统存在两个或多个Master,每一个Master都提供read-write服务。这个模型是Master-Slave的增强版,数据间同步通常是经过Master间的异步完成,因此是最终一致性。 Master-Master的好处是,一台Master挂了,别的Master能够正常作读写服务,他和Master-Slave同样,当数据没有被复制到别的Master上时,数据会丢失。不少数据库都支持Master-Master的Replication的机制。

另外,若是多个Master对同一个数据进行修改的时候,这个模型的恶梦就出现了——对数据间的冲突合并,这并非一件容易的事情。看看Dynamo的Vector Clock的设计(记录数据的版本号和修改者)就知道这个事并不那么简单,并且Dynamo对数据冲突这个事是交给用户本身搞的。就像咱们的SVN源码冲突同样,对于同一行代码的冲突,只能交给开发者本身来处理。(在本文后后面会讨论一下Dynamo的Vector Clock)

Two/Three Phase Commit

这个协议的缩写又叫2PC,中文叫两阶段提交。在分布式系统中,每一个节点虽然能够知晓本身的操做时成功或者失败,却没法知道其余节点的操做的成功或失败。当一个事务跨越多个节点时,为了保持事务的ACID特性,须要引入一个做为协调者的组件来统一掌控全部节点(称做参与者)的操做结果并最终指示这些节点是否要把操做结果进行真正的提交(好比将更新后的数据写入磁盘等等)。 两阶段提交的算法以下:

第一阶段

  1. 协调者会问全部的参与者结点,是否能够执行提交操做。
  2. 各个参与者开始事务执行的准备工做:如:为资源上锁,预留资源,写undo/redo log……
  3. 参与者响应协调者,若是事务的准备工做成功,则回应“能够提交”,不然回应“拒绝提交”。

第二阶段

  • 若是全部的参与者都回应“能够提交”,那么,协调者向全部的参与者发送“正式提交”的命令。参与者完成正式提交,并释放全部资源,而后回应“完成”,协调者收集各结点的“完成”回应后结束这个Global Transaction。
  • 若是有一个参与者回应“拒绝提交”,那么,协调者向全部的参与者发送“回滚操做”,并释放全部资源,而后回应“回滚完成”,协调者收集各结点的“回滚”回应后,取消这个Global Transaction。

咱们能够看到,2PC说白了就是第一阶段作Vote,第二阶段作决定的一个算法,也能够看到2PC这个事是强一致性的算法。在前面咱们讨论过Master-Slave的强一致性策略,和2PC有点类似,只不过2PC更为保守一些——先尝试再提交。 2PC用的是比较多的,在一些系统设计中,会串联一系列的调用,好比:A -> B -> C -> D,每一步都会分配一些资源或改写一些数据。好比咱们B2C网上购物的下单操做在后台会有一系列的流程须要作。若是咱们一步一步地作,就会出现这样的问题,若是某一步作不下去了,那么前面每一次所分配的资源须要作反向操做把他们都回收掉,因此,操做起来比较复杂。如今不少处理流程(Workflow)都会借鉴2PC这个算法,使用 try -> confirm的流程来确保整个流程的可以成功完成。 举个通俗的例子,西方教堂结婚的时候,都有这样的桥段:

1)牧师分别问新郎和新娘:你是否愿意……无论生老病死……(询问阶段)

2)当新郎和新娘都回答愿意后(锁定一辈子的资源),牧师就会说:我宣布大家……(事务提交)

这是多么经典的一个两阶段提交的事务处理。 另外,咱们也能够看到其中的一些问题, A)其中一个是同步阻塞操做,这个事情必然会很是大地影响性能。 B)另外一个主要的问题是在TimeOut上,好比,

1)若是第一阶段中,参与者没有收到询问请求,或是参与者的回应没有到达协调者。那么,须要协调者作超时处理,一旦超时,能够看成失败,也能够重试。

2)若是第二阶段中,正式提交发出后,若是有的参与者没有收到,或是参与者提交/回滚后的确认信息没有返回,一旦参与者的回应超时,要么重试,要么把那个参与者标记为问题结点剔除整个集群,这样能够保证服务结点都是数据一致性的。

3)糟糕的状况是,第二阶段中,若是参与者收不到协调者的commit/fallback指令,参与者将处于“状态未知”阶段,参与者彻底不知道要怎么办,好比:若是全部的参与者完成第一阶段的回复后(可能所有yes,可能所有no,可能部分yes部分no),若是协调者在这个时候挂掉了。那么全部的结点彻底不知道怎么办(问别的参与者都不行)。为了一致性,要么死等协调者,要么重发第一阶段的yes/no命令。

两段提交最大的问题就是第3)项,若是第一阶段完成后,参与者在第二阶没有收到决策,那么数据结点会进入“不知所措”的状态,这个状态会block住整个事务。也就是说,协调者Coordinator对于事务的完成很是重要,Coordinator的可用性是个关键。 因些,咱们引入三段提交,三段提交在Wikipedia上的描述以下,他把二段提交的第一个段break成了两段:询问,而后再锁资源。最后真正提交。三段提交的示意图以下:

三段提交的核心理念是:在询问的时候并不锁定资源,除非全部人都赞成了,才开始锁资源

理论上来讲,若是第一阶段全部的结点返回成功,那么有理由相信成功提交的几率很大。这样一来,能够下降参与者Cohorts的状态未知的几率。也就是说,一旦参与者收到了PreCommit,意味他知道你们其实都赞成修改了。这一点很重要。下面咱们来看一下3PC的状态迁移图:(注意图中的虚线,那些F,T是Failuer或Timeout,其中的:状态含义是 q – Query,a – Abort,w – Wait,p – PreCommit,c – Commit)

从上图的状态变化图咱们能够从虚线(那些F,T是Failuer或Timeout)看到——若是结点处在P状态(PreCommit)的时候发生了F/T的问题,三段提交比两段提交的好处是,三段提交能够继续直接把状态变成C状态(Commit),而两段提交则不知所措

其实,三段提交是一个很复杂的事情,实现起来至关难,并且也有一些问题。

看到这里,我相信你有不少不少的问题,你必定在思考2PC/3PC中各类各样的失败场景,你会发现Timeout是个很是难处理的事情,由于网络上的Timeout在不少时候让你无所事从,你也不知道对方是作了仍是没有作。因而你好好的一个状态机就由于Timeout成了个摆设

一个网络服务会有三种状态:1)Success,2)Failure,3)Timeout,第三个绝对是恶梦,尤为在你须要维护状态的时候

Two Generals Problem(两将军问题)

Two Generals Problem 两将军问题是这么一个思惟性实验问题: 有两支军队,它们分别有一位将军领导,如今准备攻击一座修筑了防护工事的城市。这两支军队都驻扎在那座城市的附近,分占一座山头。一道山谷把两座山分隔开来,而且两位将军惟一的通讯方式就是派各自的信使来往于山谷两边。不幸的是,这个山谷已经被那座城市的保卫者占领,而且存在一种可能,那就是任何被派出的信使经过山谷是会被捕。 请注意,虽然两位将军已经就攻击那座城市达成共识,但在他们各自占领山头阵地以前,并无就进攻时间达成共识。两位将军必须让本身的军队同时进攻城市才能取得成功。所以,他们必须互相沟通,以肯定一个时间来攻击,并赞成就在那时攻击。若是只有一个将军进行攻击,那么这将是一个灾难性的失败。 这个思惟实验就包括考虑他们如何去作这件事情。下面是咱们的思考:

1)第一位将军先发送一段消息“让咱们在上午9点开始进攻”。然而,一旦信使被派遣,他是否经过了山谷,第一位将军就不得而知了。任何一点的不肯定性都会使得第一位将军攻击犹豫,由于若是第二位将军不能在同一时刻发动攻击,那座城市的驻军就会击退他的军队的进攻,致使他的军对被摧毁。

2)知道了这一点,第二位将军就须要发送一个确认回条:“我收到您的邮件,并会在9点的攻击。”可是,若是带着确认消息的信使被抓怎么办?因此第二位将军会犹豫本身的确认消息是否能到达。

3)因而,彷佛咱们还要让第一位将军再发送一条确认消息——“我收到了你的确认”。然而,若是这位信使被抓怎么办呢?

4)这样一来,是否是咱们还要第二位将军发送一个“确认收到你的确认”的信息。

靠,因而你会发现,这事情很快就发展成为无论发送多少个确认消息,都没有办法来保证两位将军有足够的自信本身的信使没有被敌军捕获。

这个问题是无解的。两个将军问题和它的无解证实首先由E.A.Akkoyunlu,K.Ekanadham和R.V.Huber于1975年在《一些限制与折衷的网络通讯设计》一文中发表,就在这篇文章的第73页中一段描述两个黑帮之间的通讯中被阐明。 1978年,在Jim Gray的《数据库操做系统注意事项》一书中(从第465页开始)被命名为两个将军悖论。做为两个将军问题的定义和无解性的证实的来源,这一参考被普遍说起。

这个实验意在阐明:试图经过创建在一个不可靠的链接上的交流来协调一项行动的隐患和设计上的巨大挑战。

从工程上来讲,一个解决两个将军问题的实际方法是使用一个可以承受通讯信道不可靠性的方案,并不试图去消除这个不可靠性,但要将不可靠性削减到一个能够接受的程度。好比,第一位将军排出了100位信使并预计他们都被捕的可能性很小。在这种状况下,无论第二位将军是否会攻击或者受到任何消息,第一位将军都会进行攻击。另外,第一位将军能够发送一个消息流,而第二位将军能够对其中的每一条消息发送一个确认消息,这样若是每条消息都被接收到,两位将军会感受更好。然而咱们能够从证实中看出,他们俩都不能确定这个攻击是能够协调的。他们没有算法可用(好比,收到4条以上的消息就攻击)可以确保防止仅有一方攻击。再者,第一位将军还能够为每条消息编号,说这是1号,2号……直到n号。这种方法能让第二位将军知道通讯信道到底有多可靠,而且返回合适的数量的消息来确保最后一条消息被接收到。若是信道是可靠的话,只要一条消息就好了,其他的就帮不上什么忙了。最后一条和第一条消息丢失的几率是相等的。

 两将军问题能够扩展成更变态的拜占庭将军问题 (Byzantine Generals Problem),其故事背景是这样的:拜占庭位于如今土耳其的伊斯坦布尔,是东罗马帝国的首都。因为当时拜占庭罗马帝国国土辽阔,为了防护目的,所以每一个军队都分隔很远,将军与将军之间只能靠信差传消息。 在战争的时候,拜占庭军队内全部将军必需达成一致的共识,决定是否有赢的机会才去攻打敌人的阵营。可是,军队可能有叛徒和敌军间谍,这些叛徒将军们会扰乱或左右决策的过程。这时候,在已知有成员谋反的状况下,其他忠诚的将军在不受叛徒的影响下如何达成一致的协议,这就是拜占庭将军问题。

Paxos算法

Wikipedia上的各类Paxos算法的描述很是详细,你们能够去围观一下。

Paxos 算法解决的问题是在一个可能发生上述异常的分布式系统中如何就某个值达成一致,保证不论发生以上任何异常,都不会破坏决议的一致性。一个典型的场景是,在一个分布式数据库系统中,若是各节点的初始状态一致,每一个节点都执行相同的操做序列,那么他们最后能获得一个一致的状态。为保证每一个节点执行相同的命令序列,须要在每一条指令上执行一个「一致性算法」以保证每一个节点看到的指令一致。一个通用的一致性算法能够应用在许多场景中,是分布式计算中的重要问题。从20世纪80年代起对于一致性算法的研究就没有中止过。

Notes:Paxos算法是莱斯利·兰伯特(Leslie Lamport,就是 LaTeX 中的”La”,此人如今在微软研究院)于1990年提出的一种基于消息传递的一致性算法。因为算法难以理解起初并无引发人们的重视,使Lamport在八年后1998年从新发表到ACM Transactions on Computer Systems上(The Part-Time Parliament)。即使如此paxos算法仍是没有获得重视,2001年Lamport 以为同行没法接受他的幽默感,因而用容易接受的方法从新表述了一遍(Paxos Made Simple)。可见Lamport对Paxos算法情有独钟。近几年Paxos算法的广泛使用也证实它在分布式一致性算法中的重要地位。2006年Google的三篇论文初现“云”的端倪,其中的Chubby Lock服务使用Paxos做为Chubby Cell中的一致性算法,Paxos的人气今后一路狂飙。(Lamport 本人在 他的blog 中描写了他用9年时间发表这个算法的前先后后)

注:Amazon的AWS中,全部的云服务都基于一个ALF(Async Lock Framework)的框架实现的,这个ALF用的就是Paxos算法。我在Amazon的时候,看内部的分享视频时,设计者在内部的Principle Talk里说他参考了ZooKeeper的方法,但他用了另外一种比ZooKeeper更易读的方式实现了这个算法。

简单说来,Paxos的目的是让整个集群的结点对某个值的变动达成一致。Paxos算法基本上来讲是个民主选举的算法——大多数的决定会成个整个集群的统一决定。任何一个点均可以提出要修改某个数据的提案,是否经过这个提案取决于这个集群中是否有超过半数的结点赞成(因此Paxos算法须要集群中的结点是单数)。

这个算法有两个阶段(假设这个有三个结点:A,B,C):

第一阶段:Prepare阶段

A把申请修改的请求Prepare Request发给全部的结点A,B,C。注意,Paxos算法会有一个Sequence Number(你能够认为是一个提案号,这个数不断递增,并且是惟一的,也就是说A和B不可能有相同的提案号),这个提案号会和修改请求一同发出,任何结点在“Prepare阶段”时都会拒绝其值小于当前提案号的请求。因此,结点A在向全部结点申请修改请求的时候,须要带一个提案号,越新的提案,这个提案号就越是是最大的。

若是接收结点收到的提案号n大于其它结点发过来的提案号,这个结点会回应Yes(本结点上最新的被批准提案号),并保证不接收其它<n的提案。这样一来,结点上在Prepare阶段里老是会对最新的提案作承诺。

优化:在上述 prepare 过程当中,若是任何一个结点发现存在一个更高编号的提案,则须要通知 提案人,提醒其中断此次提案。

第二阶段:Accept阶段

若是提案者A收到了超过半数的结点返回的Yes,而后他就会向全部的结点发布Accept Request(一样,须要带上提案号n),若是没有超过半数的话,那就返回失败。

当结点们收到了Accept Request后,若是对于接收的结点来讲,n是最大的了,那么,它就会修改这个值,若是发现本身有一个更大的提案号,那么,结点就会拒绝修改。

咱们能够看以,这彷佛就是一个“两段提交”的优化。其实,2PC/3PC都是分布式一致性算法的残次版本,Google Chubby的做者Mike Burrows说过这个世界上只有一种一致性算法,那就是Paxos,其它的算法都是残次品。

咱们还能够看到:对于同一个值的在不一样结点的修改提案就算是在接收方被乱序收到也是没有问题的。

关于一些实例,你能够看一下Wikipedia中文中的“Paxos样例”一节,我在这里就再也不多说了。对于Paxos算法中的一些异常示例,你们能够本身推导一下。你会发现基本上来讲只要保证有半数以上的结点存活,就没有什么问题。

多说一下,自从Lamport在1998年发表Paxos算法后,对Paxos的各类改进工做就从未中止,其中动做最大的莫过于2005年发表的Fast Paxos。不管何种改进,其重点依然是在消息延迟与性能、吞吐量之间做出各类权衡。为了容易地从概念上区分两者,称前者Classic Paxos,改进后的后者为Fast Paxos。

总结

下图来自:Google App Engine的co-founder Ryan Barrett在2009年的google i/o上的演讲《Transaction Across DataCenter》(视频: http://www.youtube.com/watch?v=srOgpXECblk

前面,咱们说过,要想让数据有高可用性,就须要冗余数据写多份。写多份的问题会带来一致性的问题,而一致性的问题又会带来性能问题。从上图咱们能够看到,咱们基本上来讲不可让全部的项都绿起来,这就是著名的CAP理论:一致性,可用性,分区容忍性,你只可能要其中的两个。

NWR模型

最后我还想提一下Amazon Dynamo的NWR模型。这个NWR模型把CAP的选择权交给了用户,让用户本身的选择你的CAP中的哪两个

所谓NWR模型。N表明N个备份,W表明要写入至少W份才认为成功,R表示至少读取R个备份。配置的时候要求W+R > N。 由于W+R > N, 因此 R > N-W 这个是什么意思呢?就是读取的份数必定要比总备份数减去确保写成功的倍数的差值要大。

也就是说,每次读取,都至少读取到一个最新的版本。从而不会读到一份旧数据。当咱们须要高可写的环境的时候,咱们能够配置W = 1 若是N=3 那么R = 3。 这个时候只要写任何节点成功就认为成功,可是读的时候必须从全部的节点都读出数据。若是咱们要求读的高效率,咱们能够配置 W=N R=1。这个时候任何一个节点读成功就认为成功,可是写的时候必须写全部三个节点成功才认为成功。

NWR模型的一些设置会形成脏数据的问题,由于这很明显不是像Paxos同样是一个强一致的东西,因此,可能每次的读写操做都不在同一个结点上,因而会出现一些结点上的数据并非最新版本,但却进行了最新的操做。

因此,Amazon Dynamo引了数据版本的设计。也就是说,若是你读出来数据的版本是v1,当你计算完成后要回填数据后,却发现数据的版本号已经被人更新成了v2,那么服务器就会拒绝你。版本这个事就像“乐观锁”同样。

可是,对于分布式和NWR模型来讲,版本也会有恶梦的时候——就是版本冲的问题,好比:咱们设置了N=3 W=1,若是A结点上接受了一个值,版本由v1 -> v2,但尚未来得及同步到结点B上(异步的,应该W=1,写一份就算成功),B结点上仍是v1版本,此时,B结点接到写请求,按道理来讲,他须要拒绝掉,可是他一方面并不知作别的结点已经被更新到v2,另外一方面他也没法拒绝,由于W=1,因此写一分就成功了。因而,出现了严重的版本冲突。

Amazon的Dynamo把版本冲突这个问题巧妙地回避掉了——版本冲这个事交给用户本身来处理。

因而,Dynamo引入了Vector Clock(矢量钟?!)这个设计。这个设计让每一个结点各自记录本身的版本信息,也就是说,对于同一个数据,须要记录两个事:1)谁更新的我,2)个人版本号是什么。

下面,咱们来看一个操做序列:

1)一个写请求,第一次被节点A处理了。节点A会增长一个版本信息(A,1)。咱们把这个时候的数据记作D1(A,1)。 而后另一个对一样key的请求仍是被A处理了因而有D2(A,2)。这个时候,D2是能够覆盖D1的,不会有冲突产生。

2)如今咱们假设D2传播到了全部节点(B和C),B和C收到的数据不是从客户产生的,而是别人复制给他们的,因此他们不产生新的版本信息,因此如今B和C所持有的数据仍是D2(A,2)。因而A,B,C上的数据及其版本号都是同样的。

3)若是咱们有一个新的写请求到了B结点上,因而B结点生成数据D3(A,2; B,1),意思是:数据D全局版本号为3,A升了两新,B升了一次。这不就是所谓的代码版本的log么?

4)若是D3没有传播到C的时候又一个请求被C处理了,因而,以C结点上的数据是D4(A,2; C,1)。

5)好,最精彩的事情来了:若是这个时候来了一个读请求,咱们要记得,咱们的W=1 那么R=N=3,因此R会从全部三个节点上读,此时,他会读到三个版本:

    • A结点:D2(A,2)
    • B结点:D3(A,2;  B,1);
    • C结点:D4(A,2;  C,1)

6)这个时候能够判断出,D2已是旧版本(已经包含在D3/D4中),能够舍弃。

7)可是D3和D4是明显的版本冲突。因而,交给调用方本身去作版本冲突处理。就像源代码版本管理同样。

很明显,上述的Dynamo的配置用的是CAP里的A和P。

我很是推你们都去看看这篇论文:《Dynamo:Amazon’s Highly Available Key-Value Store》,若是英文痛苦,你能够看看译文(译者不详)。

相关文章
相关标签/搜索