介绍常见的分布式一致性协议html
CAP理论又称之为布鲁尔定理(Brewer’S theorem),认为在设计一个大规模可扩放的网络服务时候不能同时兼容:一致性(consistency)、可用性(Availability)、分区容错(Partition-tolerance)。git
一致性:在分布式系统中的全部数据备份,在同一时刻是否有一样的值。(等同于全部节点访问同一份最新的数据副本)github
可用性:在集群中一部分节点故障后,集群总体是否还能响应客户端的读写请求。算法
分区容忍性:以实际效果而言,分区至关于对通讯的时限要求。系统若是不能在时限内达成数据一致性,就意味着发生了分区的状况,必须就当前操做在C和A之间作出选择数据库
CAP理论容易理解,网上也有关于该理论的说明,包括模型的简易证实;弱条件下模型的成立等。apache
参考资料:安全
http://www.cnblogs.com/mmjx/archive/2011/12/19/2290540.html服务器
http://nathanmarz.com/blog/how-to-beat-the-cap-theorem.html网络
BASE是Basically Available(基本可用)、Soft state(软状态)和Eventually consistent(最终一致性)三个短语的简写,BASE是对CAP中一致性和可用性权衡的结果,其来源于对大规模互联网系统分布式实践的结论,是基于CAP定理逐步演化而来的,其核心思想是即便没法作到强一致性(Strong consistency),但每一个应用均可以根据自身的业务特色,采用适当的方式来使系统达到最终一致性(Eventual consistency)架构
基本可用:分布式系统在出现故障的时候,容许损失部分可用性,即保证核心可用。
软状态:容许系统存在中间状态,而该中间状态不会影响系统总体可用性
最终一致性:系统中的全部数据副本通过必定时间后,最终可以达到一致的状态
两阶段提交(Two-phaseCommit)是指,在计算机网络以及数据库领域内,为了使基于分布式系统架构下的全部节点在进行事务提交时保持一致性而设计的一种算法。
在分布式系统中,每一个节点虽然能够知晓本身的操做时成功或者失败,却没法知道其余节点的操做的成功或失败。当一个事务跨越多个节点时,为了保持事务的ACID特性,须要引入一个做为协调者的组件来统一掌控全部节点(称做参与者)的操做结果并最终指示这些节点是否要把操做结果进行真正的提交。
如上图示,该协议分为两个阶段:
请求阶段(commit-request phase,或称表决阶段,voting phase)
在请求阶段,协调者将通知事务参与者准备提交或取消事务,而后进入表决过程。在表决过程当中,参与者将告知协调者本身的决策:赞成(事务参与者本地做业执行成功)或取消(本地做业执行故障)。
提交阶段(commit phase)
在该阶段,协调者将基于第一个阶段的投票结果进行决策:提交或取消。当且仅当全部的参与者赞成提交事务协调者才通知全部的参与者提交事务,不然协调者将通知全部的参与者取消事务。参与者在接收到协调者发来的消息后将执行相应的操做。
二阶段提交算法的最大缺点就在于它在执行过程当中间,节点都处于阻塞态。即节点之间在等待对方的响应消息时,它将什么也作不了。特别是,当一个节点在已经占有了某项资源的状况下,为了等待其余节点的响应消息而陷入阻塞状态时,当第三个节点尝试访问该节点占有的资源时,这个节点也将连带陷入阻塞状态。
另外,协调者节点指示参与者节点进行提交等操做时,若有参与者节点出现了崩溃等状况而致使协调者始终没法获取全部参与者的响应信息,这时协调者将只能依赖协调者自身的超时机制来生效。但每每超时机制生效时,协调者都会指示参与者进行回滚操做。这样的策略显得比较保守。
参考资料
与两阶段提交不一样的是,三阶段提交是“非阻塞”协议。三阶段提交在两阶段提交的第一阶段与第二阶段之间插入了一个准备阶段,同时对于协调者和参与者都设置了超时机制,使得原先在两阶段提交中,参与者在投票以后,因为协调者发生崩溃或错误,而致使参与者处于没法知晓是否提交或者停止的“不肯定状态”所产生的可能至关长的延时的问题得以解决。
如上图示,该协议分为三个阶段:
协调者向参与者发送commit请求,参与者若是能够提交就返回Yes响应,不然返回No响应。
preCommit
协调者根据参与者的反应状况来决定是否能够进行事务的PreCommit操做。根据响应状况,有如下两种可能.
发送预提交请求,Coordinator向Cohort发送PreCommit请求,并进入Prepared阶段。
事务预提交,Cohort接收到PreCommit请求后,会执行事务操做,并将undo和redo信息记录到事务日志中。
响应反馈,若是Cohort成功的执行了事务操做,则返回ACK响应,同时开始等待最终指令。
发送中断请求,Coordinator向全部Cohort发送abort请求。
中断事务,Cohort收到来自Coordinator的abort请求以后(或超时以后,仍未收到Cohort的请求),执行事务的中断。
doCommit
该阶段进行真正的事务提交,也能够分为如下两种状况:
A.发送提交请求。Coordinator接收到Cohort发送的ACK响应,那么他将从预提交状态进入到提交状态。并向全部Cohort发送doCommit请求。
B.事务提交。Cohort接收到doCommit请求以后,执行正式的事务提交。并在完成事务提交以后释放全部事务资源。
C.响应反馈。事务提交完以后,向Coordinator发送ACK响应。
D.完成事务。Coordinator接收到全部Cohort的ACK响应以后,完成事务。
参考资料:
在常见的分布式系统中,总会发生诸如机器宕机或网络异常等状况。Paxos算法须要解决的问题就是如何在一个可能发生上述异常的分布式系统中,快速且正确地在集群内部对某个数据的值达成一致,而且保证不论发生以上任何异常,都不会破坏整个系统的一致性。这里某个数据的值并不仅是狭义上的某个数,它能够是一条日志,也能够是一条命令(command)等,根据应用场景不一样,某个数据的值有不一样的含义。
在Paxos算法中,有三种角色:
Proposer:提案发起者,向集群中的其余节点发一个提案Proposal,该提案中有一个对应的值。
Acceptor:提案接受者,负责处理接收到的提议,他们的回复就是一次投票。会存储一些状态来决定是否接收一个值。
Learners:不参与提案的发起和投票,学习已经被Acceptor接受的提案
保证最终有一个value会被选定,当value被选定后,进程最终也能获取到被选定的value。
安全性原则:保证不能作错的事。只能有一个值被批准,不能出现第二个值把第一个覆盖的状况;每一个节点只能学习到已经被批准的值,不能学习没有被批准的值。
存活原则:只要有多数服务器存活而且彼此间能够通讯最终都要作到的事。最终会批准某个被提议的值;一个值被批准了,其余服务器最终会学习到这个值。
Basic Paxos算法分为两个阶段。具体以下:
Proposer选择一个提案编号N,而后向半数以上的Acceptor发送编号为N的Prepare请求。
若是一个Acceptor收到一个编号为N的Prepare请求,且N大于该Acceptor已经响应过的全部Prepare请求的编号,那么它就会将它已经接受过的编号最大的提案(若是有的话)做为响应反馈给Proposer,同时该Acceptor承诺再也不接受任何编号小于N的提案。
若是Proposer收到半数以上Acceptor对其发出的编号为N的Prepare请求的响应,那么它就会发送一个针对[N,V]提案的Accept请求给半数以上的Acceptor。注意:V就是收到的响应中编号最大的提案的value,若是响应中不包含任何提案,那么V就由Proposer本身决定。
若是Acceptor收到一个针对编号为N的提案的Accept请求,只要该Acceptor没有对编号大于N的Prepare请求作出过响应,它就接受该提案。
示例过程以下,其中MaxN,AccerptN,AcceptV分别表示Acceptor的最大响应提案编号,接受的最大编号和接受编号对应的值:
1)只有一个Acceptor,只要Acceptor接受它收到的第一个提案并被选定,则能够保证只有一个value会被选定。 2)多个Acceptor 提案(Propersal) = [value] 即便只有一个Proposer提出了一个value,该value也最终被选定 => P1:一个Acceptor必须接受它收到的第一个提案。 => 若是每一个Proposer分别提出不一样的value,发给不一样的Acceptor, Acceptor分别接受本身收到的value,就致使不一样的value被选定。 => 一个提案被选定须要被半数以上的Acceptor接受 => 一个Acceptor必须可以接受不止一个提案 => 提案(Propersal) = [提案编号,value] => 必须保证全部被选定的提案都具备相同的value值 => P2:若是某个value为v的提案被选定了,那么每一个编号更高的被选定提案的value必须也是v。=> P2a:若是某个value为v的提案被选定了,那么每一个编号更高的被Acceptor接受的提案的value必须也是v。=> 分区不可用不符合 => P2b:若是某个value为v的提案被选定了,那么以后任何Proposer提出的编号更高的提案的value必须也是v。=> P2c:对于任意的N和V,若是提案[N, V]被提出,那么存在一个半数以上的Acceptor组成的集合S,知足如下两个条件中的任意一个: - S中每一个Acceptor都没有接受过编号小于N的提案。 - S中Acceptor接受过的最大编号的提案的value为V。=> Proposer生成提案,须要先询问集群中其余的节点,肯定可被接受的value。=> Acceptor接受提案,一个Acceptor只要还没有响应过任何编号大于N的Prepare请求,那么他就能够接受这个编号为N的提案。
basic paxos是由client发起的同步过程,在两阶段返回前,client不能获得成功的返回。引用wiki上的流程图:
multi-paxos 在basic paxos的二阶段上引入了一个机制,先运行一次完整的paxos算法选举出leader,由leader处理全部的读写请求,而后省略掉prepare过程,让本身看起来像一阶段同样。multi-paxos强leader状态的流程图:
流程图中没有了basic paxos的两阶段,变成了一个一阶段的递交协议:
一阶段a:发起者(leader)直接告诉Acceptor,准备递交协议号为I+1的协议
一阶段b:收到了大部分acceptor的回复后(图中是所有),acceptor就直接回复client协议成功写入
Multi-Paxos能够简单的理解为, 通过一轮的Basic Paxos, 成功获得多数派accept的proposer即成为leader(这个过程称为leader Election), 以后能够经过lease机制, 保持这个leader的身份, 使得其余proposer再也不发起提案, 这样就进入了一个leader任期。在leader任期中, 因为没有了并发冲突, 这个leader在对后续的日志进行投票时, 没必要每次都向多数派询问提案, 也没必要执行prepare阶段, 直接执行accept阶段便可。
所以在Multi-Paxos中, 咱们将leader Election过程当中的prepare操做, 视为对leader任期内将要写的全部日志的一次性prepare操做, 在leader任期内投票的全部日志将携带有相同的提案编号. 须要强调的是, 为了遵照Basic Paxos协议约束, 在leader Election的prepare阶段, acceptor应答prepare成功的消息以前要先将此次prepare请求所携带的提案编号持久化到本地。
参考资料:
[https://en.wikipedia.org/wiki/Paxos_(computer_science)](https://en.wikipedia.org/wiki/Paxos_(computer_science)
http://www.infoq.com/cn/articles/weinxin-open-source-paxos-phxpaxos
与Paxos不一样Raft强调的是易懂(Understandability),Raft和Paxos同样只要保证n/2+1节点正常就可以提供服务;众所周知但问题较为复杂时能够把问题分解为几个小问题来处理,Raft也使用了分而治之的思想把算法流程分为三个子问题:选举(Leader election)、日志复制(Log replication)、安全性(Safety)三个子问题。
在任意的时间,每个服务器必定会处于如下三种状态中的一个:Leader、Candidate、Follower。在正常状况下,只有一个服务器是Leader,剩下的服务器是Follower。Follower们是被动的:他们不会发送任何请求,只是响应来自领导人和候选人的请求;Leader来处理全部来自客户端的请求(若是一个客户端与追随者进行通讯,追随者会将信息发送给领导人);Candidate是用来选取一个新的领导人的,在Leader肯定后降级为Follower。以下图为各个角色的转换流程:
在Raft中使用了一个能够理解为周期(第几届、任期)的概念,用Term做为一个周期,每一个Term都是一个连续递增的编号,每一轮选举都是一个Term周期,在一个Term中只能产生一个Leader:
每一台服务器都存储着一个当前Term的数字,这个数字会单调的增长。当服务器之间进行通讯时,会互相交换当前Term值;若是一台服务器的当前Term比其它服务器的小,则更新为较大的值。若是一个候选人或者领导人意识到它的Term过期了,它会马上转换为Follower状态。若是一台服务器收到的请求的Term是过期的,那么它会拒绝这次请求。
RequestVote RPC:Candidate在选举过程当中触发,用于从Follower获取选票。
AppendEntries RPC:Leader触发的,为的是复制日志条目和提供一种心跳机制。
在Leader election中有两个超时时间,分别为:
Election timeout:Follower转变为Candidate的超时时间,是一个随机值。在选举Leader过程当中,若是一个Follower没有在Election timeout时间内收到来自Leader的AppendEntries RPC请求或者Candidate的RequestVote RPC请求的话,则Follower会转变为Candidate,并发起选举。
Heartbeate timeout:Leader存在时,Follower在Heartbeat timeout时间内没有接收到Leader的AppendEntires请求,则认为Leader已经下线,本身会转变为Candidate,发起选举。
开始时,每一个节点都以Follower启动,在第一轮时,因为没有收到任何的RPC请求,会触发Election timeout,节点会自增Term,转为Candidate,发起选举,不断重试,直到知足如下任一条件:
得到超过半数Server的投票,转换为Leader,广播Heartbeat
接收到合法Leader的AppendEntries RPC,转换为Follower
选举超时,没有Server选举成功,自增currentTerm,从新选举
Candidate在等待投票结果的过程当中,可能会接收到来自其它Leader的AppendEntries RPC。若是该Leader的Term不小于本地的currentTerm,则承认该Leader身份的合法性,主动降级为Follower;反之,则维持Candidate身份,继续等待投票结果;Candidate既没有选举成功,也没有收到其它Leader的RPC请求,这种状况通常出如今多个节点同时发起选举,最终每一个Candidate都将超时。为了减小冲突,这里采起“随机退让”策略,每一个Candidate重启选举定时器(随机值),大大下降了冲突几率。
Log Replication主要做用是用于保证节点的一致性,这阶段所作的操做也是为了保证一致性与高可用性;当Leader选举出来后便开始负责客户端的请求,全部事务(更新操做)请求都必须先通过Leader处理。
正常操做流程:
Client发送command给Leader
Leader追加command至本地log
Leader广播AppendEntries RPC至Followers
一旦收到知足数量的日志项committed成功的响应:
Leader应用对应的command至本地State Machine,并返回结果至Client
Leader经过后续Append EntriesRPC将committed日志项通知到Follower
Follower收到committed日志项后,将其应用至本地State Machine
安全性是用于保证每一个节点都执行相同序列的安全机制, Raft算法保证如下属性时刻为真:
Election Safety:在任意指定Term内,最多选举出一个Leader
Leader Append-Only
Log Matching:若是两个节点上的日志项拥有相同的Index和Term,那么这两个节点[0, Index]范围内的Log彻底一致
Leader Completeness:若是某个日志项在某个Term被commit,那么后续任意Term的Leader均拥有该日志项
State Machine Safety
一旦某个server将某个日志项应用于本地状态机,之后全部server对于该偏移都将应用相同日志项
集群拓扑变化的意思是在运行过程当中多副本集群的结构性变化,如增长/减小副本数、节点替换等 。为了防止出现多主的状况,raft使用了两阶段提交来处理。假设在Raft中,老集群配置用Cold表示,新集群配置用Cnew表示,整个集群拓扑变化的流程以下:
当集群成员配置改变时,leader收到人工发出的重配置命令从Cold切成Cnew;
Leader副本在本地生成一个新的log entry,其内容是Cold∪Cnew,表明当前时刻新旧拓扑配置共存,写入本地日志,同时将该log entry推送至其余Follower节点;
Follower副本收到log entry后更新本地日志,而且此时就以该配置做为本身了解的全局拓扑结构;若是多数Follower确认了Cold∪Cnew这条日志的时候,Leader就Commit这条log entry;
接下来Leader生成一条新的log entry,其内容是全新的配置Cnew,一样将该log entry写入本地日志,同时推送到Follower上;
Follower收到新的配置日志Cnew后,将其写入日志,而且今后刻起,就以该新的配置做为系统拓扑,而且若是发现本身不在Cnew这个配置中会自动退出;
Leader收到多数Follower的确认消息之后,给客户端发起命令执行成功的消息。
参考资料:
ZAB 协议是为分布式协调服务 ZooKeeper 专门设计的一种支持崩溃恢复的原子广播协议。在 ZooKeeper 中,主要依赖 ZAB 协议来实现分布式数据一致性,基于该协议,ZooKeeper 实现了一种主备模式的系统架构来保持集群中各个副本之间的数据一致性。
Peer:节点。表明了系统中的进程,每每系统有多个进程,也就有多个节点提供服务
Quorum:多数。当有N个Peer,多数就表明Q(Q>N/2)个Peer
Leader:主节点,最多存在一个。表明zookeeper系统中主要的工做进程,Leader才是真正处理全部zookeeper写请求的节点,写请求会从Leader广播到Quorum Follower
Follower:从节点,能够有多个。若是client对zookeeper发起一个读请求,Follower能够直接处理。若是client对zookeeper发起一个写请求,Follower须要转发到惟一的Leader,再有Leader处理并发起广播
transaction:事务,一次写请求就表明一次事务
zxid:事务id。zk为了保证消息有序性,提出了事务编号这个概念。zxid是一个二元组,e表明选举纪元,c表明事务的计数,同一纪元内每次出现写请求,c自增。容易理解,纪元不变时,计数不断增长,纪元变化时,计数清零。
acceptedEpoch:follower 已经接受的 leader 更改纪元的提议。
state:节点状态,目前只有这三种:
election:节点处于选举状态
leading:当前节点是 leader,负责协调事务
following:当前节点是跟随者,服从 leader 节点的命令
代码实现中多了一种:observing 状态,这是 Zookeeper 引入 Observer 以后加入的,Observer 不参与选举,是只读节点,跟 ZAB 协议没有关系节点的持久状态。
history:当前节点接收到事务提议的 log。
lastZxid:history 中最近接收到的提议的 zxid (最大的)。
随着系统启动或者恢复,会经历Zab协议中描述的以下四个阶段:
Phase 0:Leader选举,每一个peer从Quorum peer中选出本身心中的准leader。节点在一开始都处于选举阶段,只要有一个节点获得超半数节点的票数,它就能够当选准 leader。这一阶段的目的是就是为了选出一个准 leader,而后进入下一个阶段。
Phase 1:发现,准leader从Quorum Follower中发现最新的数据,并覆盖本身的过时数据。在这个阶段,followers 跟准 leader 进行通讯,同步 followers 最近接收的事务提议。这一阶段的主要目的是发现当前大多数节点接收的最新提议,而且准 leader 生成新的 epoch,让 followers 接受,更新它们的 acceptedEpoch。
Phase 2:同步,准leader采用二阶段提交的方式将本身的最新数据同步给Quorum Follower。完成这个步骤,准leader就转为正式leader。同步阶段主要是利用 leader 前一阶段得到的最新提议历史,同步集群中全部的副本。只有当 quorum 都同步完成,准 leader 才会成为真正的 leader。follower 只会接收 zxid 比本身的 lastZxid 大的提议。
Phase 3:广播,Leader接受写请求,并经过二阶段提交的方式广播给Quorum Follower。到了这个阶段,Zookeeper 集群才能正式对外提供事务服务,而且 leader 能够进行消息广播。同时若是有新的节点加入,还须要对新节点进行同步。ZAB 提交事务并不像 2PC 同样须要所有 follower 都 ACK,只须要获得 quorum (超过半数的节点)的 ACK 就能够了。
真正apache zookeeper在实现上提出设想:优化leader选举,直接选出最新的Peer做为预备Leader,这样就能将Phase 0和Phase 1合并,减小网络上的开销和多流程的复杂性。
实现上,协议被简化为三个阶段
Fast Leader Election:从Quorum Peer中选出数据最新的peer做为leader。该阶段会选举拥有最新提议历史(lastZixd最大)的节点做为 leader,这样就省去了发现最新提议的步骤。这是基于拥有最新提议的节点也有最新提交记录的前提。成为 leader 的条件:选epoch最大的;epoch相等,选 zxid 最大的;epoch和zxid都相等,选择myid最大的。节点在选举开始都默认投票给本身,当接收其余节点的选票时,会根据上面的条件更改本身的选票并从新发送选票给其余节点,当有一个节点的得票超过半数,该节点会设置本身的状态为 leading,其余节点会设置本身的状态为 following。
Recovery Phase:Leader将数据同步给Quorum Follower。
Broadcast Phase:Leader接受写请求,并广播给Quorum Follower。
对于选举和恢复阶段,zab算法须要确保两件事:
已经处理过的提案不能被丢弃。
已经丢弃的提案不能被重复处理。
参考资料:
我的公众号:啊驼