速学-分布式系统与一致性协议

概述

《分布式系统概念与设计》一书中对分布式系统概念的定义以下:分布式系统是一个硬件或软件组件分布在不一样的网络计算机上,彼此之间仅仅经过消息传递进行通讯和协调的系统html

分布式系统的设计目标通常包括以下几个方面git

  • 可用性:可用性是分布式系统的核心需求,其用于衡量一个分布式系统持续对外提供服务的能力。
  • 可扩展性:增长机器后不会改变或极少改变系统行为,而且能得到近似线性的性能提高。
  • 容错性:系统发生错误时,具备对错误进行规避以及从错误中恢复的能力。
  • 性能:对外服务的响应延时和吞吐率要能知足用户的需求。

分布式架构比单体式架构拥有更多的挑战程序员

  • 节点之间的网络通讯是不可靠的,存在网络延时和丢包等状况。
  • 存在节点处理错误的状况,节点自身随时也有宕机的可能。
  • 同步调用使系统变得不具有可扩展性。

CAP原理

提到分布式系统,就不得不提CAP原理。github

CAP的完整定义为:算法

  • C:Consistency(一致性)。这里的一致性特指强一致,通俗地说,就是全部节点上的数据时刻保持同步。一致性严谨的表述是原子读写,即全部读写都应该看起来是“原子”的,或串行的。全部的读写请求都好像是经全局排序过的同样,写后面的读必定能读到前面所写的内容。
  • A:Availability(可用性)。任何非故障节点都应该在有限的时间内给出请求的响应,不论请求是否成功。
  • P:Tolerance to the partition of network(分区容忍性)。当发生网络分区时(即节点之间没法通讯),在丢失任意多消息的状况下,系统仍然可以正常工做。

CAP原理具备重大的指导意义:在任何分布式系统中,可用性、一致性和分区容忍性这三个方面都是相互矛盾的,三者不可兼得,最多只能取其二。数据库


直观的说明以下:
缓存

1)AP知足但C不知足:若是既要求系统高可用又要求分区容错,那么就要放弃一致性了。由于一旦发生网络分区(P),节点之间将没法通讯,为了知足高可用(A),每一个节点只能用本地数据提供服务,这样就会致使数据的不一致(!C)。一些信奉BASE(Basic Availability, Soft state, Eventually Consistency)原则的NoSQL数据库(例如,Cassandra、CouchDB等)每每会放宽对一致性的要求(知足最终一致性便可),以此来换取基本的可用性。安全

2)CP知足但A不知足:若是要求数据在各个服务器上是强一致的(C),然而网络分区(P)会致使同步时间无限延长,那么如此一来可用性就得不到保障了(!A)。坚持事务ACID(原子性、一致性、隔离性和持久性)的传统数据库以及对结果一致性很是敏感的应用(例如,金融业务)一般会作出这样的选择。性能优化

3)CA知足但P不知足:指的是若是不存在网络分区,那么强一致性和可用性是能够同时知足的。服务器

正如热力学第二定律揭示了任未尝试发明永动机的努力都是徒劳的同样,CAP原理明确指出了完美知足CAP三种属性的分布式系统是不存在的。

了解CAP原理的目的在于,其可以帮助咱们更好地理解实际分布式协议实现过程当中的取舍

一致性

分布式存储系统一般会经过维护多个副原本进行容错,以提升系统的可用性。这就引出了分布式存储系统的核心问题——如何保证多个副本的一致性?

“一致性”有三种含义:

  • Coherence这个单词只在Cache Coherence场景下出现过,其所关注的是多核共享内存的CPU架构下,各个核的Cache上的数据应如何保持一致。

  • Consensus是共识,它强调的是多个提议者就某件事情达成共识,其所关注的是达成共识的过程,例如Paxos协议、Raft选举等。Consensus属于replication protocol的范畴。

  • Consistency表达的含义相对复杂一些,广义上说,它描述了系统自己的不变量的维护程度对上层业务客户端的影响,以及该系统的并发状态会向客户端暴露什么样的异常行为。CAP、ACID中的C都有这层意思。

这里重点讨论的分布式系统中的一致性问题,属于上文中提到的Consensus和Consistency范畴。

分布式系统的一致性是一个具有容错能力的分布式系统须要解决的基本问题。通俗地讲,一致性就是不一样的副本服务器承认同一份数据。一旦这些服务器对某份数据达成了一致,那么该决定即是最终的决定,且将来也没法推翻。

这里有一点须要注意:一致性与结果的正确性没有关系,而是系统对外呈现的状态是否一致(统一)。例如,全部节点都达成一个错误的共识也是一致性的一种表现。

一致性协议就是用来解决一致性问题的,它能使得一组机器像一个总体同样工做,即便其中的一些机器发生了错误也能正常工做。正由于如此,一致性协议在大规模分布式系统中扮演着关键角色。

一致性协议从20世纪80年代开始研究,一致性协议衍生出了不少算法。衡量一致性算法的标准具体以下:

  • 可终止性:非失败进程在有限的时间内可以作出决定,等价于liveness。
  • 一致性:全部的进程必须对最终的决定达成一致,等价于safety。
  • 合法性:算法作出的决定值必须在其余进程(客户端)的指望值范围以内。即客户端请求回答“是”或“否”时,不能返回“不肯定”。

一致性模型

在给定了与操做和状态相关的一些规则的状况下,系统中的操做历史应该老是遵循这些规则。咱们称这些规则为一致性模型

一致性模型分述

对于一致性,能够分别从客户端和服务端两个不一样的视角来理解。

从客户端来看,一致性主要是指多并发访问时如何获取更新过的数据的问题。

从服务端来看,则是更新如何复制分布到整个系统,以保证数据最终的一致性。

所以,能够从两个角度来查看一致性模型:以数据为中心的一致性模型和以用户为中心的一致性模型。

以数据为中心的一致性模型

实现如下这几种一致性模型的难度会依次递减,对一致性强度的要求也依次递减。

  1. 严格一致性(Strong Consistency)

    严格一致性也称强一致性,原子一致性或者是可线性化(Linearizability),是要求最高的一致性模型。严格一致性的要求具体以下:

    • 任何一次读都能读到某个数据的最近一次写的数据。
    • 系统中的全部进程,看到的操做顺序,都与全局时钟下的顺序一致。

    **严格一致性维护的是一个绝对全局时间顺序。**单机系统遵照严格一致性,但对于分布式系统,为每一个操做都分配一个准确的全局时间戳是不可能实现的,因此严格一致性只是存在于理论中的一致性模型。

  2. 顺序一致性(Sequential Consistency)

    顺序一致性,也称为可序列化,比严格一致性要求弱一点,但也是可以实现的最高级别的一致性模型。

    由于全局时钟致使严格一致性很难实现,所以顺序一致性放弃了全局时钟的约束,改成分布式逻辑时钟实现。顺序一致性是指全部的进程都以相同的顺序看到全部的修改。读操做未必可以及时获得此前其余进程对同一数据的写更新,可是每一个进程读到的该数据不一样值的顺序倒是一致的。

    知足顺序一致性的存储系统须要一个额外的逻辑时钟服务。

    下图解释了严格一致性和顺序一致性

    a) 顺序一致性,从这两个进程的角度来看,顺序应该是这样的:Write(y, 2)→Read(x, 0)→Write(x, 4)→Read(y, 2),彻底没有冲突

    b) 严格一致性,从两个进程看到的操做顺序与全局时钟的顺序同样,都是Write(y, 2)→Write(x, 4)→Read(x, 4)→Read(y, 2)。

    c) 不知足顺序一致性,Write(x, 4)→Read(y, 0)→Write(y, 2)→Read(x, 0),这个有冲突


3. 因果一致性(Causal Consistency)

因果关系能够描述成以下状况:

  • 本地顺序:本进程中,事件执行的顺序即为本地因果顺序。
  • 异地顺序:若是读操做返回的是写操做的值,那么该写操做在顺序上必定在读操做以前。
  • 闭包传递:与时钟向量里面定义的同样,若是a→b且b→c,那么确定也有a→c。

不严格地说,因果一致性弱于顺序一致性。

因果一致性和顺序一致性的对比


因果关系可能比较难以理解,下面给你们解释一下

P2写x=7,P2同步到P3,P3读到7

P1写x=2,P1同步到P3,P3读到2

P1写x=4,P1同步到P4,P4读到4

P2同步到P4,P4读到7

永远不会出现先读到4,再读到2的状况

因果关系只保证因果的顺序是正确的,其余的顺序不理会

  1. 可串行化一致性(Serializable Consis-tency)

    若是说操做的历史等同于以某种单一原子顺序发生的历史,但对调用和完成时间没有说明,那么就能够得到称为可序列化的一致性模型。

    在一个可序列化的系统中,有以下所示的这样一个程序:

    x = 1

    x = x + 1

    puts x

    在这里,咱们假设每行表明一个操做,而且全部的操做都成功。由于这些操做能够以任何顺序进行,因此可能打印出nil、1或2。所以,一致性显得很弱。

    但在另外一方面,串行化的一致性又很强,由于它须要一个线性顺序。例如,下面的这个程序:

    print x if x = 3

    x = 1 if x = nil

    x = 2 if x = 1

    x = 3 if x = 2

    它可能不会严格地以咱们编写的顺序发生,但它可以可靠地将x从nil→1→2,更改成3,最后打印出3。

以用户为中心的一致性模型

  1. 最终一致性(Eventual Consistency)

    最终一致性是指若是更新的间隔时间比较长,那么全部的副本都可以最终达到一致性。

    用户读到某一操做对系统特定数据的更新须要一段时间,咱们将这段时间称为“不一致性窗口”。

    在读多写少的场景中,例如CDN,读写之比很是悬殊,若是网站的运营人员修改了一张图片,最终用户延迟了一段时间才看到这个更新实际上问题并非很大。

复制状态机

复制状态机的基本思想是:一个分布式的复制状态机系统由多个复制单元组成,每一个复制单元均是一个状态机,它的状态保存在一组状态变量中。状态机的状态可以而且只能经过外部命令来改变。

上文提到的“一组状态变量”一般是基于操做日志来实现的。每个复制单元存储一个包含一系列指令的日志,而且严格按照顺序逐条执行日志上的指令。

因此,在复制状态机模型下,一致性算法的主要工做就变成了如何保证操做日志的一致性

复制状态机的运行过程以下图所示:


服务器上的一致性模块负责接收外部命令,而后追加到本身的操做日志中。它与其余服务器上的一致性模块进行通讯以保证每个服务器上的操做日志最终都以相同的顺序包含相同的指令。一旦指令被正确复制,那么每个服务器的状态机都将按照操做日志的顺序来处理它们,而后将输出结果返回给客户端。

复制状态机在分布式系统中常被用于解决各类容错相关的问题,例如,GFS、HDFS、Chubby、ZooKeeper和etcd等分布式系统都是基于复制状态机模型实现的。

须要注意的是,指令在状态机上的执行顺序并不必定等同于指令的发出顺序或接收顺序。

复制状态机只是保证全部的状态机都以相同的顺序执行这些命令。

拜占庭将军问题

拜占庭位于现在土耳其的伊斯坦布尔,是东罗马帝国的首都。因为当时拜占庭罗马帝国幅员辽阔,出于防护的缘由,每一个军队都相隔甚远,将军与将军之间只能靠信差来传递消息。发生战争时,拜占庭军队内全部将军必需达成共识,决定是否攻击敌人。可是军队内可能存在叛徒和敌军的间谍扰乱将军们的决定,所以在进行共识交流时,结果可能并不能真正表明大多数人的意见。这时,在已知有成员不可靠的状况下,其他忠诚的将军如何排除叛徒或间谍的影响来达成一致的决定,就是著名的拜占庭将军问题。

拜占庭将军问题讲述的是一个共识问题。拜占庭将军问题是对现实世界的模型化。

  • 拜占庭错误是一个overly pessimistic模型(最悲观、最强的错误模型)

    研究这个模型的意义在于:若是某个一致性协议可以保证系统在出现N个拜占庭错误时,依旧能够作出一致性决定,那么这个协议也就可以处理系统出现N个其余任意类型的错误。

  • 进程失败错误(fail-stop Failure,如同宕机)则是一个overly optimistic模型(最乐观、最弱的错误模型)

    研究这个模型的意义在于:若是某个一致性协议在系统出现N个进程失败错误时都没法保证作出一致性决定,那么这个协议也就没法处理系统出现N个其余任意类型的错误。

Fred Schneider在前面提到的论文《Implementing fault-tolerant services using thestate machine approach》中指出了这样一个基本假设:

一个RSM(分布式状态机)系统要容忍N个拜占庭错误,至少须要2N+1个复制节点。

若是只是把错误的类型缩小到进程失败,则至少须要N+1个复制节点才能容错。

可是不是只要知足上文提到的2N+1个要求就能保证万无一失了呢?很不幸,答案是否认的。

FLP不可能性

FLP不可能性是分布式领域中一个很是著名的定理:

No completely asynchronous consensusprotocol can tolerate even a single unan-nounced process death.

异步通讯场景下,任何一致性协议都不能保证,即便只有一个进程失败,其余非失败进程也不能达成一致。

这里的进程失败(unannounced process death)指的是一个进程发生了故障,但其余节点并不知道,继续认为这个进程尚未处理完成或发生消息延迟了。

举个例子:

甲、乙、丙三我的各自分开进行投票(投票结果是0或1)。他们彼此能够经过电话进行沟通,但有人会睡着。例如:甲投票0,乙投票1,这时候甲和乙打平,丙的选票就很关键。然而丙睡着了,在他醒来以前甲和乙都将没法达成最终的结果。即便从新投票,也有可能陷入无尽的循环之中。

FLP定理实际上说明了在容许节点失效的场景下,基于异步通讯方式的分布式协议,没法确保在有限的时间内达成一致性。用CAP理论解释的话,在P的条件下,没法知足C和A。

请注意,这个结论的前提是异步通讯。在分布式系统中,“异步通讯”与“同步通讯”的最大区别是没有时钟、不能时间同步、不能使用超时、不能探测失败、消息可任意延迟、消息可乱序等。

因此,实际的一致性协议(Paxos、Raft等)在理论上都是有缺陷的,最大的问题是理论上存在不可终止性!但他们都作了一些调整,下降了几率。

Paxos协议

大神Leslie Lamport对相似拜占庭将军这样的问题进行了深刻研究,并发表了几篇论文。

总结起来就是回答以下的三个问题:

1)相似拜占庭将军这样的分布式一致性问题是否有解?

2)若是有解的话须要知足什么样的条件?

3)基于特定的前提条件,提出一种解法。

Leslie Lamport在论文“拜占庭将军问题”中已经给出了前两个问题的回答,而第三个问题在他的论文“The Part-Time Parliament”中提出了一种基于消息传递的一致性算法

下面讲述的就是大神的平常操做:

1990年,Lamport向ACM Transac-tions on Computer Systems提交了他那篇关于Paxos算法的论文。主编回信建议他用数学而不是神话描述他的算法,不然他们不会考虑接受这篇论文。Lamport以为那些人太迂腐,拒绝作任何修改,转而将论文贴在了本身的我的博客上。

起初Paxos算法因为难以理解并无引发多少人的重视,直到2006年Google的三大论文初现“云”端,其中Chubby Lock服务使用了Paxos做为Chubby Cell的一致性算法,这件事使得Paxos算法的人气今后一路飙升,几乎垄断了一致性算法领域。在Raft算法诞生以前,Paxos几乎成了一致性协议的代名词。

Lamport本人以为Paxos很简单,但事实上对于大多数人来讲,Paxos仍是太难理解了。

引用NSDI社区上的一句话就是:全世界真正理解Paxos算法的人只有5个!

这可能就是人和神之间的区别吧。

而后,更容易理解的一致性算法Raft诞生了。

Raft协议:为可理解性而生

终于讲到Raft了,我太不容易了。

Raft算法主要使用两种方法来提升可理解性。提升理解性主要经过两个经常使用手段

  1. 问题分解

    尽量地将问题分解成为若干个可解决的、更容易理解的小问题——这是众所周知的简化问题的方法论。例如,Raft算法把问题分解成了领袖选举(leader election)、日志复制(log repli-cation)、安全性(safety)和成员关系变化(membershipchanges)这几个子问题。

    • 领袖选举:在一个领袖节点发生故障以后必须从新给出一个新的领袖节点。
    • 日志复制:领袖节点从客户端接收操做请求,而后将操做日志复制到集群中的其余服务器上,而且强制要求其余服务器的日志必须和本身的保持一致。
    • 安全性:Raft关键的安全特性是下文提到的状态机安全原则(State Machine Safety)——若是一个服务器已经将给定索引位置的日志条目应用到状态机中,则全部其余服务器不会在该索引位置应用不一样的条目。下文将会证实Raft是如何保证这条原则的。
    • 成员关系变化:配置发生变化的时候,集群可以继续工做。
  2. 减小状态空间

    Raft算法经过减小须要考虑的状态数量来简化状态空间。这将使得整个系统更加一致而且可以尽量地消除不肯定性。

Raft有几点重要的创新

  • 强领导人。Raft使用一种比其余算法更强的领导形式。例如,日志条目只从领导人发向其余服务器。这样就简化了对日志复制的管理,提升了Raft的可理解性。
  • 领袖选举。Raft使用随机定时器来选举领导者。这种方式仅仅是在全部算法都须要实现的心跳机制上增长了一点变化,就使得冲突解决更加简单和快速。
  • 成员变化。Raft在调整集群成员关系时使用了新的一致性(joint consensus,联合一致性)方法。使用这种方法,使得集群配置在发生改变时,集群依旧可以正常工做。

Raft一致性算法

基本概念

  1. Leader(领袖)

  2. Candidate(候选人)

  3. Follower(群众)

  4. 任期(Term):Raft算法将时间划分红为任意个不一样长度的任期,任期是单调递增的,用连续的数字(1, 2, 3……)表示。在Raft的世界里,每个任期的开始都是一次领导人的选举。若是一个候选人赢得了选举,那么它就会在该任期的剩余时间内担任领导人。在某些状况下,选票会被瓜分,致使没有哪位候选人可以获得超过半数的选票,这样本次任期将以没有选出领导人而结束。那么,系统就会自动进入下一个任期,开始一次新的选举。Raft算法保证在给定的一个任期内最多只有一个领导人。某些Term会因为选举失败,存在没有领导人的状况,如t3所示。


任期在Raft中起着逻辑时钟的做用,同时也可用于在Raft节点中检测过时信息——好比过时的领导人。每一个Raft节点各自都在本地维护一个当前任期值,触发这个数字变化(增长)主要有两个场景:开始选举和与其余节点交换信息。若是一个节点(包括领导人)的当前任期号比其余节点的任期号小,则将本身本地的任期号自觉地更新为较大的任期号。若是一个候选人或者领导人意识到它的任期号过期了(比别人的小),那么它会马上切换回群众状态;若是一个节点收到的请求所携带的任期号是过期的,那么该节点就会拒绝响应本次请求。

领导人选举

Raft经过选举一个权力至高无上的领导人,并采起赋予他管理复制日志重任的方式来维护节点间复制日志的一致性

领导人从客户端接收日志条目,再把日志条目复制到其余服务器上,而且在保证安全性的前提下,告诉其余服务器将日志条目应用到它们的状态机中。领导人能够决定新的日志条目须要放在日志文件的什么位置,而不须要和其余服务器商议,而且数据都是单向地从领导人流向其余服务器。

领导人选举的过程,就是Raft三种角色切换的过程

开始的时候,系统有一个Leader和众多Follower

  1. 每个Raft节点(包含Follower)内有一个选举定时器,若是收到Leader广播的心跳包,则Follower将选举定时器重置。
  2. 若是Follower的选举定时器时间到了,在这个期间没有收到任何心跳包,Follower认为Leader,本身能够当Leader了,就开始发起选举,主要作以下步骤
    • 将本身本地维护的当前任期号(current_term_id)加1
    • 将本身的状态切换到候选人(Candidate),并为本身投票
    • 向其所在集群中的其余节点发送RequestVote RPC(RPC消息会携带“current_term_id”值),要求它们投票给本身
    • 设置选举超时时间,通常是随机选取一个区间中的值
  3. 在Candidate状态下
    • 若是获得大多数选票,则成为Leader
    • 若是发现已经产生了新的领导人或者有更大的任期,则状态更改成Follower
    • 若是选举超时时间过了,候选人自增任期号(Term++)而且发起新一轮的拉选票活动
  4. 在Leader状态下,若是发现了更高的任期,则将本身变动为Follower

日志复制

一旦某个领导人赢得了选举,那么它就会开始接收客户端的请求。领导人将把这条指令做为一条新的日志条目加入它的日志文件中,而后并行地向其余Raft节点发起AppendEntriesRPC,要求其余节点复制这个日志条目。当这个日志条目被“安全”地复制以后,Leader会将这条日志应用(apply,即执行该指令)到它的状态机中,而且向客户端返回执行结果。若是Follower发生错误,运行缓慢没有及时响应AppendEntries RPC,或者发生了网络丢包的问题,那么领导人会无限地重试AppendEntries RPC(甚至在它响应了客户端以后),直到全部的追随者最终存储了和Leader同样的日志条目。

日志由有序编号的日志条目组成。每个日志条目通常均包含三个属性:整数索引(log index)、任期号(term)和指令(command)。通常以下所示:


一旦领导人建立的条目已经被复制到半数以上的节点上了,那么这个条目就称为可被提交的。

Raft日志复制主要流程以下:

  1. 客户端向Leader发送写请求。
  2. Leader将写请求解析成操做指令追加到本地日志文件中。
  3. Leader为每一个Follower广播AppendEntries RPC。
  4. Follower经过一致性检查,选择从哪一个位置开始追加Leader的日志条目。
  5. 一旦日志项提交成功,Leader就将该日志条目对应的指令应用(apply)到本地状态机,并向客户端返回操做结果。
  6. Leader后续经过AppendEntries RPC将已经成功(在大多数节点上)提交的日志项告知Follower。
  7. Follower收到提交的日志项以后,将其应用至本地状态机。

从上面的步骤能够看出,针对Raft日志条目有两个操做,提交(commit)和应用(apply),应用必须发生在提交以后,即某个日志条目只有被提交以后才能被应用到本地状态机上。

流程图以下:https://www.processon.com/view/link/5fa6c1045653bb25634dea4a

安全性

本文介绍如何在领导人选举部分加入一个限制规则来保证——任何的领导人都拥有以前任期提交的所有日志条目。

  1. 怎样才能具备成为领导人的资格?- 必须包含全部已提交日志条目

    RequestVote RPC的接收方有一个检查:若是Follower本身的日志比RPC调用方(候选人)的日志更加新,就会拒绝候选人的投票请求。

    这就Raft算法使用投票的方式来阻止那些没有包含全部已提交日志条目的节点赢得选举。

  2. 如何判断日志已经提交?

    在提交以前term的日志项时,必须保证当前term新建的日志项已经复制到超过半数节点。这样,以前term的日志项才算真正提交的。

可用性与时序性

broadcastTime << electionTimeout << MTBF

broadcastTime指的是一个节点向集群中其余节点发送RPC,而且收到它们响应的平均时间。

electionTimeout就是选举超时时间。

MTBF指的是单个节点发生故障的平均时间间隔。

为了使领导人可以持续发送心跳包来阻止下面的Follower发起选举,broadcastTime应该比electionTimeout小一个数量级。

异常状况

####追随者/候选人异常

Raft算法经过领导人无限的重试来应对这些失败,直到故障的节点重启并处理了这些RPC为止。

由于Raft算法中的RPC都是幂等的,所以不会有什么问题。

领导人异常


Raft数据交换流程如上图所示,在任什么时候刻,领导人都有可能崩溃。

  1. 数据在到达Leader以前


不影响一致性

  1. 数据到达Leader节点,但未复制到Follower节点


不影响一致性

若是在这个阶段Leader出现故障,此时数据属于未提交状态,那么Client不会收到ACK,而是会认为超时失败可安全发起重试。Follower节点上没有该数据,从新选主后Client重试从新提交可成功。原来的Leader节点恢复以后将做为Follower加入集群,从新从当前任期的新Leader处同步数据,与Leader数据强制保持一致。

  1. 数据到达Leader节点,成功复制到Follower的部分节点上,但还未向Leader响应接收


数据不丢失,也不影响一致性

若是在这个阶段Leader出现故障,此时数据在Follower节点处于未提交状态(Uncommitted)且不一致,那么Raft协议要求投票只能投给拥有最新数据的节点。因此拥有最新数据的节点会被选为Leader,再将数据强制同步到Follower,数据不会丢失而且可以保证最终一致。

  1. 数据到达Leader节点,成功复制到Follower的全部节点上,但还未向Leader响应接收


数据不丢失,也不影响一致性

若是在这个阶段Leader出现故障,虽然此时数据在Fol-lower节点处于未提交状态(Uncommitted),但也能保持一致,那么从新选出Leader后便可完成数据提交,因为此时客户端不知到底有没有提交成功,所以可重试提交。针对这种状况,Raft要求RPC请求实现幂等性,也就是要实现内部去重机制。

  1. 数据到达Leader节点,成功复制到Follower的全部或大多数节点上,数据在Leader上处于已提交状态,但在Follower上处于未提交状态


数据不丢失,也不影响一致性

  1. 数据到达Leader节点,成功复制到Follower的全部或大多数节点上,数据在全部节点都处于已提交状态,但还未响应Client


数据不丢失,也不影响一致性

  1. 网络分区致使的脑裂状况,出现双Leader


不影响一致性

网络分区将原先的Leader节点和Follower节点分隔开,Follower收不到Leader的心跳将发起选举产生新的Leader。这时就产生了双Leader,原先的Leader独自在一个区,向它提交数据不可能复制到大多数节点上,因此永远都是提交不成功。向新的Leader提交数据能够提交成功,网络恢复后旧的Leader发现集群中有更新任期(Term)的新Leader,则自动降级为Fol-lower并重新Leader处同步数据达成集群数据一致。

总结

分布式系统和通常的业务系统区别仍是挺大的,涉及到更多论文、数理知识,更加偏学术一些。随着计算机的不断发展,关于分布式的知识,仍是须要进行掌握的。

关于Raft算法,建议看一下源码https://github.com/etcd-io/etcd/tree/master/raft。

Raft经过领导选举机制,简化了总体的复杂性。利用日志复制+复制状态机,保证状态执行的一致。同时设置了一些对应的安全规则,增强了日志复制的安全,维护了一致性。

若是你们时间有限,能够只看一下CAP、复制状态机和Raft。

资料

  1. https://www.infoq.cn/article/wechat-serial-number-generator-architecture/ 微信序列号生成器架构设计及演变

  2. 从微信朋友圈的评论可见性,谈因果一致性在分布式系统中的应用

  3. https://www.jianshu.com/p/ab511132a34f raft 系列解读(3) 之 代码实现

  4. https://blog.csdn.net/lanyang123456/article/details/109279234 raft协议中的日志安全性

  5. http://www.duokan.com/book/180790 云原生分布式存储基石:etcd深刻解析

最后

你们若是喜欢个人文章,能够关注个人公众号(程序员麻辣烫)

个人我的博客为:https://shidawuhen.github.io/

往期文章回顾:

技术

  1. 微服务之服务框架和注册中心
  2. Beego框架使用
  3. 浅谈微服务
  4. TCP性能优化
  5. 限流实现1
  6. Redis实现分布式锁
  7. Golang源码BUG追查
  8. 事务原子性、一致性、持久性的实现原理
  9. CDN请求过程详解
  10. 记博客服务被压垮的历程
  11. 经常使用缓存技巧
  12. 如何高效对接第三方支付
  13. Gin框架简洁版
  14. InnoDB锁与事务简析
  15. 算法总结
  16. 分布式系统与一致性协议

读书笔记

  1. 敏捷革命
  2. 如何锻炼本身的记忆力
  3. 简单的逻辑学-读后感
  4. 热风-读后感
  5. 论语-读后感
  6. 孙子兵法-读后感

思考

  1. 对项目管理的一些见解
  2. 对产品经理的一些思考
  3. 关于程序员职业发展的思考
  4. 关于代码review的思考
  5. Markdown编辑器推荐-typora
相关文章
相关标签/搜索