点一下关注吧!!!非常感谢!!持续更新!!!
🚀 AI篇持续更新中!(长期更新)
目前2025年06月16日更新到:
AI炼丹日志-29 - 字节跳动 DeerFlow 深度研究框斜体样式架 私有部署 测试上手 架构研究,持续打造实用AI工具指南!📐🤖
💻 Java篇正式开启!(300篇)
目前2025年06月28日更新到:
Java-58 深入浅出 分布式服务 ACID 三阶段提交3PC 对比2PC
MyBatis 已完结,Spring 已完结,Nginx已完结,Tomcat已完结,分布式服务正在更新!深入浅出助你打牢基础!
📊 大数据板块已完成多项干货更新(300篇):
包括 Hadoop、Hive、Kafka、Flink、ClickHouse、Elasticsearch 等二十余项核心组件,覆盖离线+实时数仓全栈!
目前2025年06月13日更新到:
大数据-278 Spark MLib - 基础介绍 机器学习算法 梯度提升树 GBDT案例 详解
Paxos 算法详解
基本介绍
Paxos 算法是由计算机科学家 Leslie Lamport 提出的一种基于消息传递的分布式一致性算法,这项开创性工作使他获得了2013年图灵奖。该算法解决了分布式系统中如何就某个值(决议)达成一致的问题,即使在部分节点失效或网络不稳定的情况下也能保证系统一致性。
发展历史
Paxos 算法最早由 Lamport 于 1998 年在《The Part-Time Parliament》论文中首次公开。论文采用了一种独特的叙述方式,使用希腊的一个名为 Paxos 的小岛作为比喻,详细描述了 Paxos 小岛中通过议会决议的流程,并以此命名这个算法。这种隐喻式的描述虽然富有创意,但增加了理解的难度。
在2001年,Lamport 意识到学术同行们难以理解他这种幽默的表达方式,于是重新发表了更加简洁直接的算法描述版本《Paxos Made Simple》。这篇论文摒弃了之前的隐喻,直接阐述算法核心原理,大大提高了可理解性。
行业影响与应用
自 Paxos 问世以来,它就在分布式一致性算法领域占据主导地位,"Paxos"这个名词几乎成为了分布式一致性的代名词。该算法在工业界得到了广泛应用:
-
Google 系统应用:
- Chubby:分布式锁服务
- Megastore:高可用性存储系统
- Spanner:全球分布式数据库
-
开源系统应用:
- ZooKeeper:分布式协调服务
- MySQL 5.7 的 Group Replication:取代传统主从复制的新机制
算法优化
上面的内容中,分别从 Proposer 和 Acceptor 对提案的生成和批准两个方面讲解了 Paxos 算法在提案选定过程中的算法细节,同时也在提案的编号全局唯一的前提下,获得了一个提案选定宣发,接下来我们再对这个初步算法做一个小优化,尽可能忽略Prepare请求。
如果Acceptor收到一个编号为N的Prepare请求,在此之前它已经响应过编号大于N的Prepare请求。根据P1a,该Acceptor不可能接受编号为N的提案。因此,该Acceptor可以忽略编号为N的Prepare请求。
通过这个优化,每个Acceptor只需要记住它已经批准的提案的最大编号以及它已经做出了Prepare请求响应的提案的最大编号,以便出现故障或节点重启的情况下,也能保证P2c的不变性,而对于Proposer来说,只要它可以保证不会产生具体相同编号的提案,那么就可以丢弃任意的提案以及它所有运行时状态信息。
算法描述
综合前面的讲解,我们来对Paxos算法的提案选定过程进行总结,结合 Proposer 和 Acceptor 对提案的处理逻辑,就可以得到类似的两阶段提交的算法执行过程
阶段一
● Proposer 选择一个提案编号N,然后向半数以上的 Acceptor 发送编号为N的Prepare请求
● 如果一个 Acceptor 收到一个编号为 N的Prepare请求,且N大于Acceptor已经响应过的所有Prepare请求的编号,那么它就会将它已经接受过的编号最大的提案(如果有的话)作为响应反馈给Proposer,同时该Acceptor承诺不再接受任何编号小于N的提案。
阶段二
● 如果Proposer收到半数以上Acceptor对其发出的编号为N的Prepare请求的响应,那么它就会发送一个针对【N,V】提案的 Acceptor请求给半数以上的 Acceptor。注意:V就是收到的响应中编号最大的法案的Value,如果响应中不包含任何提案,那么V就是由Proposer自己决定。
● 如果Acceptor收到一个针对编号为N的提案的 Acceptor请求,只要该Acceptor没有对编号大于N的Prepare请求做出过响应,它就接受该提案。
当然,实际运行过程中,每一个Proposer都可能产生多个提案,但只要每个Proposer都遵循如上所述的算法运行,就一定能够保证算法执行的正确性。
Learn 学习被选定的Value
方案一
Learner 获取了一个已经被选定的提案的前提是,该提案已经被半数以上的Acceptor批准,因此,最简单的做法就是一旦Acceptor批准了一个提案,就将该提案发送给所有的 Learner。
很显然,这种做法虽然可以让Learner尽快的获取被选定的提案,但是却需要让每个Acceptor与所有的Learner逐个进行一次通信,通信的次数至少为二者个数的乘积
方案二
另一种可行的方案是,我们可以让所有的Acceptor将他们对提案的批准情况,统一发送给一个特定的Learner(称为主 Learner),各个Learner之间可以通过消息通信来互相感知提案的选定情况,基于这样的前提,当主Learner被通知一个提案已经被选定时,它会负责通知其他Learner
在这种方案中,Acceptor 首先会将得到的批准的提案发送给主Learner,再由其同步给其他 Learner,因此较方案一而言,方案二虽然需要多一个步骤才能将提案通知到所有的Learner,但其通信次数却大大减少了,通常只是Acceptor和Learner的个数总和。但同时,该方案引入一个新的不稳定因素:主Learner随时可能出现故障。
方案三
在讲解方案二的时候,我们提到,方案二最大的问题在于主Learn存在单点问题,即主Learner随时可能出现故障,因此,对方案二进行改进,可以将主Learner的范围扩大,即Acceptor可以将批准的提案发送给一个特定的Learner集合,该集合每个Learner都可以在一个提案被选定后通知其他的Learner。这个Learner集合中的Learner个数越多,可靠性就越好,但同时网络通信的复杂程度也就越高。
如何保证Paxos算法的活性
活性问题的定义
在分布式系统中,活性(liveness)是指系统最终一定会达成某些期望结果的性质。对于Paxos算法而言,活性具体表现为:最终一定会有一个Value被选定(accepted)。这是Paxos算法正确性的重要保证之一。
活性失效的场景分析
假设存在一种极端情况,这种场景会导致Paxos算法无法保证活性:
-
提案竞争循环:有两个Proposer(P1和P2)持续交替提出一系列编号递增的提案
-
死锁形成:
- P1提出提案n1,获得多数派Acceptance承诺
- P2提出更高编号n2(n2>n1),导致P1的提案被废弃
- P1随后提出更高编号n3(n3>n2),导致P2的提案被废弃
- 这个过程不断重复,形成"提案编号竞赛"
-
具体流程示例:
- 阶段1:P1准备(prepare)提案n1,获得多数派承诺
- 阶段2:P1尝试接受(accept)提案n1,但此时P2已发出更高编号n2的准备请求
- 阶段3:P2的准备请求n2获得多数派承诺,导致n1被废弃
- 阶段4:P2尝试接受提案n2时,P1又发出更高编号n3的准备请求
- 这个循环会无限持续下去,没有Value最终被选定
活性问题的解决方案
为了解决这个活性问题,Paxos算法提出了以下几种保障机制:
-
选举主Proposer:
- 系统指定一个唯一的"主"Proposer
- 只有主Proposer可以提出提案
- 当主Proposer失效时,选举新的主Proposer
- 这样可以避免多个Proposer之间的竞争
-
随机退避机制:
- 当Proposer发现提案被拒绝时,随机等待一段时间再重试
- 等待时间随失败次数指数增长(类似以太网的退避算法)
- 这增加了单个Proposer成功完成提案的概率
-
提案编号限制:
- 设置提案编号的上限
- 当编号达到上限时强制选定当前最高编号的提案
- 避免无限递增的编号竞赛
-
超时机制:
- 为每个提案阶段设置合理的超时时间
- 超时后自动进入下一轮提案
- 防止单个提案无限期阻塞系统
在实际工程实现中,通常采用选举主Proposer的方案来解决活性问题,因为这种方法最可靠且易于实现。例如在Google的Chubby锁服务和很多分布式存储系统中都采用了这种方案。