1. 分布式系统
1.1 分布式系统的概念
分布式系统是由多台计算机组成的网络,这些计算机共同协作以实现一个共同的目标。在这种环境中,每台计算机作为一个独立的进程运行。但对最终用户来说,它们似乎是作为一个单一系统在操作。这个概念对于创建高效和可扩展的计算机网络至关重要。
1.2 分布式系统的特性
1.2.1 并发性(Concurrency)
分布式系统中的每台计算机(或称进程)都在与网络中其他计算机同时独立地执行事件;这需要各个计算机之间进行协调。
1.2.2 缺乏全局时钟(Lack of a global clock)
有时候我们无法断定两个事件中哪一个先发生;通过逻辑时间,我们可以在系统中对事件进行部分排序。
1.2.3 组件的独立失败(Independent failure of components):
需要设计容错系统来应对不同类型的故障。
崩溃停止(crash-stop):进程在没有预警的情况下停止。
拜占庭错误(byzantine):组件的行为是任意的(它可能是恶意的,不遵守协议等)。
1.2.4 消息传递(Message passing)
根据消息交付的时间特性不同,可分为:
同步(Synchronous):假设消息将在某个固定的、已知的时间量内被送达。
异步(Asynchronous):消息传递没有固定的上限,消息花多长时间被接收是不确定的。
每个特性都对分布式系统的设计和功能有着深远的影响。例如,并发允许多个操作同时进行,从而提高系统的效率;但同时也带来了复杂的协调和同步问题。缺乏全局时钟意味着系统中的时间需要通过一些算法(如Lamport时间戳或向量时钟)来协调。独立的组件失败强调了设计健壮、能够处理部分失败而不影响整个系统运行的重要性。消息传递是分布式系统中通信的基础,不同的传递模型(同步或异步)会导致系统设计和性能的显著不同。
2. 复刻状态机
2.1 状态机
这个图为“状态机”,展示了一个单独的状态机与客户端进行交互。有多个客户端向状态机发送请求,并从中接收结果。这代表了一个集中式系统,其中一台机器处理所有请求并维护状态。
2.2 复制状态机的概念
分布式系统中的一个特殊类型是复制状态机。一般来说,状态机是一个执行一系列命令、转换其状态并可能产生输出的系统。这些过程是确定性的,意味着它们总是从相同的初始状态和输入产生相同的输出。
在复制状态机中,这个概念被扩展到多台计算机上。网络中的每台计算机都有一个状态机的副本。当客户想要执行一个命令时,他们将其发送给这些状态机中的一个。
此时,共识模块的作用就显现出来了。共识模块的任务是将客户的命令添加到所有机器的交易日志中,确保每个状态机以相同的顺序执行相同的命令。这种协调至关重要,因为它维护了所有机器上系统的一致性。
这个图为“复制状态机”,则描绘了一个更复杂的设置。这里有多个状态机,每个状态机都有一个日志。共识模块负责确保所有状态机在状态和要执行的命令序列上达成一致,这是通过复制和共识过程实现的。共识模块与状态机之间的箭头指示了信息流动,确保每个状态机的日志以相同的顺序更新相同的命令。这个系统被设计为对客户端来说像一个单一的状态机,尽管背后有复杂的结构。它是一个具有容错能力的设置,即使某些状态机发生故障,它也能继续运行。
2.2 复制状态机的优势
这个系统的美妙之处在于它的可靠性和容错性。即使集群中的一些服务器失败,系统仍然能从客户的角度无缝地运行。对他们来说,他们感觉自己是在与一个单一的、可靠的状态机交互,尽管处理过程是分布在多个服务器上的。
简单来说,想象一群人(计算机)在共同完成一个项目(统一的目标)。每个人负责项目的一部分,但他们定期相互沟通和更新信息以确保每个人都在同一个频道。对外人(最终用户)来说,项目似乎是由一个单一实体完成的,尽管这是一个团队努力的结果。这个团队的协调努力,类似于复制状态机,确保了最终成果的一致性和可靠性,即使其中一些团队成员遇到问题。
3. 分布式中的共识问题
共识问题是许多分布式系统需要解决的核心问题。
3.1 共识的必要性
一组服务器可能需要解决不同的问题,例如:
3.1.1 确保所有服务器以相同的顺序接收相同的更新
这在分布式账本技术(如区块链)中尤为重要,因为需要保证每个节点的账本数据一致。
3.1.2 成员列表维护
服务器需要知道彼此的存在,了解哪些服务器尚未离开或崩溃,所有服务器都应持有相同的列表,并且这个列表应当实时更新。
3.1.3 领导者选举
在服务器群体中选出一个领导者,并确保群组中的每个成员都知道这一点。
3.1.4 互斥
确保一次只有一个进程访问某个关键资源(如文件)。
共识的定义是一组服务器之间的协调,并就某件事物的值达成一致。然而,根据FLP不可能性定理,在异步分布式系统中,完全解决共识问题是不可能的。这个定理表明,在不存在全局时钟,并且消息传递时间不确定的异步系统中,如果系统中的一个服务器可能失败,那么没有算法能够保证在有限时间内达成共识。尽管有FLP定理的存在,实践中还是有多种算法被设计出来以便在分布式系统中实现共识,尤其是在假设某种形式的同步或部分同步的情况下。例如,实用拜占庭容错(PBFT)算法可以在一定数量的服务器存在拜占庭错误的情况下达成共识。另一个例子是Raft算法,它为领导者选举和日志复制提供了一种相对容易理解的共识机制。这些算法通过引入一些同步假设,如假设消息在一定时间内会被送达,或者通过引入随机化来规避FLP定理,使得在大多数实际应用中共识成为可能。
3.2 共识的定义与属性
3.2.1 定义
共识指的是关于某件事情的一般性协议,或者是一个由一组成员全部共享的想法或意见。
3.2.2 属性
在一组各自具有初始值的进程中,共识具有以下特性:
3.2.2.1 终止性(Termination)
所有非故障进程最终都能决定一个值。
3.2.2.2 一致性(Agreement)
所有作出决定的进程都对相同的值达成了一致。
3.2.2.3 有效性(Validity)
被所有进程决定的值必须是由某个进程提出的。
3.2.3 共识问题的正式表述
有N个进程,每个进程p具有:
a.一个输入变量,初始设定为 x_p = 0 或 1 (进程的提议)。
b.一个输出变量, y_p 在{0, 1} 范围内(最终决定);注意:输出变量只能改变一次。
共识问题的目标是设计一个算法,使得在算法结束时:
所有(非故障)进程将其输出值设定为0(即所有为0的解决方案)或所有进程将其输出值设定为1(即所有为1的解决方案)。
3.2.3 共识为什么重要
在分布式计算中,许多重要的问题要么等同于共识问题,要么比共识问题更难。例如,故障检测就等同于共识问题(这意味着,如果你能设计一个完美的故障检测器,那么你就能解决共识问题,反之亦然)。换句话说,共识是分布式系统中许多关键操作的基础,比如确保数据一致性、进行领导者选举、同步系统状态等等。如果没有有效的共识机制,这些操作就无法可靠地执行,分布式系统的核心优势就无法实现。
4. 系统模型
系统模型可以分为同步系统模型和异步系统模型。
4.1 同步系统模型(Synchronous system model)
在同步分布式系统中,消息传输延迟、本地时钟的漂移都有界限,每个进程中每一步的执行时间也有上限。
在同步系统模型中,由于时间的界限和可预测性,共识问题是可解的。
4.2 异步系统模型(Asynchronous system model)
异步分布式系统中没有消息传输延迟的界限,本地时钟的漂移是任意的,进程执行没有固定的时间界限。
在异步系统模型中,由于缺乏时间上的限制和不可预测性,共识问题被认为是不可能解决的。
4.3 FLP不可能性证明(FLP impossibility proof)
由Fischer、Lynch和Paterson在1985年提出这个证明指出,不论使用何种协议,在存在失败和消息延迟的最坏情况下,总有可能发生阻止系统达成共识的执行序列。即使共识在异步模型中被证明是不可能的,人们还是提出了一些解决方案(如安全或概率性方案)来解决共识问题,这些方案在实践中非常受欢迎。
我们会研究Paxos协议的一个简化版本,Paxos是实现共识的一个经典协议,尽管它有一定的复杂性,但它提供了在部分同步系统中解决共识问题的方法。
这些系统模型为分布式系统的研究提供了基础,它们定义了理论和实际应用之间的桥梁,使得研究者和工程师可以在已知的限制和假设下设计和分析分布式算法。
5. 同步系统的共识
5.1 在同步系统中实现共识的环境特点
5.1.1 同步系统(Synchronous System)
5.1.1.1 消息传递延迟有界
系统定义了消息从一个进程传递到另一个进程所需时间的最大界限
5.1.1.2 进程步骤执行时间有最大限制
每个进程完成其操作步骤的时间有一个预先定义的最大值。
5.1.1.3 本地时钟漂移有界
系统中每个进程的本地时钟与真实时间相比可能有所偏差,但这种偏差是有限制的。
5.1.2 故障(Failures)
这里考虑的是崩溃停止故障(Crash-Stop Failures),即进程可能失败并且不会恢复。一旦进程失败,它就永久退出运行状态。系统设计时考虑到最多有f个进程可能会发生崩溃停止故障。
在这样的环境下,共识算法需要考虑如何在存在进程可能失败的情况下,依然能够保证所有非故障进程能够达成一致的决定。同步系统由于其时间界限和可预测性,为设计有效的共识协议提供了有利条件。例如,算法可以利用固定的时间界限来确保消息的传递和处理,并在预定轮次结束时达成共识决定。尽管存在故障进程,剩余的非故障进程仍可以通过交换和更新信息来最终达成一致的决策。
5.2 协议
进程按照时间轮次操作;算法在 f + 1 轮中进行。 是进程 i 在轮次 r 开始时已知的提议值集合。
这个共识协议的目标是在同步系统中,即使有进程可能失败,所有非故障进程仍然能够达成共识。这个协议是通过多轮交流来实现的
5.2.1 具体操作如下
5.2.1.1 初始化
对于每个进程 i,初始时 = { } , = { } ,其中 是进程 i的提议值。
5.2.1.2 进行轮次操作
对于每一轮 r(从 1 到 f+1),执行以下步骤:
广播 - (向每个进程发送消息),即把上一轮新接收的值发送给其他进程。
设置 =。
对于这一轮接收到的每个值 Valj,执行= Valj 即将新接收的值加入到自己的值集合中。
5.2.1.3 最终决定
在第f+1轮结束后,每个进程P_i的决定值 d_i 设为 中的最小值。
5.2.1.4 总结
在第一轮中,每个进程广播自己的值。
然后,在每一轮中广播上一轮从其他进程收到的新值。
每一轮中,将收到的所有新值加入到自己的值集合中。
最后,在 f+1轮后,从自己所知的所有值中取最小值作为最终决定。
5.2.1.5 结果
经过 f+1轮后,所有非故障进程将会接收到相同的值集合。因此,它们能够根据这个共同的值集合做出统一的决定。
这个协议的关键在于利用同步系统中的轮次和消息传递的确定性,确保即使部分进程失败,剩余的非故障进程也能够通过交换信息达成一致的决定。通过在多个轮次中不断更新和共享提议值,最终使得所有非故障进程拥有相同的信息,并基于这个信息集合做出统一的选择。
5.2.2 用反证法证明结论
这个练习是通过反证法来证明为什么在同步系统中,经过 f+1轮后,所有非故障进程都收到了相同的一组值。我们假设有一个进程 p_i 拥有一个值 v,而另一个进程没有这个值,然后通过逐步推理找出这种情况的矛盾。以下是推理过程:
a.
假设:假设进程 拥有一个值 v,而进程没有这个值。问题:是在何时接收到这个值的?
答案:必须是在最后一轮接收到这个值。如果不是这样,那么 会在之前的某个轮次把它发送给,而 也就会拥有这个值。
b.
因此:在最后一轮,必然有一个第三个进程向 发送了这个值。问题:在轮次结束前,发生了什么?
答案:进程 p_k\) 必须是在向 \(p_i\) 发送了 v后崩溃,并且在发送 \(v\) 给 \(p_j\) 之前。所以,p_k 在最后一轮期间崩溃了。
c.
类似地:必须有一个第四个进程 p_l\) 在倒数第二轮崩溃。问题:为什么会这样?
答案:第四个进程 p_l 在倒数第二轮向 \(p_k\) 发送了 v,然后在它能够发送给 p_j\) 之前崩溃了。
d.
结论:我们可以得出结论,必须在每一轮(\(f+1\) 轮)中都有一个进程崩溃。但是,已知最多只有 f 个进程可以崩溃,这就产生了矛盾。
因此,根据这种推理,我们可以得出结论,经过 f+1 轮之后,所有非故障进程都必定接收到了相同的一组值。这是因为在这种同步系统中,除非每一轮都有一个进程崩溃,否则所有非故障进程最终都会收到相同的信息。由于崩溃的进程数有上限 f,这种情况在f+1轮中是不可能发生的,所以所有非故障进程都必然会收到相同的值集合。
6. 异步系统的共识
6.1 FLP不可能性定理
在异步分布式系统中的共识问题和FLP不可能性定理(由Fischer、Lynch、Paterson于1985年提出)之间有着密切的联系。这个定理阐述了以下几点:
6.1.1 异步系统中共识问题的不可能性
在异步分布式系统中,解决共识问题是不可能的。这是因为在这类系统中,没有固定的时间界限来确保消息的传递或进程的执行。
6.1.2 FLP定理之前和之后的变化
在1985年FLP定理提出之前,许多供应商声称他们有100%可靠的解决方案。FLP定理之后,这些声称变成了99.999%的可靠性。
这种变化反映了人们对于分布式系统可靠性的认识,从完美的绝对可靠转变为接近完美但有一定概率失败的实际可靠。
6.1.3 FLP不可能性定理的证明关键
定理的核心在于,异步系统中无法区分一个进程是失败了还是只是非常非常慢。因此,其他非故障进程可能会永远保持犹豫不决,无法决定一个具体的值。
6.1.4 共识的重要性
尽管存在FLP定理的挑战,共识在分布式计算中仍然非常重要,因为它与许多重要的分布式计算问题相关联。
6.1.5 实际中的解决方案
实际上,存在诸如Paxos协议之类的解决方案,但这些解决方案并不是“完美”的。Paxos提供了安全性和最终活性(eventual liveness)。
“最终活性”意味着尽管系统可能在某些情况下无法迅速达成共识,但它最终会在某个时间点达成共识。
总体来说,FLP定理揭示了在异步系统中解决共识问题的困难,但同时也激发了新的协议和算法的发展,这些协议和算法在实际应用中提供了足够的可靠性,尽管不能保证100%的完美。这些实际解决方案的存在对于维持和提升分布式系统的健壮性和可靠性至关重要。
6.2 Paxos协议
Paxos协议是最流行的共识解决算法之一,由著名计算机科学家Leslie Lamport在1989年发明。Lamport同样也是逻辑时钟这一核心概念的提出者。Paxos协议因其在解决分布式系统中的共识问题上的有效性而广受欢迎,并被许多系统,如Yahoo!的Zookeeper、Google的Chubby以及其他公司所采用。
6.2.1 Paxos协议的关键特点
6.2.1.1 安全性(Safety)
Paxos协议保证了一致性,即使在异步网络中也能保证数据不会出现不一致的情况。
6.2.1.2 FLP定理的限制
根据FLP不可能性定理,在异步网络中,没有任何确定性的容错共识协议能保证进度。但是,Paxos在某些特定条件下仍然能够取得进展,尽管这些条件可能不容易触发。
6.2.1.3 协议家族
Paxos实际上是一个协议家族,包括各种变体,这些变体在处理器数量、在学习协商值之前的消息延迟次数、参与者的活跃程度、发送的消息数量以及故障类型等方面进行了权衡。其中包括基本Paxos、多Paxos(Multi Paxos)、廉价Paxos(Cheap Paxos)、快速Paxos(Fast Paxos)、泛化Paxos(Generalized Paxos)、拜占庭Paxos(Byzantine Paxos)等。
6.2.2 Paxos协议的基本思想
Paxos协议的核心是在一组分布式进程中就某个值达成一致。这一过程通常涉及到多轮的提案(Proposals)和决定(Decisions)。协议确保了即使在参与进程中有消息丢失、延迟或进程故障的情况下,依然可以达成一致的决策。Paxos协议的设计考虑到了网络延迟、分区容错性和动态变化的系统状态。
6.2.3 实际应用
由于其高效和可靠性,Paxos协议在需要高度一致性和可靠性的分布式系统中得到了广泛应用,特别是在分布式数据库、分布式文件系统和其他分布式服务中。我们将学习Paxos协议的简化版本,这有助于理解其基本原理和操作方式,尽管在实际应用中可能使用更复杂或特定场景优化的版本。
6.2.4 Paxos协议的主要假设
Paxos协议是一种分布式共识协议,其运作依赖于一系列假设,这些假设定义了协议的适用范围和限制。以下是Paxos协议的主要假设:
6.2.4.1 进程操作速度任意
进程可以以任意速度运行,没有固定的执行速度或性能标准。
6.2.4.2 崩溃-恢复故障(Crash-recovery failures)
进程可能会失败(崩溃),但它们可以在故障后重新加入协议。这通常需要稳定的存储来保留在崩溃时的状态,以便于恢复时能够从上次的状态继续。
6.2.4.3 异步消息传递
消息是异步发送的,传递时间可能会很长,并且消息可能会丢失、被重新排序或被重复。
6.2.4.4 非拜占庭故障的处理器和网络
处理器不会勾结、撒谎或尝试破坏协议。这意味着处理器的故障仅限于停止工作或性能下降,而不会有恶意的行为。
网络同样不会引发拜占庭错误,消息虽然可能丢失或延迟,但不会被蓄意篡改。
这些假设使得Paxos协议适用于一系列的分布式系统,但也限定了其应用场景。特别是,在处理拜占庭错误(即处理器或网络的恶意行为)方面,Paxos不提供保护。在实际应用中,这意味着Paxos更适合于那些处理器和网络相对可靠,主要面临崩溃或性能问题的环境,而不是需要防御内部恶意攻击或篡改的场景。
6.2.5 安全性(Safety)和活性(Liveness)
Paxos协议旨在满足分布式共识问题中的两个关键要求:安全性(Safety)和活性(Liveness)。这两个要求对于确保协议可靠性和有效性至关重要。
6.2.5.1 安全性(Safety)
提议值的选择:只有被提议的值才能被选定。这意味着任意的、未经提议的值不会意外地成为共识结果。
唯一性:在整个协议过程中,只有单一的值被选定。这避免了不同的进程就不同的值达成共识的可能性。
确信性:一个进程只有在值真正被选定后,才会了解到这个值被选定。这保证了进程不会基于错误或不完整的信息做出决定。
6.2.5.2 活性(Liveness)
最终达成共识:协议的目标是确保最终某个提议的值被选定。尽管可能会有延迟,但最终会有一个确定的结果。
了解选定值:如果一个值被选定,所有的进程最终都能够知道这个值。这意味着信息最终会传播到所有进程,即使可能存在一些初始的延迟或失败。
6.2.5.3 Paxos协议的特性
Paxos协议提供了安全性和最终活性。这表示在Paxos协议中,一旦共识达成,所有非故障的进程最终会接收到相同的共识值,尽管可能存在一定的延迟。
在Paxos协议中,活性是通过一系列复杂的选举和多轮协商过程来实现的,这确保了即使在有进程失败或消息延迟的情况下,共识仍能被达成。
总的来说,Paxos协议是设计来在满足特定假设条件的分布式系统中解决共识问题的,它通过一系列精心设计的机制来保证安全性和活性,即使在异步网络和存在故障的环境中。
6.2.6 Paxos协议的不同角色
在Paxos协议中,进程根据其在协议中扮演的不同角色,可以分为三类:提议者(Proposers)、接受者(Acceptors)和学习者(Learners)。这些角色共同工作以达成共识。每个角色在协议中承担不同的职责:
6.2.6.1 提议者(Proposers)
提议者的职责是提出值。它们向接受者发送提议,这些提议包含了它们希望被整个系统接受的值。
提议者通常代表了系统的客户端或应用,它们需要将某个特定的值或决定推广到整个系统。
6.2.6.2 接受者(Acceptors)
接受者的作用是对提议者的提议进行“投票”。它们可以接受(即投票赞成)或拒绝(即投票反对)一个提议。
一个值被选定是基于多数接受者接受这个值。换句话说,当大多数接受者接受同一个提议时,该值被视为被整个系统选定。
6.2.6.3 学习者(Learners)
学习者的角色是学习被选定的值。它们需要了解哪个值最终被系统接受。
学习者通常代表了需要知道共识结果的系统的其余部分,如复制数据库的各个副本。
在典型的实现中,一个单独的进程可以同时扮演一个或多个角色。例如,一个进程可以同时是提议者和学习者,或者同时是接受者和学习者。这种灵活性允许Paxos协议适应不同的系统需求和配置。
Paxos协议通过这些角色的相互作用来确保即使在异步通信和存在故障的环境下也能达成一致的共识。每个角色都有其在达成共识过程中的关键职责,共同确保协议的正确性和有效性。
6.3 Paxos协议步骤的详细解释
6.3.1 异步轮次以及Paxos轮次的简要概括
Paxos协议使用异步轮次(rounds)来达成共识,每个轮次都通过一个独特的票号(ballot ID)来标识。这些轮次的运作方式和组成阶段如下:
6.3.1.1 轮次的异步性
Paxos协议中的轮次是异步的。这意味着,如果一个进程正在参与轮次 j,但接收到了来自轮次 j+1的消息,它将中止当前轮次的所有操作,并切换到轮次 j+1。
这种设计允许协议适应动态变化的网络条件和进程状态。
6.3.1.2 轮次的三个阶段
6.3.1.2.1 第一阶段(Phase 1):选举领导者
在这一阶段,系统中的一个进程被选举为“领导者”。领导者负责驱动当前轮次的共识过程。
领导者的选举通常基于票号,确保每个轮次都有一个明确的领导者。
6.3.1.2.2 第二阶段(Phase 2):领导者提议值,进程响应
选举出的领导者提出一个值,该值可以是新的提议,也可以是之前轮次中未完成的提议。
其他进程(主要是接受者)对领导者的提议做出响应,即表示接受或拒绝。
6.3.1.2.3 第三阶段(Phase 3):广播最终值
一旦提议得到了足够多接受者的接受,领导者将最终的值广播给所有进程(包括学习者),使得整个系统都能学习到被选定的值。
通过这三个阶段,Paxos协议能够有效地协调不同进程间的行为,以达成共识。尽管Paxos协议的实现可能会因应用的不同而有所差异,但其基本原理和这三个阶段的结构是共通的。这个结构既保证了共识的可靠性,又提供了足够的灵活性以适应各种网络和进程的状态变化。
6.3.2 第一阶段
Paxos协议的第一阶段,即选举“领导者”的过程。这个阶段的目的是选出一个提议者,并且开始与接受者之间的沟通。具体过程如下:
6.3.2.1 提议者(Proposer)的行为
提议选择:提议者选择一个提议编号 n (也被称作投票ID)。
准备请求:提议者向多数接受者发送带有所选提议编号 n 的准备请求(prepare request)。
6.3.2.2 接受者(Acceptor)的行为
承诺:接受者收到一个编号为 n 的准备请求后,如果 n 大于接受者已经响应过的任何准备请求的编号,那么接受者将作出承诺,不再接受任何编号小于n 的提议。
响应最高编号的提议:接受者会将它已经接受的最高编号的提议(如果有的话)作为响应发回给提议者。如果之前没有接受过任何提议,它只需简单地确认承诺并回复编号 n 。
图片底部的图表似乎展示了提议者和接受者之间的通信,其中提议者请求被选为领导者,并且接受者回复“OK!”,表示同意或者确认。
这一阶段在Paxos协议中非常关键,因为它通过确保一旦接受者对某个编号的准备请求作出了响应,就不能再接受任何编号更低的提议,从而防止了冲突。这种机制确保了提议按照顺序被考虑,并且最终只选择一个值。
6.3.3 第二阶段
Paxos协议的第二阶段,即领导者提出值并由接受者确认这一值的过程。以下是该过程的详细步骤:
6.3.3.1 提议者(即领导者)的行动
如果提议者从多数接受者那里收到了对其准备请求(提议编号为 n)的响应,那么它就会继续下一步。
提议者确定要提议的值v。这个值是基于接受者回应中编号最高的提议值v_a来选择的。如果接受者没有接受过任何提议,则提议者可以提出任何值。
然后提议者向这些接受者发送接受请求,包含提议编号n 和选定的值v。
6.3.3.2 接受者的行动
接受者收到编号为 n 和值为的接受请求后,会接受提议,除非它已经对编号大于n 的准备请求做出了响应。
如果接受了提议,接受者会将其接受的最高编号的提议设置为)(即),并将接受的值设置为)(即 )。
接受提议后,接受者会通知学习者关于被接受的提议,以便学习者可以更新他们对系统状态的了解。
图片底部的图表显示了提议者和接受者之间的消息流动,指出提议者发送值,并且接受者在接受提议后回复“OK”。
这个阶段至关重要,因为如果多数接受者同意,就可能在这个点上选定一个值。要求多数同意的机制确保了在协议的给定轮次中只能选定一个值,这是维护系统一致性的关键部分。
6.3.4 第三阶段
在Paxos协议的第三阶段,最终确定的值被广播(multicasted)给所有学习者(Learners)。学习者的角色是了解最终被选择的值,以便系统可以采取相应的行动。这一阶段确保了一致性决策能够被系统内的所有相关部分知悉。
有两种方法可以让学习者知道哪个值被选中:
6.3.4.1 方法一
每个接受者直接通知所有学习者:这种方法简单直接,但成本较高,因为每个接受者都必须将消息发送给所有的学习者,这在有许多学习者的情况下会导致大量的消息传输。
6.3.4.2 方法二
选举一个“杰出学习者”(distinguished learner):在这种方法中,接受者只需要通知被选举出来的杰出学习者,然后由这个杰出学习者负责将决定的值通知其他所有学习者。
这种方法可能更容易受到单点故障的影响,因为如果杰出学习者出现故障,那么其他学习者可能无法得知最终选择的值。
通常情况下,会根据系统的特点和需要处理的负载来选择最合适的方法。在某些系统中可能会采用更复杂的机制,例如使用消息队列或发布/订阅系统来优化这一过程。
6.3.4 安全性
Paxos协议被认为是“安全的”(safe),这是因为它能够保证即使在分布式系统中出现故障和不确定性时,一旦某个值被选定,就不会有其他的不同值被选定。
这里的直觉解释是:如果一个提议的值 v 被选定,那么任何提议者发起的具有更高编号的提议也必须使用值 v。
这个直觉背后的原因是:
6.3.4.1 多数原则
Paxos协议要求一个提议获得多数接受者的接受才能被选定。这意味着至少有半数以上的接受者同意了提议。
6.3.4.2 多数交集
在Paxos协议中,任何两个多数集合必然有交集。这意味着任何新的多数提议必须获得至少一个之前已经同意旧提议的接受者的支持。由于这个接受者之前已经接受了值 v \),而根据协议,它不能接受一个更低编号的提议,因此新的多数提议也只能包含值 v \)。
6.3.4.3 编号递增
每个新的提议都有一个比之前所有提议更高的编号。这保证了选定值的一致性,因为一旦一个值被多数接受者接受,随后的所有提议都必须包含这个被接受的值,以保持一致性。
因此,通过要求多数接受并且确保编号的递增,Paxos协议可以确保一旦一个值被选定,所有后续的提议都不会改变这个决定,从而实现了安全性。这是Paxos协议设计的核心,它确保了在分布式系统中的共识决定是一致和可靠的。
6.3.5 最终一致性
您上传的图片解释了Paxos协议中的"(最终)活性"问题。在分布式系统中,活性是一个保证系统最终能够做出决策的属性。在Paxos协议的背景下,活性意味着最终应该会有一个提议被选择,系统应该能够取得进展。
然而,幻灯片指出Paxos通常是活跃的,但并不总是如此。提供的示例说明了一个潜在的问题:
进程0:完成了提议编号 n0 的第一阶段,然后进入第二阶段。但是,接受者拒绝了进程0的提议。随后,进程0重新开始第一阶段,并且使用一个新的更大的提议编号 n2。
与此同时,进程1 开始并完成了提议编号 n1 的第一阶段,这个编号大于 n0,然后它进入第二阶段,但是它的提议也被接受者拒绝。
这可能导致两个进程不断地以递增的编号发出新的提议,但这些提议不断被接受者拒绝。这种情况可能无限地持续下去,没有任何提议被接受,表明Paxos并不能总保证活性。
术语"最终活性"暗示尽管系统可能会陷入这种拒绝循环,但期望在某个时刻,条件会允许某个提议被接受,从而使系统能够取得进展。这种过程的不确定性是为什么协议只保证"最终活性"而不是立即活性的原因。
好的,让我用一个更简单的例子来解释“最终活性”问题:
假设有两个小组(进程0和进程1)在一个会议上分别推荐他们的计划。这个会议的目标是选出一个计划来实施。
1. 进程0提出了计划A,并请求大家同意(这就是第一阶段)。然后,它告诉大家“我们执行计划A吧”(这是第二阶段,也就是提出具体的计划)。
2. 与此同时,进程1也提出了计划B,它的计划编号比进程0的计划A的编号要高。
3. 如果参与会议的大多数成员(接受者)都不同意进程0和进程1的计划,那么这两个进程就需要重新提出新的计划。
如果这种情况不断发生——进程0和进程1不断地提出新的计划,但都没有得到大多数人的支持——那么会议就会陷入僵局,似乎永远无法决定执行哪个计划。这就是所谓的“没有活性”,因为没有实际的进展。
但是,Paxos协议的设计理念是基于一个假设:最终,会有某个时刻,所有的参与者(接受者)会对某个计划达成共识。这可能是因为大家厌倦了不断的争论,或者因为最终有一个更有说服力的计划被提出。这就是“最终活性”的含义——尽管过程可能会有延迟和很多重试,但是最终,会议(系统)会决定一个计划来执行。
换句话说,“最终活性”意味着尽管决策过程可能会延迟,但Paxos保证最终会有一个决策被做出,会议不会永远处于僵局状态。
在Paxos协议中,如果多数成员(接受者)不同意进程0和进程1的计划,这通常是因为这些进程提出的提议编号不是目前已知的最高编号。根据Paxos的规则,接受者只能接受编号最高的提议,这样做是为了保证系统的一致性。
这里是可能发生的一种情形:
进程0 提出了编号为 n0 的计划A。
进程1注意到进程0的行动,它提出了编号更高的 n1 的计划B。
由于Paxos要求接受者必须考虑最高编号的提议,所以即使进程0的计划A已经被某些接受者所接受,进程1的计划B由于编号更高,这就导致了先前接受计划A的接受者必须转而考虑计划B。
在进程1的计划B发送给接受者后,如果进程0再次提出一个新的计划,它必须使用一个比 n1 更高的编号,我们可以称之为 n2。
如果进程0和进程1不断地这样轮流超越对方,不断地提出更高编号的计划,而没有达成一致,这就会导致所谓的“活性锁”,因为每个新的计划都阻止了之前的计划得到接受。这种情况下,决策过程就会陷入停滞,因为总是有一个更高编号的提议在路上,接受者始终在等待可能会出现的、更高编号的提议。
但是,Paxos协议保证了“最终活性”,这意味着尽管可能出现短期的活性锁,但最终会有一些机制(比如选举一个新的领导者)来打破僵局,从而允许一个计划被接受。这可能是因为提议者之间达成了某种间接的协调,或者因为系统状态的某种变化,使得一些提议者停止提交新的提议,或者所有接受者最终接受了一个特定的提议。
7. 拜占庭容错
拜占庭容错(Byzantine Fault Tolerance, BFT)是分布式系统中的一个概念,指的是系统能够抵抗拜占庭故障的能力。拜占庭故障是指在系统中,一部分节点可能会出现故障,并且这些故障的节点可能以任意方式行为,包括发送错误或矛盾的信息,或者完全不发送信息。这种行为可能是因为错误、攻击或其他恶意行为导致的。
7.1 拜占庭故障与崩溃-停止(crash-stop)故障
7.1.1 拜占庭故障的特点
在受控环境中(例如谷歌或亚马逊的数据中心),这种故障类型一般不会发生。
在对抗环境中更常见,比如在互联网上的分布式系统,节点可能故意以“拜占庭”方式行事,即恶意地选择修改、阻塞或根本不发送消息。
7.1.2 简单的容错
像Paxos这样的算法可以用来处理崩溃-停止(crash-stop)故障,也就是节点在故障后不会再进行任何操作。
为了抵抗至多 f 个节点的崩溃-停止故障,需要至少拥有 2f + 1 个副本。
7.1.3 拜占庭容错
在拜占庭系统中,节点可能撒谎、协同作弊或以任意方式行事。简单的多数投票不足以达成共识。
如果选举出的领导者是拜占庭节点,它可能会破坏整个系统。为了抵抗拜占庭故障,需要至少有 3f + 1 个副本。
这意味着在可能出现拜占庭行为的系统中,为了确保系统能够达成共识并继续正常运作,需要更多的冗余和更复杂的协议来确保即使在一部分节点可能会恶意行事的情况下也能保持一致性。这通常涉及到使用额外的通信轮次和复杂的验证机制来确保即使在存在恶意节点的情况下也能达到一致。比如著名的拜占庭容错协议包括PBFT(Practical Byzantine Fault Tolerance)和它的变种。
7.2 拜占庭将军问题
拜占庭将军问题是由Leslie Lamport、Robert Shostak和Marshall Pease在1982年提出的一个经典分布式系统问题。这个问题描述了一个假想的场景,其中拜占庭帝国的将军们和他们的军队包围了敌人的城市。他们只能通过信使互相通信,并且必须就共同的战斗计划(进攻或撤退)达成一致,否则就会面临失败。在这些将军中,有一些可能是叛徒,他们的目的是阻止其他将军达成共识。
拜占庭将军问题的核心挑战:
如何在部分参与者可能不诚实的情况下,确保所有诚实的参与者可以达成一致的决策。
如何设计通信协议和算法来保证即使在有叛徒存在的情况下,也能做出正确的集体行动。
解决条件:
已经证明,要解决拜占庭将军问题,至少需要超过三分之二的将军是忠诚的。这是因为在任何给定的时间,忠诚的将军们能形成多数派,并且他们的决策将不会受到叛徒的影响。
算法限制:
1982年论文中展示的算法是为同步环境设计的,即假设消息的传递时间是有上限的,参与者的行动是可以按时间顺序进行协调的。
在实际应用中,为了解决拜占庭将军问题,研究者们发展了许多拜占庭容错(BFT)算法,这些算法能够在异步环境中工作,其中包括了在网络延迟不确定和参与者可能会恶意行为的情况下,也能确保系统达成共识。这些算法通常需要复杂的投票和多数派确认机制,并且可能需要更多的通信轮次和冗余消息,以保证系统能够在存在拜占庭行为的参与者时仍然正确地运行。
7.3 异步系统中实现拜占庭容错的算法
在异步系统中实现拜占庭容错(BFT)是一个挑战,因为FLP不可能性定理表明,在一个完全异步的系统中,不能保证在存在单个故障节点的情况下总是达到共识。要解决这个问题,研究者们提出了两种主要的方法:
7.3.1 引入一定的同步性
这种方法的目标是在系统中引入足够的同步性,以至于可以克服FLP不可能性和实现活性。这通常意味着系统中的通信和进程步骤有一些已知的时间界限,或者至少在某些时期是同步的。
在传统的共识算法中,提议者和一组接受者必须协调沟通才能决定下一个值。这要求知道网络中的每个节点,并且每个节点需要与其他每个节点通信,导致了二次方级别的通信开销。
传统共识不适用于规模较大的网络,也不适用于开放的、无需许可的系统,因为它的扩展性差。
7.3.2 使用非确定性
另一种方法是使用随机性或非确定性元素,以便在不确定的延迟和潜在的拜占庭行为存在的情况下,系统仍然能够达到共识。
非确定性方法,如Nakamoto共识(比特币中使用的工作证明,Proof of Work,简称PoW),通过引入一定的随机性来解决FLP不可能性问题。
Nakamoto共识不要求所有节点之间完全同步。相反,它利用工作量证明机制来决定哪个节点有权添加新的记录到区块链。这个过程通过解决一个计算难题来实现,解决难题的节点获得创建下一个区块的权利。
这种方法允许系统在没有全局或锁定同步的情况下达成共识。虽然这种方法可能会引入其他问题(如大量的计算资源消耗和潜在的安全问题),但它在如比特币这样的开放、无需许可的系统中实现了可扩展性和活性。
总的来说,非确定性方法为分布式共识提供了一种替代传统同步算法的手段,特别是在大规模和开放网络环境中。通过降低对即时同步的需求,这种方法使得系统能够在更广泛的环境中有效地运作。
7.4 两种引入同步性的著名算法
7.4.1 DLS算法
由Dwork, Lynch, 和 Stockmeyer在1988年提出的"DLS算法",其全称是“Consensus in the Presence of Partial Synchrony”。这个算法描述了在部分同步模型下,即系统在长时间的异步行为之后会有一段同步的时间,共识是如何达成的。
DLS算法是Dwork, Lynch和Stockmeyer为了在"部分同步系统"中达成共识而设计的算法。在这种系统中,通信可能会出现延迟,但这些延迟不会无限大,而是存在某些界限。DLS算法考虑了两种部分同步的情况:
7.4.1.1 消息延迟的未知固定界限
在这种情况下,消息传递的延迟有一个固定的上界,但这个界限的具体值是未知的。DLS算法设计的目标是不依赖这个界限的实际值,也能够设计出一个共识算法。
7.4.1.2 消息延迟的已知界限但开始时间未知
在这种情况下,消息传递的延迟界限是已知的,但这些界限保证开始生效的具体时间点是未知的。DLS算法需要设计出一个系统,无论这个时间点何时发生,都能够达成共识。
这种部分同步的假设足以实现活性条件,从而克服了FLP不可能性定理所描述的问题。FLP不可能性定理指出,在完全异步的系统中,不能保证总是能够达到共识,特别是在存在故障的情况下。通过引入部分同步,DLS算法能够保证系统最终能够达到共识。
DLS算法将共识过程分成一系列轮次,每个轮次又分为“尝试”和“释放锁”两个阶段。
在“尝试”阶段,节点尝试达成共识;在“释放锁”阶段,如果节点发现它们之前的尝试未能成功,它们会放弃当前的状态,准备开始下一个轮次的尝试。
尽管DLS算法在理论上是一个重要的步骤,但它在实际的拜占庭环境中并未得到广泛实现或使用。DLS算法的一个核心假设是使用同步的处理器时钟来获得共同的时间概念,但在实际应用中这不是特别实用。在实际的分布式系统中,确保完全同步的时钟是非常困难的,因为各个节点的时钟可能会由于多种原因而发生漂移。
7.4.2 PBFT算法
由Miguel Castro和Barbara Liskov在1999年提出的"PBFT算法",全称是“Practical Byzantine Fault Tolerance”。PBFT被设计用来在分布式计算系统中提供拜占庭容错的共识机制,即使在异步网络中也能保证系统的安全性和活性。PBFT算法通过多轮通信以及复制状态机的方法来达到这个目的,它要求系统中至少有3f+1个节点,以承受f个可能的拜占庭故障节点。
这两个算法都是拜占庭容错算法研究领域的重要工作,它们证明即使在面临复杂故障和异步条件的分布式系统中,达成共识也是可能的。这些算法为构建可靠的分布式系统提供了基础,特别是在需要处理恶意行为和网络问题的场景中。
实用拜占庭容错(Practical Byzantine Fault Tolerance,简称PBFT)算法是由Miguel Castro和Barbara Liskov在1999年提出的,它旨在为分布式系统提供一种既安全又能实现活性的共识机制,尤其是在可能存在恶意节点的情况下。
7.4.2.1 为什么称为“实用”?
PBFT被称为“实用”因为它能够在诸如互联网这样的异步环境中工作,并且比之前的拜占庭容错共识算法更快。这使得PBFT更适合实际应用,而不仅仅是理论上的解决方案。
7.4.2.2 PBFT的工作原理
PBFT假设在所有节点中,最多有 个是有故障的。这是一个理想的比例,因为在拜占庭问题的背景下,要确保系统能够抵抗少于或等于系统节点三分之一的恶意节点。
7.4.2.3 活性和同步性的关系
虽然PBFT算法可以在异步环境下提供安全性,但它对活性的保证依赖于某种形式的同步性假设。它假定消息延迟不会无限增长,并且存在一个时间限制,使得消息最终会在这个时间限制内到达。这样一来,即使网络条件变差,系统也能保持活性,继续进展。
7.4.2.4 与之前算法的改进
PBFT在减少消息复杂度和提高效率方面相比之前的算法有所改进。PBFT的消息交换数量为,这意味着节点数量的增加会导致所需的消息数量以二次方的速度增加。
7.4.2.5 现实世界的应用限制:
尽管PBFT算法在一些环境下非常有效,但它在具有大量参与者的真实世界用例中,例如公共区块链,可能就不够实用了。这是因为当节点数量非常大时,\( O(n^2) \)的消息复杂度会导致巨大的通信开销,这可能会使得算法在实际应用中变得不切实际。
总的来说,PBFT是分布式系统和分布式共识领域的一个里程碑,它证明了即使在拜占庭行为存在的情况下,系统也可以达到一致性和活性。然而,随着参与者数量的增加,它的实用性有所限制,尤其是在需要高度可扩展性的场景中。
7.5 应用场景
Nakamoto共识(Nakamoto Consensus),最著名的应用是在比特币(Bitcoin)中,是一种基于概率的共识机制。它与传统的共识算法不同,因为它不要求每个节点对特定值达成一致,而是使节点们同意某个值被正确接受的概率。
Nakamoto共识的核心特点包括:
7.5.1 计算难题
节点需要解决一个计算难题,通常是找到一个哈希函数的碰撞。这个问题的难度被控制,以确保平均每10分钟解决一次。
解决这个难题最快的节点(被称为矿工)有权将一个新的区块添加到区块链上,并获得一定的奖励,从而经济上激励节点参与。
7.5.2 链的延续
一旦一个节点(矿工)添加了新区块,整个网络就会在这个区块链上继续构建。
这个过程涉及到每个参与节点对区块链的有效性和持续性的认可。
7.5.3 分叉(Fork)的可能性
由于不同节点可能几乎同时解决计算难题,区块链可能会出现分叉,即出现两条潜在的路径。
在这种情况下,具有最多累积计算工作量的链被视为规范链。
7.5.4 51%攻击的风险
如果攻击者控制了超过50%的计算能力,他们可以操控规范区块链的发展。
这被称为51%攻击,是Nakamoto共识的主要安全风险之一。
7.5.5 概率性的安全保证
Nakamoto共识的安全保证是基于概率的。随着区块链上不断增加的新区块,恶意节点试图构建另一条有效链的概率逐渐降低。
这种方式下,最长的链(即有最多计算工作量支持的链)被认为是最可信的。
总体来说,Nakamoto共识是一种在分布式网络中,尤其是在公共、无需许可的区块链(如比特币)中有效实现共识的创新方法。它通过经济激励和概率性的安全保障,允许网络在没有中央权威机构的情况下运行,尽管它也带来了特定的安全风险和挑战。