共识算法揭秘:理解分布式系统的关键
一. 引言
1. 分布式系统面临的挑战
分布式系统是由多个计算机节点协同工作,共同完成一个任务或提供一个服务的系统。这些节点通过网络进行通信和协调,彼此之间相互独立且具有一定程度的自治性。分布式系统的设计旨在提高系统的可靠性、可扩展性和性能。
然而,分布式系统也面临一些挑战和困难:通信和协调、一致性和同步、容错性和可靠性、可扩展性、安全性和隐私保护、故障诊断等等。
解决这些挑战需要采用适当的架构设计、协议和算法,以确保分布式系统的可靠性和性能。其中解决一致性和可用性可以说是最重要的事情,需要选择合适的共识算法。
分布式一致性问题主要有两类挑战:
1),故障错误(Crash Fault)
节点随时可能宕机,然后又可能随时恢复
节点硬件可能会坏掉
网络随时可能中断
网络消息可能会丢失,或延迟
消息可能出现乱序
网络可能分化或隔离,形成 1 个以上的子网络
这些都是由于分布式系统中的物理硬件的不可靠,不稳定带来的风险,是我们系统设计中必须考虑的问题。
2),拜占庭错误(Byzantine Fault)
伪造信息
2. 共识算法的定义
维基百科:分布式系统的一个基本问题是在存在一些故障进程的情况下实现系统的整体可靠性。这往往需要协调进程来达成共识,或者就计算过程中需要的一些数据值达成一致。共识的应用实例包括同意以何种顺序向数据库提交哪些事务、状态机复制和原子广播。
简单说在分布式系统中,共识就是系统中的多个节点对某个值达成一致。即使出现部分节点故障,网络延时等情况,也不影响各节点,进而提高系统的整体可用性。
举个生活中例子,你和几个朋友要去吃饭,你说“咱们去吃烧烤吧”,大家都同意即达成了共识。
真正意义上的共识算法需要满足以下特性:
终止性(Termination):所有正确的进程最终都会认同某一个值。
一致性(Agreement):所有正确的进程认同的值都是同一个值。
完整性(Integrity),也称作合法性(Validity):如果正确的进程都提议同一个值,那么所有处于认同状态的正确进程都选择该值。
终止性,描述了算法必须在有限时间内结束,不能无限循环下去;一致性描述了我们期望的相同决议;合法性是为了排除无效或无意义的值。
3. 文章范围的概述
本文限于篇幅,不会对算法部分细节展开讨论,希望能帮助读者对于共识算法有个整体上的认知。
二. 拜占庭将军问题
1、拜占庭将军问题是什么?
拜占庭将军问题实际上是一个共识问题。通过类比的方式来描述分布式系统中最困难的一类问题:
拜占庭帝国派出多支军队去围攻一个强大的敌人,每支军队都有一个将军,但是由于彼此之间距离较远,他们之间只能通过信使传递消息。敌方实力强大,只有超过半数的拜占庭军队一起参与进攻才能击败敌人。在这个过程中,将军们彼此之间需要通过信使传递消息并协商一致,然后在同一时间点发起进攻。
问题难点:这些将军们困扰于不确定自己中是否存在叛徒,叛徒可能会擅自改变进攻意图或进攻时间。在这种情况下,拜占庭的将军们能否找到一种分布式协议,让他们能够远程协商并取得胜利?
2、问题实质推演
回顾上述问题:
一群将军希望达成某个目标(一致进攻或一致撤退),但单独行动无法实现,必须合作达成共识。由于叛徒的存在,将军们不知道如何达成一致。
需要注意的是,在拜占庭将军问题的探讨中,重点在于"一致性"。如果叛徒的数量已经多到问题无法解决的程度,那就是"反叛"的问题了。同时,我们的目标是让忠诚的将军能够达成一致,对于这些忠诚的将军来说,进攻或撤退都是可以的,只要他们能够达成一致就行。
但是,仔细想想:仅凭“一致性”就可以解决问题吗?
考虑这样一种情况:假设一切都准备就绪,客观上每个忠诚的将军只要进攻就一定能够胜利,但由于叛徒的存在,他们无法达成一致性而无法进攻;相反地,条件并不利,将军们不应该进攻,但由于叛徒的存在,却出现了所有人都一致性进攻的情况。因此,我们还需要提出一个"正确性"要求。
那我们该如何定义这个“正确性”呢?
我们可以简单地说,正确性意味着每个忠诚的将军都能正确表达自己的意图,并且其他将军不会因为错误地认为忠诚的将军是叛徒而忽略他传达的消息。最终,所有忠诚的将军应该能够相互传达自己的真实意图并达成共识。形式上的要求就是“一致性”和“正确性”。
对应到实际应用场景,我们不仅需要考虑网络问题和一致性问题,还需要考虑数据的正确性和是否被篡改问题。
三. 非拜占庭将军问题
通常情况下,发生故障(如崩溃或停止响应)但不产生伪造信息的情况被称为“非拜占庭错误(Non-Byzantine Fault)”。
四. FLP 不可能性结果
提出并证明该定理的论文《Impossibility of Distributed Consensus with One Faulty Process》是由 Fischer、Lynch 和 Patterson 三位科学家于 1985 年发表,该论文后来获得了 Dijkstra 奖。
“It is impossible to have a deterministic protocol that solves consensus in a message-passing asynchronous system in which at most one process may fail by crashing.”
在一个完全异步的消息传递分布式系统中,最多有一个进程可能因崩溃而失败,不存在一个可以解决一致性问题的确定性共识算法。
简单来说,这意味着在异步系统中,不可能设计出能够满足协议、终止和容错的确定性共识算法。
关键词
deterministic protocol:
● 即对于一个特定的输入,始终产生相同的输出
asynchronous:
● 不假设进程的相对速度或传递消息的延迟时间
● 进程无权访问同步时钟
● 无法检测进程的死亡
crashing:
● 非拜占庭问题,非 fail-stop,只是崩溃问题。
● 所有进程遵循协议,除了崩溃的进程。
solve:
● 意味着同时满足一致性和终止性。
不可能的根本原因在于,在异步网络模型的定义中,消息传递的延迟和节点处理的延迟没有上限。因此,在节点实际崩溃时,我们无法确定是否有节点发生崩溃,只能无限等待下去,使共识算法无法满足“终止性”。然而,在实际环境中,我们可以允许共识算法使用超时、随机化、故障检测或其他方法来识别可疑的崩溃节点(即使有时怀疑是错误的)。这样就避免了无限等待,并使得达成共识成为一个可行的任务。Paxos 和 Raft 就是这样的共识算法的例子。
FLP 不可能性原理告诉我们,不要浪费时间尝试为异步分布式系统设计适用于任何场景的共识算法。换句话说,我们需要有边界思维,并根据实际情况设计可行的工程方案。
五. 关键共识算法概念
CAP 定理
FLP 不可能原理告诉我们要有边界思维,不要存有妄念去追求完美的共识方案,那么,退一步,在付出一些代价的情况下,共识能做到多好?回答这一问题的是:CAP 理论。
CAP 定理指出对于任何分布式数据存储系统来说,只能提供以下三项保障中的两项:
一致性(Consistency),客户端的每次读操作,不管访问哪个节点,要么读到的都是同一份最新的数据,要么读取失败。
可用性(Availability),任何来自客户端的请求,不管访问哪个节点,都能得到响应数据,但不保证是同一份最新数据。你也可以把可用性看作是分布式系统对访问本系统的客户端的另外一种承诺:我尽力给你返回数据,不会不响应你,但是我不保证每个节点给你的数据都是最新的。这个指标强调的是服务可用,但不保证数据的一致。
分区容错性(Partition Tolerance),当节点间出现任意数量的消息丢失或高延迟的时候,系统仍然可以继续提供服务。也就是说,分布式系统在告诉访问本系统的客户端:不管我的内部出现什么样的数据同步问题,我会一直运行,提供服务。这个指标,强调的是集群对分区故障的容错能力。
由于节点间的分区故障是必然发生的。也就是说,分区容错性(P)是前提,是必须要保证的。
当选择了一致性(C)的时候,即 CP,那么当分区故障发生时,新的写入请求是会被拒绝的,因为无法保证所有节点都是最新数据。
当选择了一致性(A)的时候,即 AP,那么当分区故障发生时,部分节点返回的数据有可能不是最新的。
澄清几个误解:
1,即只有在发生分区故障时,才会在 C 和 A 之间选择一个,正常运行的时候 C 和 A 是能够同时保证的。分区故障概率很低的。
2,C 和 A 的选择不是针对整个分布式系统,子系统可以选择不同的指标。
3,这三个指标的选择不是极端的,比如 AP 系统里,C 只是被弱化,不意味着完全不考虑。
BASE 理论/原则
BASE 理论是 CAP 理论中 AP 的延伸,考虑了网络时延和系统恢复正常后的状态,是对互联网大规模分布式系统的实践总结,强调可用性。
BASE 理论通过放松一致性要求并将可用性和最终一致性作为分布式系统设计原则的重点,对 CAP 定理进行了扩展。它承认在存在故障和网络分区的情况下实现强一致性可能具有挑战性,并且在某些情况下为了改善可用性和分区容忍性而牺牲严格一致性是合理的权衡。
BASE 理论核心思想是:既然无法达到强一致性,就采用合适的办法达到最终一致性。
基本可用(Basically Available),基本可用就是假设系统某个模块出现了不可预知的故障,但其他模块依旧可用
软状态(Soft state),软状态指的是允许系统中的数据存在中间状态,并认为该状态不影响系统的整体可用性,即允许数据副本存在短暂的不一致。
最终一致性(Eventually consistent),意味着在足够的时间内,系统通过解决冲突和协调副本之间的差异来达到一致状态。
BASE 理论主要是针对 NoSQL 数据库而言的,它是对 CAP 理论中一致性(C)和可用性(A)进行权衡的结果。最终一致性是 BASE 原理的核心,也是 NoSQL 数据库的主要特点,通过弱化一致性,提高系统的可伸缩性、可靠性和可用性。而且对于大多数 Web 应用,其实并不需要强一致性,因此牺牲一致性而换取高可用性,是多数分布式数据库产品的方向。
以下是一些符合 BASE 理论的系统:
NoSQL 数据库:像 DynamoDB、Cassandra、Riak、CouchDB 等 NoSQL 数据库普遍采用了 BASE 理论的思想。它们强调可用性和分区容忍性,通过最终一致性实现数据的复制和分布。
分布式文件系统:一些分布式文件系统,如 GFS(Google File System)和 HDFS(Hadoop Distributed File System),也倾向于 BASE 理论。它们追求高可用性和分区容忍性,并通过异步复制和副本管理实现数据的一致性。
消息队列系统:消息队列系统,如 Apache Kafka、RabbitMQ 和 ActiveMQ,也符合 BASE 理论。它们注重可用性和分区容忍性,通过异步传输和最终一致性确保消息的可靠传递。
分布式缓存:像 Memcached 和 Redis 这样的分布式缓存系统通常遵循 BASE 理论的原则。它们追求高可用性和性能,并通过最终一致性来实现数据的复制和分布。
需要注意的是,BASE 理论并非对于特定系统或领域的具体要求,而是一种设计思想和权衡考虑。在实际应用中,BASE 理论的具体实现和应用可能因系统的特点和需求而有所不同。因此,在选择和使用系统时,应根据具体情况评估系统的可用性、一致性和分区容忍性等要求,并根据需求做出合适的选择。
六. 共识算法的类型
限于篇幅,这里只列出几个常用的共识算法并简单描述。
拜占庭容错算法(比如 PoW 算法、PBFT 算法),在相对开放的场景中应用广泛,比如公链、联盟链。
非拜占庭容错算法(比如 Paxos 算法、Raft 算法),主要用于封闭、绝对可信的场景中,比如私链、公司内网的 DevOps 环境。
Paxos 算法
Paxos 算法包含 2 个部分:
一个是 Basic Paxos 算法,描述的是多节点之间如何就某个值(提案 Value)达成共识;
另一个是 Multi-Paxos 思想,描述的是执行多个 Basic Paxos 实例,就一系列值达成共识。
Basic Paxos 算法
在 Basic Paxos 中,有提议者(Proposer)、接受者(Acceptor)、学习者(Learner)三种角色,他们之间的关系如下:
提议者(Proposer):提议一个值,用于投票表决。
接受者(Acceptor):对每个提议的值进行投票,并存储接受的值
学习者(Learner):被告知投票的结果,接受达成共识的值,存储保存,不参与投票的过程。一般来说,学习者是数据备份节点。
达成共识:
整个共识协商是分 2 个阶段进行的,简单描述如下:
准备(Prepare)阶段,提议者分别向所有接受者发送包含提案编号(唯一标识)的准备请求。
接受(Accept)阶段,提议者在收到大多数节点的准备响应之后,会分别发送接受请求,包括了提案编号和提案的值。
Multi-Paxos 思想
上面提到 Multi-Paxos 是多个 Basic Paxos 实例,就一系列值达成共识,结合 Basic Paxos 算法的流程描述会发现将存在几个问题:
多个提议者提交提案,可能导致频繁的协商失败。
两阶段协商(准备、接受),导致性能低下,出错的概率变高。
针对这两个问题,Multi-Paxos 的解决方案:
引入领导者节点,领导者作为唯一的提议者,避免了多个提议者的提案冲突。
当领导者处于稳定状态,省掉准备阶段,直接进入接受阶段。
Raft 算法
Raft 算法现在是分布式系统开发首选的共识算法。Raft 可以看成是 Multi-Paxos 的改进和具体实现算法。
成员身份,也叫节点状态:分为领导者(Leader)、跟随者(Follower)和候选人(Candidate)3 种状态。
选举领导者
因为领导者是唯一的,因此需要有选举流程,大致如下:
跟随者等待领导者的心跳信息,如果随机超时时间内等不到,即认为超时,这时这个节点(假定为 A)增加自己的任期编号,并推举自己为候选人,先投自己一票,然后向其它节点发投票消息请求它们选举自己。
其它节点如果没投过票,就投给节点 A,并更新自己的任期编号。
如果节点 A 赢得大多数选票,即成为新的领导者。
其它说明:
必须是日志最完整的节点才能当选为领导者。
Raft 算法与 Paxos 有个很大的区别是,日志必须是连续的。
任期编号用于标识领导者,每个节点都保存一份。
实用拜占庭容错(PBFT)
PBFT 是一种能在实际场景中落地的拜占庭容错算法,在区块链中应用广泛。
拜占庭错误,在网络问题的基础上,还需要处理伪造信息的问题。上面的半数以上节点达成共识的算法解决不了这种问题。
PBFT 引入了签名机制 + 三阶段协议来达成共识:
签名,可以保证消息发送者的身份和消息内容不被伪造和篡改。
三阶段协议分为,预准备阶段、准备阶段、提交阶段。因为拜占庭将军们是分散的,没有一个中心的领导机构,所以需要多个阶段达成一致。
PBFT 算法能容忍 (n - 1)/3 个恶意节点 (也可以是故障节点)
在准备和提交阶段,每个节点是要把消息广播给其它节点的,消息数量还是比较多的。因此适合在中小型分布式系统中使用。
工作量证明(PoW)和区块链共识
PBFT 算法能够容忍(n - 1)/3 个恶意节点,在中小规模且相对可信的场景下已足够,但在公共网络中运行则不够安全。
工作量证明(Proof of Work)通过增加恶意行为的成本来解决这个问题。防止坏人作恶的最佳方式是让其付出更高的成本,使其利益小于成本,甚至无法承担成本。
具体而言,工作量证明通过执行哈希计算(比特币使用 SHA-256 算法)来完成,经过一段时间的计算后,得到符合特定条件的哈希值,然后将该信息广播给网络中的所有节点。其他节点在验证后将该区块添加到区块链中。
存在的问题
高能耗:需要大量的电力和计算资源。
性能低下:比特币每秒处理约 7 笔交易,以太坊每秒处理 10-20 笔交易。
算力集中化:计算能力集中在矿池中。
总结
本文先介绍了分布式系统面临的挑战以及共识算法可用于解决其核心难题和痛点;
然后表述了共识算法的定义,并说明了其关键特性:终止性、一致性和合法性;
拜占庭将军问题和非拜占庭将军问题提供了对分布式共识问题的情景化描述;
FLP 不可能性原理为共识问题设定了界限,告诉我们不要浪费时间追求完美的共识算法;
CAP 定理和 BASE 原则提出了现实可行的理论指导;
最后,简要介绍了 Paxos、Raft、PBFT 和 PoW 算法。
版权声明: 本文为 InfoQ 作者【Steven】的原创文章。
原文链接:【http://xie.infoq.cn/article/c0424b578b9e54a01d49ac2ff】。文章转载请联系作者。
评论