Paxos 学习笔记2 - Multi-Paxos

图片来自 John Ousterhout 的 Raft user study 系列课程

Multi-Paxos 论文里对很多问题并没有描述清楚,所以实践里往往用的是修改过的 Multi-Paxos 协议或 Raft 协议

用 basic paxos 实现 replicated log 的简单思路

实现 replicated log 的最简单思路就是对每个日志项运行一个 basic paxos

  • 在 basic paxos 请求的参数里加一个 index 参数

当提议者收到客户端发来的一条指令,他会尝试使用下一个没有被选中的日志项位置

比如 S1 从客户端收到指令 jmp 后

  • 会先尝试日志项 3,发现已经写入了 cmp(但还没有被选中),于是走 basic paxos 的两阶段协议提议在日志项 3 中放置 cmp
  • 等到 cmp 被选中后,发现日志项 4 是空的,于是提议将在日志项 4 中放置 jmp ,最终达成共识的不是 jmp(提议之间产生了冲突),于是 S1 就会去尝试下一个日志项,直到给 jmp 找到一个可以达成共识的位置

在这种方案中服务器可以并发地处理多个客户端请求,只要为他们选择不同的日志项,并独立地执行 basic paxos 流程即可

如果一条指令和它之前的全部指令都已经被选中了,那么就可以交给状态机去执行了

可能的性能优化

  • 使用领导人来避免提议者之间的冲突

    • basic paxos 的活锁问题会在高负载的 replicated log 场景下变得更加严峻
  • 消除多余的 Prepare 请求

尚未解决的问题

  • 确保完全复制

    • basic paxos 中共识的结果只有提议者自己知道,但 replicated log 场景中每个服务器都应该拥有一份已选中日志的拷贝
  • 客户端协议
    • 客户端如何与 paxos 集群交互
  • 配置改变
    • 服务器加入或离开时,算法应该做怎样的改变

领导人选举

Lamport 提出的选举方法

  • 让 ID 最大的服务器作为领导人
  • 所有服务器每隔 T ms 就发其他所有服务器发送心跳报文
  • 如果一个服务器在过去的 2T ms 里都没有收到其他比他 ID 更大的服务器发来的心跳报文,他就作为领导人开始工作
    • 接受客户端请求
    • 同时作为提议者和接受者
  • 如果服务器不是领导人
    • 拒绝客户端请求
    • 只作为接受者

Chubby 中是通过运行 basic paxos 投票选举选出领导人,在运行过程中,主节点会通过不断续租的方式来延长租期(Lease)

消除 Prepare 阶段

回忆 Basic Paxos 中 Prepare 阶段的用途

  • 阻止那些更老的还未完成的提议被选中
  • 看看有没有已经被选中的值,如果有,提议者的提议就应该使用这个值

消除 Prepare 的设计:

  • Prepare 请求中对整个日志使用一个提议号,而不是针对每个日志项用一个独立的提议号
  • 接受者回复 Prepare 请求时
    • 回复当前日志项的最大已接收提议

      • acceptedProposal[index]acceptedValue[index]
    • 查看 index 之后的所有日志项,如果没有已接受的日志项,就在回复里设置 noMoreAccepted

noMoreAccepted:

  • 如果提议者收到了某个接受者发送的 noMoreAccepted

    • 就可以省略发给该接受者的任何 Prepare 报文
    • 直到发给该接受者的某个 Accept 报文被拒绝
  • 如果提议者收到了多数接受者发送的 noMoreAccepted
    • 就不需要发送任何 Prepare 报文了,直接发送 Accept 报文即可

减少算法中非必要的协商步骤,优化系统性能

完全拷贝

使用上面的方法来实现 replicated log,当提议者发现某个日志项被选中后,并没有一种机制能保证该日志项能拷贝给其他节点,由于我们使用了领导人,因此接受者是没有任何主动的手段去获得已选中日志项的内容的,因此我们引入了下面的机制来保证日志项被完全地拷贝

机制 1

领导人收到多数 Accept 回复后

  • 在后台继续重试发送 Accept 报文,直到所有接受者都回复
  • 不能保证完全拷贝,比如重试过程中领导人宕机了,或者服务器故障期间达成共识的日志项,就没法以这种方式同步过来

机制 2

所有节点都必须将已经被选中的日志项记录下来

  • 通过将 acceptedProposal[i] 设置为 ∞

    • 表示该日志项已经被选中了
    • 因为只有提议编号大于等于 acceptedProposal 的情况下,该日志项才会被覆盖,如果是无穷大,该日志项就再也不能被覆盖了
  • 维护一个 firstUnchosenIndex
    • 表示最早的没有被选中的日志项下标
    • 即第一个 acceptedProposal[i] != ∞ 的日志项

机制 3

领导人主动告知接受者哪些日志项被选中了

  • 在 Accept 请求中包含 firstUnchosenIndex,也就是告诉接受者小于 firstUnchosenIndex 的日志项都已经被选中了
  • 如果某个日志项 i 满足
    • i < request.firstUnchosenIndex
    • acceptedProposal[i] == request.proposal
      • 提议号相同说明,日志项中的内容和 Accept 请求来自同一个提议者,firstUnchosenIndex 又告诉我们这条日志项已经被选中了,说明这条日志项里记录的正好就是被选中的日志内容
  • 将满足上述条件的所有日志项标记为已选中

上图中,日志项 6 的下标小于 firstUnchosenIndex,且它的 acceptedProposal 等于请求的提议编号,因此可以将它标记为已选中

机制 4

通过上面的三种机制,并不能解决日志拷贝中的全部问题,比如上图的日志项 4,他的 acceptedProposal 和 Accept 请求的提议号是不同的,说明这条记录来自于其他提议者或来自于同一个提议者在较老的提议轮中提出的提议,这种情况下我们是无法判断这条日志项中的内容正确与否的,这种情况往往发生在领导人切换时

为了处理上一任领导人留下来的日志项

  • 接受者在 Accept 回复中带上他的 firstUnchosenIndex
  • 如果领导人的 firstUnchosenIndex 大于回复里的 firstUnchosenIndex,说明存在这样的不同步的日志项,这时,领导人会发送 Success RPC(在后台)

Success RPC 的格式为 Success (index, v),也就是说在 index 的位置上,有已经被选中的提议值 v,收到 Success 报文后,接受者应该

  • acceptedValue[index] = v
  • acceptedProposal[index] = ∞
  • 回复给提议者新的 firstUnchosenIndex
  • 如果需要,领导人会继续发送 Success RPC,直到和接受者达成同步

通常情况下,只有领导人切换时才需要执行这一步

客户端协议

客户端与 Paxos 集群交互的方式

  • 如果客户端不知道领导人是哪台机器

    • 随便找一台服务器发送请求
    • 如果接受请求的服务器不是领导人,他就会将消息转发给领导人
  • 只有请求被记录到日志中,并提交给领导人的状态机执行结束后,领导人才会给客户端回复
  • 如果请求超时(比如领导人崩溃的情况下)
    • 客户端将请求发送给其他服务器
    • 请求最终会被转发给新的领导人
    • 新的领导人重试这条请求

这样做的问题在于如果领导人崩溃前,已经执行了一次客户端请求,只不过还没来得及回复给客户端,这样同一条客户端请求就被执行了两次

解决方法是客户端在请求上带上唯一 id,服务器记录已经执行过了哪些指令,状态机执行新的指令前先检查是否已经执行过了,如果执行过了就直接将结果回复给客户端

只要客户端不崩溃,上述方法就可以保证 exactly-once 语义

更改配置

分布式系统中系统配置指的是

  • 每台服务器的 ID、地址
  • 多少服务器能构成 quorum 中的多数

共识机制必须支持对系统配置的更改,比如移除掉故障的机器

安全性要求

配置更改期间,不能出现集群中不同的部分针对某个日志项选中了不同的值

  • 比如图中前两个节点还在使用旧的配置,他们认为自己构成了多数节点,选择了 v1
  • 而后三个节点已经切换到了新的配置,他们也认为自己构成了多数节点,选择了另一个值 v2

Paxos中配置更改的方式

使用日志项来管理配置的更改

  • 配置也被存储为日志项
  • 也像其他日志项一样在服务器间拷贝
  • 日志项生效有延迟,放到日志项 i 中的配置,要在选择日志项 i+α 时才能生效

  • α 限制了 paxos 的并发度,只有选中了日志项 i 后,我们才可以开始选择日志项 i+α
  • 如果想要配置更改快点生效,可以发出多条空指令

Paxos 学习笔记2 - Multi-Paxos的更多相关文章

  1. paxos ---学习笔记

    摘自维基百科:分布式系统中的节点通信存在两种模型:共享内存(Shared memory)和消息传递(Messages passing).基于消息传递通信模型的分布式系统,不可避免的会发生以下错误:进程 ...

  2. python学习笔记4-redis multi watch实现锁库存

    python 关于redis的基本操作网上已经很多了,这里主要介绍点个人觉得有意思的内容1.redis的事务操作以及watch 乐观锁:后面描述2.tornado下异步使用redis的方式       ...

  3. Multi Paxos

    Multi Paxos [2] 通过basic paxos 以上步骤分布式系统已经能确定一个值,“只确定一个值有什么用?这可解决不了我面临的问题.” 你心中可能有这样的疑问. 原simple paxo ...

  4. SRE学习笔记:分布式共识系统、Paxos协议

    最近阅读了<SRE Google运维解密>的第23章,有一些感触,记录一下. 日常工作中,我们经常需要一些服务分布式的运行.跨区域如跨城.跨洲部署运行分布式系统往往是容易的,但是如何保证各 ...

  5. 从 Basic Paxos 到 Multi Paxos 到 Raft

    在朴素Paxos算法中, 各个节点经过 Prepare 和 Accept 阶段, 会达成一个值, 这个值一旦达成, 就不能被修改, 如下例子: 图示1 上面的操作几乎没有任何实用价值, 于是演变成下面 ...

  6. Paxos协议笔记

    对Paxos协议的介绍,可以通过Leslie Lamport的<Paxos Made Simple>展开学习和了解.Paxos算法在允许失败的分布式系统环境下,实现系统一致性.失败的情况有 ...

  7. 经典Paxos算法笔记

    介绍 Paxos算法是一个高容错性的分布式一致性算法.去年学习过Paxos算法,一直没将整理到博客.现在将经典Paxos算法相关内容整理到博客上. 经典Paxos算法本身也并不是太难理解,Lampor ...

  8. Paxos 学习总结

    近期学习了分布式领域的重要算法Paxos,这里罗列下关键点当作总结.自己水平有限,难免存在谬误,恳请读者指正.本篇不包含Paxos的基本理论介绍.Paxos基础能够參考以下的学习资料章节. 1 Pax ...

  9. 学习笔记:The Log(我所读过的最好的一篇分布式技术文章)

    前言 这是一篇学习笔记. 学习的材料来自Jay Kreps的一篇讲Log的博文. 原文很长,但是我坚持看完了,收获颇多,也深深为Jay哥的技术能力.架构能力和对于分布式系统的理解之深刻所折服.同时也因 ...

随机推荐

  1. Easticsearch概述(API使用)二

    Rest简介 一种软件架构风格,而不是标准,只是提供了一组设计原则和约束条件.它主要用于客户端和服务端互类的软件.基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制 Rest的操作分为以 ...

  2. day4 对偶数、偶数位的操作

    1.函数fun()的功能:从低位开始取出整形变量s中偶数位上的数,依次构成一个新数放在t中.高位仍在高位. 效果理想:但是经测试的时候出现了错误 输入987654321时,打印出来的却是18681.经 ...

  3. 《剑指offer》面试题16. 数值的整数次方

    问题描述 实现函数double Power(double base, int exponent),求base的exponent次方.不得使用库函数,同时不需要考虑大数问题. 示例 1: 输入: 2.0 ...

  4. Go环境配置和GoModule

    Linux相关 Linux常用操作 mkdir directory --创建文件夹 vi file --创建文件,再关闭vim rm file --删除文件 rm -rf directory --递归 ...

  5. 云计算——实验一 HDFS与MAPREDUCE操作

    1.虚拟机集群搭建部署hadoop 利用VMware.centOS-7.Xshell(secureCrt)等软件搭建集群部署hadoop 远程连接工具使用Xshell: HDFS文件操作 2.1 HD ...

  6. python技巧一行命令搞定局域网共享

    python超强玩法--一行命令搞定局域网共享 ​ 今天刷到python的一个新玩法,利用python自带的http服务,快速创建局域网共享服务,命令如下: python -m thhp.server ...

  7. elasticsearch之请求处理流程(Rest/RPC )

    .Action概述 ES提供client供集群节点或java客户端访问集群用.client模块通过代理模式,将所有的操作都集成到client接口中.这样外部调用只需要初始化client就能够完成所有的 ...

  8. golang中通过递归或通道实现斐波那契数列

    1. 循环实现 package main import "fmt" func fibonacciFor(nums int) (s1 []int) { // 循环实现斐波那切数列 n ...

  9. Python实现自动更改系统用户密码,生成随机密码

    算是一个实用的例子,定制系统任务,并将随机密码上传至日志服务器,实现定期修改密码: 部分代码: 1 #!/usr/bin/env python 2 #coding:utf-8 3 import ran ...

  10. log4j学习记录以及相关配置(精简版)

    使用log4j时关键配置 log4j的maven依赖 <dependency> <groupId>log4j</groupId> <artifactId> ...