破译一致性难题:Raft日志复制技术及成员变更问题详解

 一、日志复制

        Raft 算法是一种用于实现分布式系统中一致性状态机复制的共识算法。在 Raft 中,日志复制是保证集群数据一致性的关键机制。每个节点(服务器)都维护着一个日志,其中包含一系列的日志条目(Log Entry),每个条目包括一个索引值、任期号以及客户端提交的状态机命令。

        在 Raft 算法中,副本数据是以日志的形式存在的,领导者接收到来自客户端的写请求后,处理写请求的过程就是一个复制和应用(Apply)日志项到状态机的过程。

        那 Raft 日志是如何复制日志的呢?又如何实现日志一致的呢?这些内容是Raft中非常核心的内容。

        1.1 什么是 Raft 日志

        副本数据是以日志的形式存在的,日志是由日志项组成,日志项究竟是什么样子呢?

        其实,日志项是一种数据格式,它主要包含用户指定的数据,也就是指令(Commond),还包含一些附加信息,比如索引值(Log Index)、任期编号(Term)。

  • 指令:一条由客户端请求指定的、状态机需要执行的指令。可以将指令理解成客户端指定的数据。
  • 索引值:日志项对应的整数索引值,其实就是用来标识日志项的,是一个连续的、递增的整数号码。
  • 任期编号:创建这条日志项的领导者的任期编号。

        从图中可以看到,一届领导者任期,往往有多条日志项。而且日志项的索引值是连续的。

        1.2 日志复制

        你可以把Raft日志复制理解成一个优化后的二阶段提交(将二阶段优化成一阶段),减少了一半的往返消息,也就是降低了一半的消息延迟。那日志复制的具体过程是什么呢? 

        首先,领导者进入第一阶段,通过日志复制(AppendEntries)RPC 消息,将日志项复制到集群其他节点。

        接着,如果领导者接收到大多数”复制成功“的响应后,它将日志项应用到自己的状态机,并返回成功给客户端。如果领导者没有接收到大多数”复制成功“响应,那么就返回错误给客户端。

        领导者将日志项应用到它的状态机,怎么没通知跟随者应用日志项呢?

        这是 Raft 的一个优化,领导者不直接发送消息通知其他节点应用日志项。因为领导者的日志复制 RPC 消息或心跳消息,包含了当前最大的,将会被提交(Commit)日志项索引值。所以通过日志复制RPC消息或心跳消息,跟随者就可以知道领导者的日志提交位置信息。

        因此,当其他节点接收到领导者心跳信息,或者新的日志复制RPC消息后,就会将日志项应用到它的状态机。而这个优化,降低了处理客户端请求的延迟,将二阶段提交优化为一阶段提交,降低了一半的消息延迟。

       

  1. 接收到客户端请求后,领导者基于客户端请求中的指令,创建一个新日志项,并附加到本地日志中。
  2. 领导者通过日志复制 RPC,将新的日志复制到其他服务器。
  3. 当领导者将日志项,成功复制到大多数服务器上时,领导者会将日志项应用到它的状态机中。
  4. 领导者将执行结果返回给客户端。
  5. 当跟随者接收到心跳信息,或者新的日志复制 RPC 消息后,如果跟随者发现领导者已经提交了某条日志项,而它还没有,那跟随者就将这条日志项应用到本地状态机。

        不过,这是一个理想状态下的日志复制过程。在实际环境中,复制日志的时候,你可能会遇到进程崩溃、服务器宕机等问题,这些问题会导致日志不一致。那么在这种情况下,Raft 算法是如何处理不一致日志,实现日志的一致的呢?

        1.3 如何实现日志一致?

        在 Raft 算法中,领导者通过强制跟随者直接复制自己的日志项,处理不一致日志。也就是说,Raft 是通过以领导者的日志为准,来实现各节点日志一致的。具体有两个步骤:

  • 首先,领导者通过日志复制 RPC 的一致性检查,找到跟随节点上,与自己相同的日志项的最大索引值。也就是说,这个索引值之前的日志,领导者和跟随者是一致的,之后的日志是不一致的。
  • 然后,领导者强制跟随者更新覆盖不一致的日志项,实现日志的一致。

        看下详细流程

  • PrevLogEntry:表示当前要复制的日志项,前面一条日志项的索引值。比如在图中,如果领导者将索引值为 8 的日志项发送给跟随者,那么此时 PrevLogEntry 值为 7。
  • PrevLogTerm:表示当前要复制的日志项,前面一条日志项的任期编号,比如在图中,如果领导者将索引值为 8 的日志项发送给跟随者,那么此时 PrevLogTerm 值为 4

  1. 领导者通过日志复制 RPC 消息,发送当前最新日志项到跟随者(假设当前需要复制的日志项是最新的),这个消息的 PrevLogEntry 值为 7,PrevLogTerm 值为 4。
  2. 如果跟随者在它的日志中,找不到与 PrevLogEntry 值为 7、PrevLogTerm 值为 4 的日志项,也就是说它的日志和领导者的不一致了,那么跟随者就会拒绝接收新的日志项,并返回失败信息给领导者。
  3. 这时,领导者会递减要复制的日志项的索引值,并发送新的日志项到跟随者,这个消息的 PrevLogEntry 值为 6,PrevLogTerm 值为 3。
  4. 如果跟随者在它的日志中,找到了 PrevLogEntry 值为 6、PrevLogTerm 值为 3 的日志项,那么日志复制 RPC 返回成功,这样一来,领导者就知道在 PrevLogEntry 值为 6、PrevLogTerm 值为 3 的位置,跟随者的日志项与自己相同。
  5. 领导者通过日志复制 RPC,复制并更新覆盖该索引值之后的日志项(也就是不一致的日志项),最终实现了集群各节点日志的一致。

        从上面步骤中可以看到,领导者通过日志复制 RPC 一致性检查,找到跟随者节点上与自己相同日志项的最大索引值,然后复制并更新覆盖该索引值之后的日志项,实现了各节点日志的一致。需要你注意的是,跟随者中的不一致日志项会被领导者的日志覆盖,而且领导者从来不会覆盖或者删除自己的日志。

二、成员变更

        Raft 是共识算法,对集群成员进行变更时(比如增加 2 台服务器),会不会因为集群分裂,出现 2 个领导者呢?

        的确会出现这个问题,因为 Raft 的领导者选举,建立在“大多数”的基础之上,那么当成员变更时,集群成员发生了变化,就可能同时存在新旧配置的 2 个“大多数”,出现 2 个领导者,破坏了 Raft 集群的领导者唯一性,影响了集群的运行。

        而关于成员变更,不仅是 Raft 算法中比较难理解的一部分,非常重要,也是 Raft 算法中唯一被优化和改进的部分。比如,最初实现成员变更的是联合共识(Joint Consensus),但这个方法实现起来难,后来 Raft 的作者就提出了一种改进后的方法,单节点变更(single-server changes)。

        先介绍一下“配置”。配置是成员变更中一个非常重要的概念,可以这么理解:他就是再说集群是哪些节点组成的,是集群各节点地址信息的集合。比如节点 A、B、C 组成的集群,那么集群的配置就是[A、B、C]集合。

        假设我们有一个由节点A、B、C组成的Raft集群,现在我们需要增加副本数,增加两个副本(也就是增加两台服务器),扩展为由节点A、B、C、D、E,5个节点组成的新集群。

        那么Raft是如何保证集群配置信息变更时,集群能稳定运行,不出现两个领导者呢?

        2.1 成员变更问题

        在集群进行成员变更的最大风险是,可能会同时出现两个领导者。比如在进行成员变更时,节点 A、B 和 C 之间发生了分区错误,节点 A、B 组成旧配置中的”大多数“,也就是变更前的3节点集群中的”大多数“,那么这时的领导者节点(节点A)依旧是领导者。

        另一方面,节点 C 和新节点 D、E 组成新配置的”大多数“,也就是变更后的5节点集群中的”大多数“,他们会选举出新的领导者(比如节点C)。那么这时就同时出现了两个领导者的情况。

        如果出现了两个领导者,那么就违背了”领导者的唯一性“原则,进而影响到集群的稳定运行。如何解决这个问题呢?

        因为我们在启动集群时,配置是固定的,不存在成员变更,在这种情况下,Raft的领导者选举能保证只有一个领导者。也就是说,这时不会出现多个领导者的问题,那我可以先将集群关闭再启动新集群啊。也就是先把节点 A、B、C 组成的集群关闭,然后再启动节点 A、B、C、D、E 组成的新集群。

        这种方法不可行。因为每次变更都需要重启集群,意味着在集群变更期间服务不可用肯定不行的,太影响用户体验。

        2.2 单节点变更

        单节点变更,就是通过一次变更一个节点实现成员变更。如果需要变更多个节点,那需要执行多次单节点变更。

        比如将 3 节点集群扩容为 5 节点集群,这时你需要执行 2 次单节点变更,先将 3 节点集群变更为 4 节点集群,然后再将 4 节点集群变更为 5 节点集群,就像下图的样子。  

      

        我们看下如何用单节点变更的方法,解决这个问题。为了演示方便,我们假设节点 A 是领导者,目前的集群配置为[A, B, C],我们先向集群中加入节点 D,这意味着新配置为[A, B, C, D]。成员变更,是通过这么两步实现的:

  1. 第一步,领导者(节点A)向新节点(节点D)同步数据;
  2. 第二步,领导者(节点A)将新配置[A、B、C、D]作为一个日志项,复制到新配置中的所有节点(节点A、B、C、D)上,然后将新配置的日志项应用到本地状态机,完成单节点变更。

        在变更完成后,现在的集群配置就是[A, B, C, D],我们再向集群中加入节点 E,也就是说,新配置为[A, B, C, D, E]。成员变更的步骤和上面类似。

        这样一来,我们就通过一次变更一个节点的方式,完成了成员变更,保证了集群中始终只有一个领导者,而且集群也在稳定运行,持续提供服务。

        在正常情况下,不管旧的集群配置是怎么组成的,旧配置的“大多数”和新配置的“大多数”都会有一个节点是重叠的。 也就是说,不会同时存在旧配置和新配置 2 个“大多数”。

        关于Raft算法日志复制和成员变更就介绍到这里。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/410892.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

AI:139-基于深度学习的语音指令识别与执行

🚀点击这里跳转到本专栏,可查阅专栏顶置最新的指南宝典~ 🎉🎊🎉 你的技术旅程将在这里启航! 从基础到实践,深入学习。无论你是初学者还是经验丰富的老手,对于本专栏案例和项目实践都有参考学习意义。 ✨✨✨ 每一个案例都附带关键代码,详细讲解供大家学习,希望…

2024年开学季推荐:数码装备购物清单,校园生活必备神器

随着开学的钟声即将敲响,全新的学年画卷正在缓缓展开。它不仅承载着我们对知识的渴望和对未来的憧憬,更是我们挥洒青春、展示才华的舞台。在这个充满无限可能的新起点,每一位学子都怀着期待,准备踏上成长的征程。然而为了更好地适…

图解 Electron 进程模型

此前,已经介绍了《如何从 0 开始,创建一个 Electron 的 App》,每个人就有了一个梦开始的地方。如果想实现一个功能丰富的 App,了解一点基础知识,是非常必要的。比如,Electron 的进程模型。 一、简介 Chrome…

空指针和Void指针的基本概念和用法

前言:本文只是限于说明空指针与void指针的基本性质和用法,关于更深层次的用法,则不介绍,因为本人自己还没有搞懂!!! 1:空指针 1.1空指针的基本定义 定义:在C语言中,如…

APP自动化第一步:Appium环境搭建

一、安装Appium Python client包 1.直接cmd窗口输入pip install Appium-Python-Client 2.要确保安装匹配版本的selenium和appium 使用命令pip install selenium -U 首先进入网盘下载这三个软件的压缩包 二、安装Appium Server 1.双击打开压缩包Appium 2.双击进行安装。 3.点…

unity导航网格无法烘培到台阶和斜坡

如图是我在b站学Unity导航网格时建的一个示例场景,本场景使用的为棱长1m的立方体,读者可以以此为参照度量其他物体大小。 可见导航网格根本无法烘焙到斜坡和台阶上,为解决问题我做了不少尝试,调整最大坡度和步高都没办法解决问题…

Kafka 面试八股题整理

前言:本文是博主自行收集的Kafka相关的八股文问题,博主还在准备暑期实习中,应该会持续更新.... 参考: 32 道常见的 Kafka 面试题你都会吗?附答案 【Kafka】10道不得不会的 Kafka 面试题 掌握这10个常见的Kafka经典面试…

openssl3.2 - crypto-mdebug被弃用后, 内存泄漏检查的替代方法

文章目录 openssl3.2 - crypto-mdebug被弃用后, 内存泄漏检查的替代方法概述笔记查看特性列表openssl3.2编译脚本 - 加入enable-crypto-mdebug看看有没有替代内存诊断的方法?main.cppmy_openSSL_lib.hmy_openSSL_lib.c备注备注这招不行啊显势调用默认上下文也不行END openssl3…

【设计模式】工厂模式、建造者模式、原型设计模式

文章目录 1、简单工厂模式2、工厂方法模式3、抽象工厂模式4、建造者模式5、原型设计模式 设计模式即总结出来的一些最佳实现。23种设计模式可分为三大类: 创建型模式:隐藏了创建对象的过程,通过逻辑方法进行创建对象,而不是直接n…

【python开发】面向对象高级和应用

这里写目录标题 一、继承(一)mro和c3算法(二)py2和py3区别(了解即可) 二、内置函数补充(一)callable:是否可以在后面加括号执行(二)super()&#…

雷达一维成像:基于数据集的实践

雷达一维成像:基于数据集的实践 (距离压缩\距离-时间图\距离-多普勒图\微多普勒图) 说明 雷达成像技术是雷达发展的一个重要里程碑:从此雷达的功能不仅仅是将所观测的对象视为点目标,并只测量它的位置与运动参数。雷达成像技术使得我们可以获…

EMQX Enterprise 5.5 发布:新增 Elasticsearch 数据集成

EMQX Enterprise 5.5.0 版本已正式发布! 在这个版本中,我们引入了一系列新的功能和改进,包括对 Elasticsearch 的集成、Apache IoTDB 和 OpenTSDB 数据集成优化、授权缓存支持排除主题等功能。此外,新版本还进行了多项改进以及 B…

Linux第63步_为新创建的虚拟机添加必要的目录和安装支持linux系统移植的软件

1、创建必要的目录 输入密码“123456”,登录虚拟机 这个“zgq”,是用户名,也是下面用到的的“zgq”目录。 1)、创建“/home/zgq/linux/”目录 打开终端,进入“/home/zgq/”目录 输入“mkdir linux回车”,创建“/ho…

电子版证件照怎么弄?分享完整制作方法!

在数字化时代,电子版证件照已成为我们生活中不可或缺的一部分。无论是求职、办理证件还是网络注册,都需要用到电子版证件照。那么,如何制作一份合格的电子版证件照呢?本文将为您详细介绍电子版证件照的制作方法,并推荐…

开年大吉!安全狗入选工信部工业互联网试点示范名单

近日,工业和信息化部信息通信管理局公布了2023年工业互联网试点示范名单。此次名单根据《工业和信息化部办公厅关于组织开展2023年工业互联网试点示范项目申报工作的通知》(工信厅信管函﹝2023﹞319号),经企业申报、地方推荐、专家…

通过OCR实现纯数字识别

基于飞浆paddle训练框架 照这个改的 https://www.paddlepaddle.org.cn/documentation/docs/zh/practices/cv/image_ocr.html 训练不到10分钟 10epoch cpu:inter i5 8250 U 脚本生成的图10000 验证训练:3:7 预测结果 chatgpt写的代码,生成数…

从ChatGPT到Sora,来了解大模型训练中的存储

1 从chatGPT到Sora 2022年底,OpenAI推出人工智能聊天机器人ChatGPT,开启了大模型领域的“竞速跑”模式。2024年2月15日,随着视频生成模型Sora的横空出世,OpenAI再度掀起热潮。 Sora将视频生成内容拉到了一个全新的高度&#xff0c…

Pybind11 在C++中运行python脚本操作内存数据

pybind11资料 官方Github:Pybind11 Github Pybind11文档:Pybind11 文档 文档在深入使用后需要细细读懂,包括全局只能有一个解释器,如何从C中返回指针/引用等。基本文档中需要注意的点都会遇到 Python环境安装及维护 对于正常使用人员&…

python自动化测试三部曲之request+django实现接口测试

这里废话少说,进入正题 我的思路是这样的 1、先用django实现登陆、增加、删除、查看4个接口 2、在excel定义好测试案例、然后读取excel中的案例,然后把案例用unittest框架组装和封装 3、启动django,执行测试案例 一、先跑通unittest到dj…

Scikit-Learn逻辑回归

Scikit-Learn逻辑回归 1、逻辑回归概述1.1、逻辑回归1.2、逻辑回归的优缺点1.3、逻辑回归与线性回归 2、逻辑回归的原理2.1、逻辑回归的概念与原理2.2、逻辑回归的损失函数 3、 1、逻辑回归概述 1.1、逻辑回归 逻辑回归(Logistic Regression)主要解决二…