6.凤凰架构:构建可靠的大型分布式系统 --- 分布式共识

第6章 分布式共识
	以同步为代表的数据复制方法,被称为状态转移,是比较符合人类的思维的可靠的保障手段,但通常要以牺牲可用性为代价.

	可靠性与可用性的矛盾造成了增加机器数量反而带来可用性的降低。为了缓解这个矛盾,在分布式系统里主流的数据复制方法是以操作转移为基础的。我们想改变数据的状态,
除了直接将目标状态赋予它之外,还有另外一种常见的方法是通过某种操作,令源状态换为目标状态。能够使确定的操作促使状态间产生确定的转移结果的计算模型,在计算机科学
中称为状态机。
	
	状态机的特性:
		任何初始状态一样的状态机,如果执行的命令序列一样,则最终到达的状态也一样。如果将此特性应用在多个参与者的协商共识上,可以理解为系统中存在多个具有完全
	相同的状态机(参与者),这些状态机最终保持一致的关键就是起始状态完全一致和执行命令序列完全一致。

	根据状态机的特性,要让多台机器的最终状态一致,只要确保它们的初始状态一致的,并且接收到的操作指令序列也是一致的即可,无论这个操作是新增、修改、删除或者是
其他任何困难的程序行为,都可以理解为将一连串的操作日志正确的广播各各个分布式节点。在广播指令与执行指令期间,允许系统内部状态存在不一致的情况,即并不要求所有
节点的每一条指令都是同时开始、同步完成的。只要求在此期间的内部状态不能被外部观察到,且当指令序列执行完毕时,所有节点的最终状态是一致的,则这种模型就被称为
状态机复制。
	
	考虑到分布式环境下网络分区现象不可能消除,甚至允许不再追求系统内所有节点在任何情况下的数据状态都一致,而是采用"少数服从多数"的原则,一旦系统中过半的节点
完成了状态的转换,就认为数据的变化已经被正确的存储到了系统中,这样就可以容忍少数(通常不超过半数)的节点失联,减弱增加机器数量对系统整体可用性的影响,这种思想
在分布式中被称为"Quorum机制"。
	
	根据上述的讨论,我们需要设计出一种算法,能够让分布式系统内部暂时容忍不同的状态,但最终保证大多数节点的状态达成一致的;同时,能够让分布式系统在外部看来
始终表现出整体一致的结果。这个让系统各节点不受局部的网络分区、机器崩溃、执行性能或者其他因素影响,都能最终表现出整体一致的过程,就被称为各个节点的协商共识。
	
	共识与一致性的区别:一致性是指数据不同副本之间的差异,而共识是指达成一致性的方法和过程。


6.1 Paxos
	6.1.1 Paxos的诞生

	6.1.2 算法流程
		paxos 算法将分布式系统中的节点分为三类:
			1.提案节点
				称为Proposer,提出对某个值进行设置操作的节点,设置值这个行为就被称为提案(Proposal),值一旦设置成功,就是不会丢失也不可变的。注意,paxos
			是典型的基于从操作转移的模型而不是状态转移模型来设计算法的,不要把这里的"设置值"类比成程序中的赋值操作,而应该类比为日志记录操作,在后面介绍的Raft
			算法中就直接把"提案"叫做"日志"了。

			2.决策节点
				称为Acceptor,是应答提案的节点,决定该提案是否可被投票、是否可被接受。提案一旦得到半数决策节点的接受,即称该提案被批准(Accept)。提案被批准
			意味着该值不能被更改,也不会丢失,且最终所有节点都会接受它。

			3.记录节点
				称为Learner,不参与提案,也不参与决策,只是单纯的从提案、决策节点中学习已经达成共识的提案,譬如少数节点从网络分区中恢复时,将会进入这种状态。

		在使用paxos算法的分布式系统里,所有的节点都是平等的,它们都可以承担以上某一种或者多种的角色,不过为了方便有明确的多数派,决策节点的数量应该被设置成
	奇数个,且在系统初始化的时候,网络中每个节点都应该知道整个网络所有决策节点的数量、地址等信息。

		在分布式环境下,如果我们说各个节点"就某个值(提案)达成一致",指的是"不存在某个时刻有一个值为A,另外一个时刻又为B的情景"。解决这个问题的复杂度主要来源于
	以下两个方面的因素的共同影响:
		1.系统内部的各个节点通信是不可靠的,不论是对系统中企图设置数据的提案节点亦或者是决定是否批准设置操作的决策节点,其发出、收到的信息可能延迟发送、可能丢失,
		但不去考虑消息有传递错误的情况。
		2.系统外部各个用户访问是可并发的,如果系统只允许一个用户,或者每次只对系统进行串行访问,那单纯的应用Quorum机制,少数节点服从多数节点,就足以保证值被正确
		的读写。

		第1点是网络通信中客观存在的,也是所有共识算法都要重点解决的问题。第2点,详细解释如下。我们现在讨论的是"分布式环境下并发操作的共享数据"的问题,即使先不考虑
	是否在分布式环境下,只考虑并发操作,假设有一个变量i当前在系统中存储的值为2,同时有外部请求A、B分别对系统发送操作指令,把"i的值加1"和"把i的值乘以3",如果不加
	任何并发控制,将可能得到"(2+1)*3=9",与"2*3+1=7"这两种可能的结果。因此,对同一个变量的并发修改必须先加锁后操作,不能让A、B的请求被交替处理,这也可以说是
	程序涉设计的基本常识。而在分布式环境下,由于要同时考虑到分布式系统内可能在任何时刻出现的通信故障,如果一个节点在取得锁之后、在释放锁之前发生崩溃失联,这将导致
	整个操作被无限期的等待所阻塞,因此算法中的加锁就不完全等同于并发控制中以互斥量来实现的加锁,还必须提供一个其他节点能抢占锁的机制,以避免因通信问题而发生死锁。

		为了解决这个问题,分布式环境中的锁必须是可抢占的。paxos算法包括2个阶段,第一个阶段是"准备"(Prepare)就相当于上面抢占锁的过程。如果某个提案节点准备发起
	提案,必须先向所有的决策节点广播一个许可申请(称为Prepare请求)。提案节点的Prepare请求中会附带一个全局唯一且单调递增的数字n作为提案ID,决策节点收到后,将会
	给与提案节点两个承若与一个应答:
		
		两个承若是指:
			1.承若不会再接受提案ID小于或等于n的Prepare请求;
			2.承若不会再接受提案ID小于n的Accept请求。

		一个应答是指:
			1.在不违背以前的承若的前提下,回复已经批准过的提案中ID最大的那个提案所设定的值和提案ID,如果该值从来没有被任何提案设定过,则返回空值。如果违反此前
			做出的承若,即收到的提案ID并不是决策节点收到的最大的ID,那允许直接对此Prepare请求不予理会。

		
		当提案节点收到了多数派决策节点的应答(称为Promise应答)后,就可以开始第二阶段的"批准(Accept)"过程了,这时有如下两种可能的结果:
			1.如果提案节点发现所有响应的决策节点此前都没有批准过该值(即为空),那说明它是第一个设置值的节点,可以随意的设置要设定的值,将自己选定的值与提案ID
			组成一个二元组"(id,value)",再次广播给全部决策节点(称为Accept请求);
			2.如果提案节点发现响应的决策节点中已经至少一个节点的应答中包含值了,那它就不能够随意取值,而是必须无条件的从应答中找出提案ID最大的那个值并接收,
			组成一个二元组"(id,maxValue)",再次广播给全部决策节点(称为Accept请求)。

		当每一个决策节点收到Accept请求时,都不会在违背以前的承若的前提下,接收并持久化当前提案ID和提案附带的值。如果违反此前做出的承若,即收到的提案ID并不是
	决策节点收到过的最大ID,那允许直接对此Accept请求不予理会。

		当提案节点收到了多数派决策节点的应答(称为Accepted应答)后,协商结束,共识决议形成,然后将形成的决议发送给所有记录节点进行学习。


	6.1.3 工作实例
		Basic Paxos 一般不会直接用于实践,只能对单个值形成决议,并且决议的形成至少需要2次网络请求和应答(准备和批准各一次),在高并发情况下产生较大的网络开销,
	极端情况下甚至可能形成活锁。


	6.2 Multi Paxos
		Basic Paxos 活锁问题,即两个提案节点争相提出自己的提案,抢占同一个值的修改权限,导致整个系统在持续性的"反复横跳",在外部看起来像被锁了一样。

		分布式共识的复杂性主要来源于网络的不可靠性与请求的并发性两大因素,活锁问题与Basic Paxos异常场景中所遇到的麻烦,都可以看做源于任何一个提案节点都能够完全
	平等的、与其他节点并发的提出提案而带来的复杂问题。

		改进版本 --- Multi Paxos算法,即不破坏Paxos中"众节点平等"的原则,又能在提案节点中实现主次之分,限制每个节点都有不可控的提案权利。

		Multi Paxos 对 Basic Paxos 的核心改进是增加了"选主"的过程,提案节点会通过定时轮询(心跳),确定当前网络中的所有节点是否存在一个主提案节点,一旦没有
	发现主节点,节点就会在心跳超时后使用Basic Paxos 中定义的准备、批准的两轮网络交互过程,向所有节点广播自己希望竞选主节点的请求,希望整个分布式系统对"由我作为
	主节点"这件事情协商达成一致共识,如果得到了决策节点多数派的批准,便宣告成功。选主完成之后,除非主节点失联后重新竞选,否则从此以后,就只有主节点本身才能发出提案。
	此时,无论哪个提案节点接收到客户端的操作请求,都会将请求转发给主节点来完成提案,而主节点提案时,就无需再经过准备过程,因为可以认为在经过选举那一次后,后续的提案
	都是相同提案ID的一连串的批准过程。也可以通俗的理解为选主过后,就不会再有其他节点与它竞争,相当于处于无并发的环境中的有序操作,所以此时系统中要对某个值达成一致,
	只需要进行一次批准交互即可。

		可能有人已经注意到这时候的二元组(id,value)已经变成了三元组(id,i,value),这是因为需要给主节点增加一个"任期编号",这个编号必须是严格单调递增的,以应付
	主节点陷入网络分区后重新恢复,但另外一部分节点仍然有多数派,且已经完成了重新选主的情况,此时必须以任期编号大的主节点为准。节点有了选主机制后,在整体看来,就可以
	进一步简化节点角色,不去区分提案、决策和记录节点,而是统统以"节点"来代替,节点只有主(leader)和从(follower)的区别。

		"分布式系统中如何对某个值达成一致"这个问题,可以分为3个子问题,当下面3个问题被解决的时候,即等价于达成共识:
			1.如何选主(Leader Election)
			2.如何把数据复制到各个节点上(Entity Replication)
			3.如何保证过程是安全的(Safety)


		数据在网络各个节点间的复制问题:
			在正常情况下,客户端向主节点发起一个操作请求,譬如提出"将某个值设置为X",此时主节点将x写入自己的变更日志,但先不提交,接着在下一次心跳中把变更信息
		广播给所有的从节点,并要求从节点回复"确认收到"的消息,从节点收到消息后,将操作写入自己的变更日志,然后向主节点发送"确认签收"的消息,主节点收到超过半数的
		签收消息后,提交自己的变更、应答客户端并且给从节点广播可以提交的消息,从节点收到提交消息后提交自己的变更,至此,数据在节点间的复制宣告完成。

			在异常情况下,网络出现了分区,部分节点失联,但只要仍能正常工作的节点数量能够满足多数派(半数)的要求,分布式系统就可以正常工作,这是的数据复制过程如下:
				1.假设有s1,s2,s3,s4,s5 5个节点,s1是主节点,由于网络故障,导致s1,s2和s3,s4,s5 之间彼此无法通信,形成分区;
				2.一段时间后,s3,s4,s5 三个节点中的某一个节点(比如是s3)最先达到心跳超时的阈值,获知当前分区中已经不存在主节点,则它向所有节点发出自己要竞选的
				广播,并收到了s4,s5节点的批准响应,加上自己一共三票,即得到了多数派的批准,竞选成功,此时系统中会同时存在s1和s3两个主节点,但由于网络分区,它不会
				知道对方的存在;
				3.这种情况下,客户端发起操作请求;
				4.如果客户端连接到了s1,s2其中之一,都将由s1处理,但由于操作只能最多获得2个节点的响应,不构成多数派的批准,所以任何变更都无法成功提交;
				5.如果客户端连接到了s3,s4,s5其中之一,都将由s3处理,此时操作可以获得最多3个节点的响应,构成多数派的批准,是有效的,变更可以被提交,即系统可以继续
				提供服务;
				6.事实上,以上两种情况很少能并存。网络分区是由于软、硬件或者网络故障而导致的,内部网络出现了分区,但两个分区仍然能分别与外界网络的客户端正常通信的
				情况甚为少见。

			假设故障恢复,分区解除,5个节点可以重新通信:
				1.s1和s3都向所有节点发送心跳包,从各自的心跳包得知两个主节点里s3的任期编号更大,它是最新的,此时5个节点均只承认s3是唯一的主节点;
				2.s1,s2回滚它们所有未被提交的变更;
				3.s1,s2从主节点发送的心跳包中获取它们失联期间发生的所有变更,将变更提交写入本地磁盘;
				4.此时分布式系统各个节点的状态最终达成一致。


		过程如何保证安全性:
			在分布式理论中,Safety和Liveness两种属性是有预定义的作用的,在专业的资料一般翻译成"协定性"和"终止性"。定义如下:
				1.协定性:所有的坏事都不会发生
				2.终止性:所有的好事都终将发生,但不知道是什么时候

			协定性保障了选主的结果一定有且只有唯一的一个主节点,不可能同时出现两个主节点;而终止性则要保证选主过程一定可以在某个时刻结束。

			把以上这种共识问题分解为"选主"、"复制"和"安全"3个问题来思考、解决的思路,即"Raft算法"。


	6.3 Gossip协议
		Paxos、Raft、ZAB等分布式算法经常被称作"强一致性"的分布式共识协议,意思是:"尽管系统内部节点可以存在不一致的状态,但从系统外部来看,不一致的情况并不会
	被察觉到,所以整体上看系统是强一致性的"。

		"最终一致性"的分布式共识算法,表明系统中不一致的状态有可能会在一段时间内被外部直接观察到。如dns系统,在各个节点缓存的ttl到期之前,都有可能与真实的域名
	翻译结果不一致。另一个"最终一致性"的分布式共识协议:Gossip协议。

		Gossip 一种用于分布式数据库在多节点间复制同步数据的算法。

		按习惯把Gossip也称作"共识协议",但必须强调它并不是直接与Paxos、Raft这些共识算法等价的,只是基于gossip之上可以通过某些方法去实现与paxos,raft相类似
	的目标而已。

		Gossip的过程十分简单,它可以看作以下两个步骤的简单循环:
			1.如果有某一项信息需要在整个网络的所有节点中传播,那从信息源开始,选择一个固定的传播周期(如1s),随机选择它相连的k个节点(称为Fan-Out)来传播消息;
			2.每一个节点收到消息后,如果这个消息是它之前没有收到过的,则在下一个周期内,该节点将向除了发送消息给它的那个节点外的其他相邻节点发送相同的消息,直到
			最终网络中所有节点都收到了这个消息。尽管这个过程需要一定时间,但是理论上最终网络的所有节点都会拥有相同的消息。

		根据Gossip的描述,我们很容易发现gossip对网络节点的连通性和稳定性几乎没有任何要求,它一开始就将网络某些节点只能与一部分节点部分连通而不是全连通网络作为
	前提;能够容忍网络上节点的增加和减少,随意的宕机和重启;新增加或者重启的节点的最终状态与其他节点同步达成一致。gossip把网络上所有节点都视为平等而普通的一员,
	没有任何中心化节点或者主节点的概念,这些特定使得gossip具有极强的鲁棒性,而且非常适合在公众互联网中的应用。

		同时我们也很容易找到gossip的缺点。消息最终是通过多个轮次的散播到全网的,因此它必然会存在全网节点状态不一致的情况,而且由于是随机选取发送消息的节点,所以
	尽管可以在整体上测算出统计学意义的传播速率,但对于个体消息来说,无法准确的预计需要多长时间才能达成全网一致。另外一个缺点是消息的冗余,同样是由于随机选取发送
	消息的节点,所以就不可避免的存在消息重复发送给同一个节点的情况,增加了网络的传输压力,也给消息节点带来了额外的处理负载。

		达到一致性消耗的时间和网络传播中消息冗余这两个缺点存在一定的对立性,如果要改善其中一个,就会恶化另外一个。由此,gossip设计了两种可能的消息传播模式:
	反熵和传谣。熵是生活中少见但科学中常用的概念,它代表事务的混乱程度。反熵的意思就是反混乱,以提升网络各个节点之间的相似度为目标。所以在反熵模式下,会同步节点
	的全部数据,以消除节点之间的差异,目标是使整个网络各个节点完全一致。但是,在节点本身就会发生变动的前提下,这个目标将使得整个网络中消息的数量非常大,给网络
	带来巨大的传输开销。而传谣模式是以传播消息为目标,只发送新到达节点的数据,即只对外发送变更的消息,这样消息的数据量将显著缩减,网络开销也相对较小。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值