Paxos algorithm and data consistency

1. CAP and data consistency

1-1. CAP Summary

It assumes a distributed system is working with multiple nodes in the system under network environment, and these nodes will itself become unstable due to various reasons. Of which there is a very important concept --CAP principle. This principle guides the most distributed system design process, CAP principles generally distributed systems must say that there are three characteristics: consistency (Consistency), partition tolerance (Partition) and availability (Availability), and three properties can not all meet at the same time in the design of distributed systems.

                                                       

For example, a distributed system with N nodes work together via network link. First, you can not only complete data X stored on a node, the node that is because once stopped working due to various reasons, the data can not be accessed X it certainly does not meet the system availability, and once this node can no longer is restored, the data X will never be lost. Therefore, data should be stored at least X different nodes on multiple parts, the more the amount of the stored copy of the data to ensure the safety of X, but also to better ensure that even in a case where a plurality of nodes simultaneously unavailable, data can be similarly X It is accessed. This is the partition of the requirements, in accordance with the general experience, the copy number data X should be at least three. In this case, when the data changes how X update these copies of it? The best results is that when the client asks if issuing an update request data X, and access data from any node X can get it up to date, this is the conformance requirements. Of course, the best results too theoretical, you know the work of network-based distributed systems affected by many external factors: If the synchronization process can not find a copy of the node connected to how to do? If a customer while another data X update again how to do? If you really want to achieve conformance requirements such theories, it will only make all need to read / write data X client to wait until after the completion of all copies of the X data synchronization, and then respond, but apparently it from the perspective of availability but also does not meet the requirements.

                                                

As another example, the design is typically based on a relational database ACID principle, i.e. atomicity (Atomicity), consistency (Consistency), isolation (Isolation) and persistent (Durability Rev), commonly used relational database and transaction technology ACID principle, wherein each transaction is to minimize atomic operations, the transaction may be provided different levels, including readable uncommitted, such repeatable read transaction level; relational database also provides Once the transaction can not be performed correctly submitted data rollback, If you want to continue to modify the data can only start a new business. And all of this in order to ensure the consistency of the database system is a strong system . Even erected on top of each relational database instance of a distributed transaction mechanism, but also to ensure that this goal: as long as there is any database instance of a distributed transaction processes involved in abnormal, the entire distributed transaction may not submit properly, of course, unable to complete the data write operation. Of course, the old data can be read, even if there is a problem in some other database node node may also assume the read operation, because such a change in the consistency of operational risk does not exist.

Any distributed system can not in principle to the CAP three properties at the same time as the main design goals , to reach very high strong consistency and high partition tolerance, it is necessary to sacrifice the availability expense (note the expense but not completely give up). The partition tolerance and is the basis for the establishment of a distributed system, without any partition tolerance of distributed systems can not even be called a distributed system; high-pressure environment can not unduly sacrificing the availability of a distributed system, you know 99.99% availability and 99.999% availability is completely two grades of distributed systems.

1-2. Data consistency

Therefore, distributed transaction mechanism similar to that over-emphasis on data consistency of design ideas are less affected by the mainstream distributed system design ideas are welcome, at least from the design principles of distributed systems present a variety of published point of view is this. For example, such HDFS distributed file system, first of all emphasized that a certain degree of high availability and partition tolerance followed by the data consistency, data consistency through a copy of the guarantee. After all copies are completed but not written to HDFS action was considered consistent with the data, but as long as part of a complete copy of the data write operation, data is successfully written to HDFS considered and the client can call the new data, but did not complete synchronized copy It will be followed by data synchronization, to reach the final data consistency. DNS is also a distributed system need to consider high availability and partition tolerance, because the DNS service across different networks, so when a new domain name resolution DNS configuration changes after a request to wait for all global DNS nodes are the changes to take effect before continuing work , but the organizational structure of the DNS to ensure that the results of several DNS service nodes resolve www.XXXX.com the domain name is ultimately the same. This sacrificial system design ideas to ensure the consistency and reliability of the system partition tolerance, there is a specific call in the field of distributed systems: BASE. Basic available (Basically Availble), soft state (Soft-state) and a final consistency (Eventual Consistency).

In the previous section, a brief description of the contents of CAP principles and examples, reference is about the consistency of two concepts: strong consistency and eventual consistency. Strong consistency can be summarized as customer acquisition data X at any time in a distributed system, whether it carried out the operation in which a node distributed system that acquires data X are the same . From this definition, a distributed transaction mechanism is a kind of strong consistency of implementation. Weak consistency corresponding definitions have strong consistency, weak consistency do not mean to maintain data consistency, but say no all the time to ensure data consistency, do not promise when can we ensure that any distributed system node can read consistent data. The final consistency is a particular weak consistency of the results , based on both a weak commitment to consistency, after a time window of data inconsistencies, and ultimately to ensure data consistency. The time window of inconsistent data in the client appeared to be very short, and a distributed system can also block inconsistent data to the client by a variety of ways, for example from a master copy mode.

2. The final consistency of the Classical: Paxos algorithm

Paxos algorithm is also known as two-phase algorithm, note the two phases are not realize distributed transaction commit protocol. Scenes work Paxos algorithm is based on the principle of CAP build distributed systems is how to efficiently achieve the ultimate consistency of the data in such an environment; and the two-phase / three-phase commit protocol scenes work is based on strong principles of data AICD the consistency of the system. Design ideas on these two scenarios have been introduced in detail above, I will not repeat them here.

Paxos algorithm has four roles Client (subject producers), Proposer (formal proponent), Acceptor (vote decision-makers) and Learner (final result learner), these four roles Learner roles and Client are two broad concept, but truly complete algorithm is Proposer Acceptor roles and roles, so many technical articles are focused on explaining the working process of the two roles. Proposer role is really being proposed by the initiator, is responsible for initiating the proposal to vote, voting results and summary of the proposal to launch a new vote after the change; Acceptor role of decision-makers, to express their vote for a proposal, and handed Proposer role for processing, Acceptor each character has its own election results are not affected by other Acceptor roles.

Overall Paxos algorithm to deal with such a scenario: When a distributed system of multiple Proposer the results can not agree on something, it launched a proposal by the Acceptor responsible for multiple voting and allow for more Proposer it's the result of final agreement. "This thing" can refer to many things, such as "primary election" another example of the variable K is represented by V1 and V2 while giving two values ​​even more value, and so on. This is the final consistency of a specific algorithm mentioned above - the result of each Proposer decision process about something in a certain period of time may be inconsistent, but ultimately rendered on the client Client will be consistent of.

It should be noted, Paxos algorithm was first proposed by Leslie Lambert (Leslie Lamport), in his paper "The Part-Time Parliament" already introduced several variants of Paxos algorithm, combined with the actual application of the technical staff joined their understanding, so it appears Paxos algorithm to achieve many different versions, but in fact the work of these algorithms versions of each character has its own principles, as long mastered these principles, understanding Paxos is not difficult.

2-1. Basic-Paxos algorithm

Although the design ideas Paxos algorithm and two-phase commit protocol is completely different, but the former draws on some of the latter's ideas - which is divided into preparation phase (Prapare) and assignment phase (Accept). In order to successfully complete the final data consistency this objectives, throughout the course of their work Proposer Acceptor roles and roles are working to maintain the principle of the following:

  • Proposer Acceptor and responsible for initiating a proposal, a proposal responds. The latter to try to respond to every request of the former, whether the request is based on principles Acceptor is successful or failed. Proposer Acceptor If no response is obtained within a predetermined time, the worst case to be considered.

  • Multiple rounds of voting for a proposal of X, each Proposer need to ensure that the voting round after round number (vote) must be greater than himself initiated the previous round of voting round number. And each Proposer does not have to ensure that their voting rounds are global maximum, of course, in order to reduce conflict in the first stage of the authorization, it can also ensure that the new voting rounds global maximum (for this reason analysis will be discussed later in detail).

  • Under what circumstances an issue for X, Proposer will not launch a new round of voting was considered and eventually consistent data? When the final vote value Proposer collected each Acceptor and found that at least N / 2 + 1 th Acceptor final value is V, the final result is that the question is X V . In fact this work rules belong Proposer role, but many times Proposer, Proposer, Acceptor is an application.

  • In Prapare stage, Acceptor does not accept the need to ensure that the voting round number (vote) less current authorization voting rounds PrepareVote the (launch) applications.

  • Accept the stage, Acceptor does not accept the need to ensure that the voting round number (vote) less than the current assignment application PrepareVote voting rounds. More than two working principle is the most important, why Pasox algorithms have several variants of it? The reason is that the algorithm implementation phase, the specific algorithm at work to ensure that these two basic principles, in order to improve efficiency and fine-tune the implementation process.

2-1-1. Prapare preparation phase

              

首先需要介绍几个在Acceptor角色上需要被持久化保存的数据属性:

  • PrepareVote保存了当前Acceptor接收到的已完成投票授权的最大投票轮次
  • AcceptedVote保存了当前Acceptor在赋值阶段完成投票赋值的投票轮次
  • AcceptedValue保存了当前Acceptor在赋值阶段被赋予的值

1、第一个阶段Proposer和Acceptor至少要完成一次网络通讯,其主要目的是确定针对提议X的当前投票轮次是否能被授权。换句话说,根据Acceptor在准备阶段的工作原则,即使确定当前投票轮次的编号值是大于Acceptor中记录的PrepareVote的值。处理过程很简单,即Proposer向所有Acceptor发出关于发起提议X的新一轮投票的申请,并等待各个Acceptor进行响应。当然会有一个超时时间,如果超过这个时间还没有得到Acceptor的响应,则认为已经被拒绝。如果有超过N/2 + 1个节点在规定的时间内没有回复响应,那就说明整个选举系统发现了问题,则终止操作抛出错误,向客户端反馈异常信息

2、收到这个发起新的一轮投票操作的授权请求后,各个Acceptor开始判断是否可以进行授权。判断的原则只与PrepareVote有关,既是如果当前申请的投票轮次小于等于PrepareVote的赋值,则拒绝授权;其它情况都要接受授权,并更改PrepareVote属性为当前新的投票伦次编号。这里有一个隐藏含义,即这个Acceptor新授权的投票轮次编号,一定大于之前PrepareVote的值。

3、Proposer将负责汇总所有Acceptor的响应情况,并根据汇总的情况判断下一步的操作。无论Acceptor是授权这次投票还是拒绝这次投票,给出的响应信息中都最好包括当前Acceptor所记录的PrepareVote、AcceptedVote和AcceptedValue信息,这样有利于Proposer分析为什么会被Acceptor拒绝。下图展示了Proposer在汇总所有Acceptor的响应时可能出现的各种情况:

                 

当然还有一种情况三,就是超过(包括)N/2 + 1个Acceptor节点在规定的时间内没有反馈结果,这种情况直接判定Paxos系统崩溃,所以就不做进一步讨论了。请注意,无论是上图的哪种情况,只要至少N/2 + 1个Acceptor节点的AcceptedValue为同一个值,就认为提议X的结果达到了最终一致,整个Paxos算法过程也结束。

3.1、在Proposer得到的响应情况一中,至少N/2 + 1个Acceptor允许这轮投票。这些Acceptor就形成了一个集合Q,这个集合Q将继续下一步骤的操作。这时集合Q中的Acceptor是否已有AcceptedValue就很重要了:如果集合Q中没有任何一个Acceptor的AcceptedValue属性有值,则当前Proposer会在下一步提议自己的值为集合Q中每一个Acceptor的赋值目标;如果集合Q中至少存在一个Acceptor的AcceptedValue属性有值,则Proposer会选择一个AcceptedVote最大的AcceptedValue属性值,作为当前Proposer会在下一步进行Acceptor赋值的目标。

3.2、在Proposer得到的响应情况二中,始终未达到N/2 + 1个Acceptor允许这轮投票——无论是不满足Acceptor的授权原则还是Acceptor超时未响应。只要至少N/2 +1个Acceptor所回复的AcceptedValue属性值相同,则表示针对提议X已经形成了最终一致的结果,无需再进行投票了。否则,Proposer会将自己的投票轮次编号进行增加后,再发起投票——这个增加规则后续再讨论,读者目前可以认为是+1。

2-1-2. Accept赋值阶段

一旦有N/2 + 1个Acceptor节点授权了本轮投票,Proposer就可以进入第二阶段——赋值阶段,第二阶段将以上一阶段形成的多数派集合Q作为操作目标。如下图所示:

          

1、Proposer将会以上一阶段3.1步骤中所确定的value和自己的vote一起发送给集合Q中的每一个Acceptor,并等待回复。

2、Acceptor收到赋值请求后,将会按照判断原则确认是否进行赋值。这个判断原则上文已经说过了,这里再说一次。如果当前收到的vote小于当前Acceptor的PrepareVote属性值,则不会进行赋值。为什么Acceptor上的PrepareVote会发生变化呢?这是因为在这个Proposer从第一阶段到第二阶段的操作间隙,另一个或者多个Proposer使用编号更大的vote发起了更新一轮的投票,并得到当前Acceptor的授权。如果当前收到的vote等于当前Acceptor的PrepareVote属性值则接受这次赋值,这时Acceptor将更改其AcceptedVote属性为vote,更改其AcceptedValue属性为value。

注意一种情况,Acceptor会不会在第二阶段操作时收到一个vote大于当前PrepareVote的赋值请求呢?这是不会的,因为任何Acceptor要更换PrepareVote,只可能更换比当前PrepareVote更大的值,所以之前被Acceptor同意授权的vote一定会小于或者等于当前Acceptor的PrepareVote属性值。

3、赋值操作完成后,Acceptor将向Proposer返回赋值操作后的AcceptedValue属性和AcceptedVote属性。换句话说就是,即使Acceptor拒绝了第二阶段的赋值操作,也要向Proposer返回AcceptedValue属性值。以下为Proposer端汇总统计时,可能出现的情况:

                      

3.1、Acceptor收到集合Q中所有Acceptor的赋值结果后,就会进行汇总判断。如果发现所有赋值结果都是一样的value,则认为针对议题X形成了最终一致的结果。整个投票过程结束,value就是达成的最终值。

3.2、如果收到集合Q中所有Acceptor的赋值结果,并进行比较的过程中,发现任何一个赋值结果不一致,则认为赋值操作失败。这时Proposer会将自己的投票轮次编号进行增加后,再回到第一阶段重新发起投票。

2-1-3. 分析一种极端情况

Paxos算法的一个核心思路在于形成多数派决议,要形成核心思路Acceptor就必须按照自己的两个工作原则进行授权操作和赋值操作。这就是为什么我们在介绍Paxos算法时经常提到N/2 + 1的边界节点数量的原因。因为一旦形成多数派,决定最终一致性的关键表决就可以落在唯一一个节点上,也就是说如果第X1轮投票和第X2轮投票如果都同时拿到了多数派Acceptor的授权,那么它们的核心战略点就是要去抢占授权X1投票的Acceptor集合和授权X2投票的Acceptor集合求交集后至少存在一个的Acceptor节点。好消息是赋值阶段X1轮投票的编号和X2轮投票的编号总是不同的,也总有大小差异的,而Acceptor的工作方式决定了它只会接受编号更大的投票操作的赋值。

这个原理可以扩展到任意多个Proposer,那么有的读者会问了,会不会出现一种每个Acceptor的AcceptedValue属性都被赋值,且都没有达到多数派的特殊情况呢(如下图所示)?

                                                  

答案是不会的,本小节我们将使用反向论证证的方式来进行阐述。首先要出现三个X1的赋值结果Proposer1便不能继续赋值这样的现象,只可能是一种情况即Proposer1在经过两阶段操作,并在进行Acceptor1~Acceptor3赋值的时候,后者三个节点上PrepareVote等于Proposer1的Vote,而在Proposer1准备进行后续三个节点赋值时,却发现Acceptor4~Acceptor6的PrepareVote改变了。如下图所示:

                 

这时发起V2轮次投票的ProposerB,有两种情况,如下图所示:

第一种情况ProposerB还没有接收到至少N/2 + 1个Acceptor节点的授权结果,还处于第一阶段(这里Acceptor节点个数为6个,所以至少应该收到4个Acceptor的授权结果),这时ProposerB会一直等待汇总,如果等待超时都没有收到必要数量的结果,那么ProposerB继续增加它的投票编号,并重新发起。这时同样能够保证Acceptor集合不被之前的Proposer继续赋值——因为投票编号仍然最大。

如果是上图所示的第二种情况,就更简单了。ProposerB节点已经收到了至少4个Acceptor节点的授权结果,而这4个Acceptor节点,至少有一个节点携带了AcceptedValue为X1的值——因为目前被赋予X1的Acceptor节点个数,刚好为一半3个节点,和N/2 + 1个节点求交集后,至少有一个重叠的Acceptor节点(注意,基数为偶数,如果为奇数就更不会出现我们现在讨论的极端情况了)。也就是说当ProposerB节点进入第二阶段赋值操作时,向剩余Acceptor节点传递的值同样为X1,而不是自身原始提议的X2,所以最终本小节最初所述的极端情况不会出现。

2-1-4、一种投票轮次的初始和增加规则

在目前我们讨论的设计中,提到一个投票轮次的确定问题。不同Proposer发起的投票可以不一定全局递增,而同一个Proposer必须保证自己的发起的新一轮投票编号vote,一定是递增的(增量必须为1吗?这个不一定)。这是因为Acceptor在第一阶段的工作原则是,只接受vote大于当前PrepareVote的新一轮投票授权,那么对于某个Proposer而言,最不济的情况就是无论自己本身如何发起新一轮的投票,都不会得到Acceptor的授权,而最终结果只能听命于别的Proposer所提议的值

                   

很显然上图中无论ProposerB如何发起新一轮投票,都会在第一阶段被Acceptor拒绝掉,因为至少有一个Proposer的投票轮次编号一直比他的大。这种算法的简便处理方式虽然一定程度上降低了代码的编写难度,但是只能算作伪算法实现(目前很多系统为了平衡性能、维护性和实现难易度,都会采用这种方式),因为所有投票轮次的发起和赋值只会听从于唯一一个Proposer,在算法的执行过程中根本就不会出现投票冲突的情况;另一个方面,对并发状态下无法最终确认选值的Client来说也不公平,因为所有Client对最终赋值的选择也只会听命于唯一一个Client(忘了说了,Client是整个Paxos中另一个角色,这个角色负责向对应的Proposer提交需要进行表决的提案)。所以我们需要一种投票轮次编号递增的方式,来保证不同轮次的投票请求真正的竞争起来。

有的读者可能第一个想到的就是zookeeper这样的分布式协调器来生成编号,保证编号的唯一性和递增性,甚至很多学术资料、网络文章上都有提到使用这样的方式。拜托!要是能够使用zookeeper解决这个问题,那我们还实现Paxos算法干什么?所有解决冲突的工作都交给zookeeper就好了嘛。之所以要实现Paxos算法,就是因为要建造一个和ZK同属一个工作层的协调组件,所以关于编号的问题就只能自己想办法了。

这里介绍一种对投票轮次的编号方式,可以有效减少编号冲突,并在针提案X的投票过程中真正产生竞争。这个编号方式的前提条件是,各个Proposer工作在局域网环境下,并能通过组播的方式发现对方。在各个Proposer内部有一个使用Proposerid进行排序后的Proposer集合列表。这时就可以通过余数原则进行编号了:

                      

(N × V) + index + 1,这个公式中,N表示集合大小,V表示投票轮次(从0开始),index表示当前Proposer所在集合的位置索引。例如,设N==4时,id为1的存在于这个有序集合列表第一个索引位置的Proposer,可以使用的投票编号就是1、5、9、13……,存在于这个有序集合列表第二个索引位置的Proposer,可以使用的投票编号就是2、6、10、14、18……,存在于这个有序集合列表第三个索引位置的Proposer,可以使用的编号就是3、7、11、15、19……

那么这样的编号设计就不会重复了吗?当然会,特别是在整个Paxos算法系统的启动阶段。例如整个Paxos算法中所有N个节点的Proposer都已经启动了,但是某个Proposer上还暂时只发现了N-2个Proposer节点,这样该Proposer计算得出的自己所使用的编号,就可能和另外某个Proposer所计算得出的自己使用的编号重复。而且这个差异还与使用的不同节点发现方式而不同,例如在后续文章中给出的Basic-Paxos算法实现代码中,就使用组播方式进行Proposer节点发现,而这种节点发现方式就会使每个Proposer节点上的节点发现进度在Paxos算法系统启动之初出现不一致。

不过好消息是,Acceptor在第一阶段的工作原则中,只会授权大于当前AcceptedVote的投票申请(此时AcceptedVote <= PrepareVote)。也就是说当两个或者多个持有相同投票伦次的Proposer向同一个Acceptor申请投票授权时,Acceptor只会授权其中一个,另一个的授权申请将被Acceptor拒绝,这就保证了有相同投票轮次编号的授权请求在同一个Acceptor上不会被重复同意,那么这些有同样投票轮次编号的Proposer就可以决定自己是否拿到了授权多数派。如果没有拿到授权,当Proposer集合稳定存在后,就会有新的且更大的投票轮次编号了。

2-3. Paxos算法变种

上文我们提到,Paxos算法的设计是为了解决多个Proposer针对一个具体提案产生的多个赋值,并最终帮助多个Proposer确定一个大家一致认可的赋值,最终达到一致性。从理论层面上说,如果多个Proposer一直不产生竞争起来,那么算法研究就没有意义。例如上文中提到的一个Proposer A的投票轮次编号始终从1000开始,其它Proposer的投票轮次编号始终从100开始,那样的话Paxos算法系统中其它Proposer发起的提案申请在第一轮就会被拒绝(在Proposer A的第一次提案申请到达Acceptor之前,可能会有其它Proposer发起的提案进入第二轮,但最终也会被大多数Acceptor决绝),所以这样的Proposer编号设定实际上就会让所有Proposer都听命于Proposer A的提案决断。

可是从实际应用的角度出发,却刚好相反——让Paxos算法过程尽可能少的产生竞争有利于提高算法效率。在Basic-Paxos算法中,任何一个提案要被通过都至少需要和单独的Acceptor通讯两次(这里还不考虑多Proposer抢占的问题),如果有N个Acceptor则至少进行 (N/2 + 1)× 2次网络通讯。如果在一个完整的Paxos算法中,网络通讯占去了相当的时间,那么Paxos算法的性能自然不会好。所以Paxos算法会产生了很多变种形式。本节我们就对一些主要的变种形式进行说明

2-4. Paxos变种:Multi-Paxos算法

介绍Basic-Paxos时我们只针对了一个议题形成多数派达成一致的值的算法过程进行了说明,无论有多个Proposer参与了抢占Acceptor赋值权的过程,无论为了形成这个多数派决议发起了多少轮Prepare投票申请,也无论Acceptor拒绝了多少次Acceptor赋值请求。所有这一切都只是为了处理一个议题,如:关于K的赋值是多少、关于你爸的名字是A还是B、关于下一次事件触发时间是在1000秒以后还是2000秒以后。

但实际业务场景往往不是这样的,在分布式存储环境中,拥有Paxos算法功能的整个系统需要在生命周期内对多种议题进行一致性处理,例如日志编号X的操作是将编号K的值赋为A还是赋为B;下一个日志编号X+1的操作,是将K的值赋为Y还是赋为W……

为了将Basic-Paxos算法用于实际应用中,Basic-Paxos算法有一种变种算法叫做Multi-Paxos,其中的“Multi”当然就代表在Paxos算法系统存活的整个生命周期需要处理的多个提案。Multi-Paxos算法的工作过程大致可以概括为选主保持、正式工作、重选恢复,本节我们就和大家一起讨论一下Multi-Paxos算法这几个工作步骤。

2-4-1. Multi-Paxos:选主与保持

Multi-Paxos算法核心过程的工作原则和Basic-Paxos算法核心工作原则是相同的,即多数派决定、Acceptor第一轮只接受更大的投票编号、Acceptor第二轮只接受和目前第一轮授权相同的投票编号。但是为了提高对每一个议题的判定效率,Multi-Paxos算法在第一投票过程中做了一个调整:首先通过一个完整的Paxos算法过程完成一次选主操作,从多个Proposer中选出一个Proposer Leader,待所有Proposer都确认这个Proposer Leader后,后续的带有业务性质的提案,都只由这个Proposer Leader发起和赋值。这样的做法至少有两个好处:

  • 其它Proposer在确定自己不是Proposer Leader后,就不再发起任何提案的投票申请,而只是接受Learner(最终结果学习者)传来的针对某个提案的最终赋值结果。而往往Learner和Proposer都存在于同一个进程中,所以这基本上消除了这些Proposer的网络负载。

  • 一旦Proposer Leader被选出,且后续的提案只由它发起和赋值。那么就不存在投票申请冲突的情况了——因为没有任何其它的Proposer和它形成竞争关系。Acceptor所接受的投票轮次编号也都由它提出,那么这些后续提案的第一阶段都可以跳过,而直接进入提案的赋值阶段。这样类似Basic-Paxos算法中要确定一个提案的赋值至少需要和每个Acceptor都通讯两次的情况就被削减成了只需要一次通讯。

请注意,Multi-Paxos算法的选主过程和上文我们提到的直接决定一个起始投票编号最大的Proposer方式的本质区别是:前者是投票决定的,而后者是被固定的,一旦后者那个投票编号最大的Proposer宕机了,其它Proposer在提案投票过程中就会一直出现冲突。另外,由于多个Proposer中并没有Leader的概念,所以这些Proposer就算提案投票申请被拒,也会不停的再发起新一轮投票申请,而这样并不会减少网络通信压力。

         

 

      

要说明整个Multi-Paxos算法的可行性,就需要明确一个事实。当我们介绍Basic-Paxos算法时,为了读者能够腾出所有精力缕清它的工作步骤,所以将Paxos算法中的Proposer、Acceptor、Leaner等角色分开介绍。但实际情况是,分布式系统中的每个节点都同时具备这几种角色,例如工作在Server 1节点的Acceptor角色能够实时知晓这个节点上Proposer角色的工作状态;Proposer角色也实时知晓同一节点上Leaner角色对提案最终结果的学习状态。这也是为什么我们讨论Paxos算法时经常会将属于Leaner角色的部分公论讨论到Proposer角色中去的原因。

回到Multi-Paxos算法的选主过程来,这个过程就是一个完整的Basic-Paxos过程,这里就不再赘述了。主要需要说明的是保持方式,其它Proposer角色可以从Basic-Paxos过程了解到“已经选出一个Proposer Leader”这个事实,但是并不能知晓这个Proposer Leader仍然是“工作正常的”。所以一旦Proposer Leader被选出,它就会向所有其它Proposer发出一个“Start Working”指令,明确通知其它Proposer节点,自己开始正常工作了,并且在后续的工作中定时更新自己在各个Proposer节点上的租约时间(本文不介绍租约协议,有兴趣的读者可以自行查找该协议的介绍,这里可以简单理解为心跳)。

2-4-2. Multi-Paxos:正式工作过程

为了说清楚Multi-Paxos对多个业务提案的赋值投票过程,我们举一个连续进行日志写入的业务场景,这里每一条日志都有一个唯一且全局递增的log id——实际上一条日志就对应了一个提案即一个Paxos instance,提案内容就是“针对log id为X的日志,完成日志内容的持久化记录”。

这里有读者会问,这个唯一的log id是怎么被确认的呢?这个问题在Multi-Paxos中就很简单了,因为整个系统中只有一个Proposer Leader能进行提案的投票申请和赋值操作,所以这个来自于外部客户端的log id完全可以由Proposer Leader赋予一个唯一的Paxos instance id,并作为内部存取顺序的依据。如果这个log id来源于分布式系统内部那么这个问题就更简单了,直接由Proposer Leader赋予一个唯一且递增的log id即可。而后者的场景占大多数——试想一下这个基于Multi-Paxos算法工作的系统本来就是一个分布式存储系统……

                

由于Proposer Leader对业务数据的一致性处理跳过了Basic-Paxos算的第一个Prapare阶段,所以对于一条日志Log id,Proposer Leader会直接携带log id(可能还有Paxos instance id)和日志内容,向所有节点发起提案赋值操作。每一个Acceptor在收到赋值请求后,会首先在本地持久化这个日志内容。如果持久化成功,则对这个提案的赋值结果为“完成”,其它任何情况都是“未完成”。

Multi-Paxos的正式工作可以保证一批要进行投票的操作,其序号一定由小到大排列(对于这个日志业务的实例,就是日志的log id一定全局唯一且单调递增)。而且由于只存在一个Proposer Leader能够处理发起投票和赋值操作,所以也避免了操作竞争的问题,这直接导致每一个Paxos instance id都省去了Basic-Paxos的第一阶段,减少了花费在网络上的耗时。但是Multi-Paxos并非没有缺点,最显著的一个问题就是,由于Multi-Paxos中只有一个Proposer在工作,那么当这个Proposer由于任何原因无法在继续工作时怎么办?Multi-Paxos的解决办法是从剩下的Proposer,再重新选择一个Proposer Leader,让整个Multi-Paxos系统继续工作。

话虽简单,但实现起来却并不容易,这也是Multi-Paxos算法的难点所在。为什么呢?我们首先应该思考一下当原有的Proposer Leader宕机时,整个系统的可能处在什么样的状态,如下图所示:

                
(宕机前夜)

  • 首先由于Paxos算法的多数派原理,所以除了当前Proposer Leader(后文称为old leader)所在Server节点上的Log数据是完整的以外,整个系统不能保证任何一个其它Proposer所在Server节点上的Log数据是完整的。既然Log数据不完整,那么怎么做数据恢复呢?

  • 分布式系统的一般要力争 7 × 24小时不间断工作,也就是说即使整个系统连续工作1周时间,积累的LOG数据也是非常庞大的,10GB、100GB、1TB、10TB甚至更庞大的日志数据规模都有可能。

  • 当old leader宕机时,唯一可以确认的一件事情是:当前通过Multi-Paxos第二阶段操作,并持久化存储在Proposer Leader上的每一条日志数据,在整个Paxos算法算法系统中,至少存在N/2 + 1的多数派份额。在Proposer Leader宕机后,多数派需要达到的数量变成了(N - 1) / 2 + 1.

  • 最后,如上图所示Proposer Leader正在对log id为X7的数据进行Multi-Paxos第二阶段操作,且还没有拿到多数派确认。那么这样的数据在另一个Proposer Leader拿到控制权后,是恢复呢还是不恢复呢?

所谓重新恢复一个Proposer Leader(后文件称new leader),要达到的目的可不是简单的重新选举一个就行了。而是要让这个new leader尽量恢复到old leader宕机时的工作状态,当Client查询第X条日志信息时,new leader能够给出正确的值,而不是向Client回复not found或者错误的值。所以要保证new leader能够接替old leader继续工作,在old leader存活期和new leader恢复阶段,它们至少要做以下工作:确认赋值、确保old leader真正下线、重选leader和恢复数据。

2-4-3. Multi-Paxos:确认赋值过程(confirm)

确认赋值过程的目的主要是帮助Paxos系统在恢复阶段尽可能少做无用功,其做法是按照一定规则周期性的批量的发送Proposer Leader上log id的confirm信息。

             

如上图所示,所谓log id的confirm信息是指满足这样条件的log id信息,这个log id通过进行Multi-Paxos的第二阶段操作后,已经得到了大多数Acceptor的赋值确认,并且已经在Proposer Leader完成了持久化存储的log id。confirm信息还可以是乱序,甚至还可以是跳跃的。而且Proposer Leader发送一批og id的confirm信息后,也无需等待Proposer都回发确认信息,甚至可以设定Proposer成本身就不回发确认信息。

Proposer在收到这些confirm信息后,会检查(会可以不检查)log id confirm信息所对应的log id是否存在,如果存在则将这个log id confirm信息进行持久化保存;如果不存在也可以进行log id confirm信息的持久化保存(只是那样做没有意义),所以才会有“可以不预先检查”的说明。为什么要这样做,我们在后续4-4-5介绍数据恢复的小节会进行说明。

2-4-4. Multi-Paxos:确认下线

请考虑这样一个问题:当分布式系统中A节点收不到B节点“工作正常”的信息时,是A节点下线了,还是B节点下线了?答案是根本说不清楚,可能B节点还在正常工作,A节点收不到信息只是由于暂时的网络抖动;由于分布式系统存在分区隔离的问题,虽然A节点收不到B节点“工作正常”的信息,但是整个系统中还可能有大部分节点能够收到这样的信息,所以都不会认为B节点下线了;最后,也可能B节点真的已经下线。

在分布式系统中,对于节点X(既是本文一直在讨论的Proposer Leader节点)是否下线了这个问题很明显不是一个节点能够决定的,而最好是由多数派节点投票确认。在节点X被认定下线的时候,整个分布式系统至少需要保证两件事:

  • 多数派认为节点X下线了:这个原因已经在上一段落进行了描述,这里就不再赘述了。

  • 原来的节点X真的下线了:这是因为如果原有的节点X没有下线,在新的节点X1接替它工作后,就可能出现两个节点都在完成相同的工作职责,而它们接受到的传入数据又可能不完全一致,最终影响数据的一致性。要解决这个问题,只需要在各个节点实现租约协议。当节点X无法向多数派节点续租时即认为自己下线,这时即使自己工作是正常的,也会强制自己停止服务。

                                       

如上图所示,当节点Server N上关于Old Proposer Leader的租约信息过期后,Server N会发起一轮新的议题,议题内容为“Old Proposer Leader已经下线”。分布式系统中的其它节点在收到这个议题后,会检查自己和Old Proposer Leader的租约是否过期,如果没有过期,则可以在Basic-Paxos算法的第一阶段拒绝授权或者在Basic-Paxos算法的第二阶段拒绝赋值

当节点Server N关于“Old Proposer Leader已经下线”的议题通过,确确实实知晓不是由于自身的原因误认为“Old Proposer Leader已经下线”后,才会开始重新选主过程。这里有一个细节,当Server N的议题“Old Proposer Leader已经下线”通过后,Old Proposer Leader也会真实离线。这是因为在Old Proposer Leader上和多数派节点的租约都已经过期,即使Old Proposer Leader工作是正常的,也会强制自己停止服务。

需要注意的是,确认下线这个过程实际上并不是Multi-Paxos算法所独有的,而是所有分布式系统需要具备的功能。确认下线这个过程也并不是一定要使用Basic-Paxos算法进行,其原则是保证多数派确认下线和Old Proposer Leader确实下线这个事实。由于Multi-Paxos算法的特点,在正式工作时只能有一个Proposer Leader,所以在进行Proposer Leader重选前就必须确保原有的Proposer Leader真实下线。

2-4-5. Multi-Paxos:重选和数据恢复

重新选主的过程实际上和2-4-1小节描述的选主过程是一致的,甚至为了简化工作过程也可以将重新选主的提议和确认下线的提议合并在一起。重选步骤的关键并不在于如何选主,而在于如何恢复数据——因为Multi-Paxos的工作过程,我们不能保证任何一个Server节点上拥有完整的数据,所以一个New Proposer Leader在开始工作前就需要对数据完整性进行检查,对缺失的数据进行恢复,这样才能保证客户端在查询第Y条日志数据时,New Proposer Leader会返回正确的查询结果,而不是返回Not Found!下图展示了一种New Proposer Leader被选出后,整个Paxos集群所处理的数据状态

              

好消息是在Old Proposer Leader工作阶段,已经向潜在的成为新主的多个Proposer节点写入了不少的信息。例如,在Old Proposer Leader正式工作开始之前,会向其它Proposer节点写入一个start work的位置,记录前者开始工作时所用的log id的开始编号位置,经历了多少次Proposer Leader选主确认过程,就会有多个start work位置。例如一个月前由于Old Proposer Leader宕机,进行了一次选主,上周5由于Old Proposer Leader宕机又进行了一次选主。期间工作的各个Proposer上就会有两个start work位置。新加入的节点和恢复正常工作的历史节点,也应在加入/重新加入分布式环境时同步这些start work位置。

在Proposer Leader工作工程中,直接使用Basic-Paxos算法的第二阶段进行log id和内容的赋值操作,所以每一条操作成功的log内容都会记录在多个Proposer节点上,且这些节点的数量是多数派。最后,Proposer Leader工作过程中还会批量发送confirm信息,告知所有Proposer节点,哪些log id的内容已经得到了多数派确认。

所以在New Proposer Leader被选出,且开始进行数据恢复前。这个New Proposer Leader至少知道三类信息:一个或者多个Old Proposer Leader的strat working的位置、目前还在线的节点以及在Old Proposer Leader上已经得到多数派确认的若干log id信息。但是New Proposer Leader并不知道全局最大的log id,虽然在New Proposer Leader节点上会有一个max log id,但它并不知道后者是不是就是全局最大的log id。原因是之前Old Proposer Leader下线时,正在等待多数派确认的那个log id还没有得到多数派赋值确认又或者虽然多数派确认了但Old Proposer Leader本身还没有拿到响应信息,而New Proposer Leader自己并不清楚自己是否属于多数派。

数据恢复的原理说起来很简单,既是从数据恢复的开始点,到数据恢复的结束点,将本节点缺失的数据补全。那么问题来了:数据恢复的起点在哪?数据恢复的结束点在哪?哪些数据需要补全?数据来源在哪里?又应该如何进行补全?

  • 数据恢复的开始点很好确定,即是还没有开始恢复的第一个start work位置所对应的log id。

  • 数据恢复的结束点稍微麻烦一点,需要发起一轮Paxos投票来确认由多数派认可的那么全局最大的log id。不过,由于这时整个系统中已经确认了一个唯一的Proposer Leader,所以不存在投票冲突的问题,可以直接进行Basic-Poxos的第二阶段。

  • 从数据恢复的起点到终点,每一条log数据都需要进行一次完整的Basic-Poxos操作用以确保恢复的数据是历史上以形成多数派的正确数据,最终目的是为了保证数据最终一致性。但是之前已存在于New Proposer Leader上,且被标记为confirm的log数据则无需进行这样的操作了,因为Old c已经在之前确定了这些数据是正确的。这样就大量减少了需要恢复的数据规模,加快了数据恢复过程。

  • 注意,如果在数据恢复过程中,某一条Log id无法形成多数派决议,则这条数据就是在Old Proposer Leader中还没有被确认的数据,这样的数据不能进行恢复。即使在New Proposer Leader上有这条记录,如果没有形成多数派决议也不能使用,否则就会出现一些的错误。请看如下所示的极端例子:

                  

                    

如上图所示,Server 2在宕机前正在对log7 、log8 、log9 进行Paxos,并且自身已经完成赋值过程(但是其它节点都还没有完成赋值过程,没有形成多数派)。这时Server 2由于某些原因下线了。Server 3通过选主过程拿到了Proposer Leader,但是它能获取到的最大log id为log6(因为现在存活的各个节点上最大的log id都是log6嘛),所以自己start working的log id 位置就是log7。就在这个时候,Server 2节点又恢复了工作,且技术人员人工命令从新授予Server 2 节点Proposer Leader身份。

这时在其上原有的log7、log8、log9虽然有值,但是也不能再使用了,需要重新进行恢复。那么Server 2依据什么来判断存储在自身节点的log7、log8、log9的值已经失效必须进行恢复的呢?那就是在start working的时候,记录了一个创建时间T1,在随后的工作中,节点每写入一条日志信息就要写入同时写入这个T1。当进行数据恢复时,如果发现某一条数据所记录的T1和其最近的start working标记点所记录的T1不一致,就认为这是一条记录时效的数据,需要进行恢复。

按照如上图所示的极端示例中,log7的值应该恢复成11,而log8、log9的值应该记为一个空值或者是恢复成一个NOP标记。

3. 其它说明

Paxos算法并不是所有最终一致性算法,Multi-Paxos也并不是唯一一种Paxos算法的变种,例如还有Fast-Paxos、Egalitarian-Paxos。Fast-Paxos进一步强调了Leaner角色在整个Paxos算法中的作用,进一步优化了多个Proposer的协作工作过程。在后续的文章中会适时回过头来介绍Fast-Paxos,这里确实不能在耽误时间向后推进了(本来计划3月下旬开始分布式文件系统Ceph的使用和原理介绍)

最终一致性算法除了Paxos外,还有ZAB算法、RAFT算法、GOSSIP算法和PacificA算法。其中ZAB算法应用在一个非常著名的软件Zookeeper中,而PacificA的思想则被用在Apache Kafka进行Partition Leader的选举过程中。

Guess you like

Origin blog.csdn.net/shuningzhang/article/details/90509993