[etcd]raft总结/选举/数据同步,协议缺陷与解决/Multi Raft

2024-08-24 05:18

本文主要是介绍[etcd]raft总结/选举/数据同步,协议缺陷与解决/Multi Raft,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

raft协议是multi paxos协议的实现.Etcd、Consu都使用了raft

1.角色

raft协议中包含这几种角色

领导者:带头大哥1.提出提议,但是不需要确认,因为我是大哥;2.复制日志,数据以大哥为准,3,领导者会定时发送心跳,确定自己的位置.告诉小弟老实呆着,一旦心跳超时,小弟就会重新选举大哥.

跟随者:只要大哥发送心跳,我就老实的同步日志.一旦没有心跳,我就变成候选人,开始发起投票,争取做大哥

候选人:心跳超时后,变成候选人,这是个中间状态.

2.选举

2.1选举时机:

1.心跳超时后,变成候选人,开始进行选举.为了避免节点全都一起选举,不同节点会有随机心跳超时时间.

2.例如机器刚启动时,大家都是follower,心跳超时后,就开始选举

3.优化.为了避免瓜分选票,心跳超时时间随机,当选举超时后,会进入第二轮选举,这个间隔时间也是随机的

2.2 选举原则:

1.每个节点,对每个任期(trem)只能投出一张票.与zab不同,zab是不断修改自己的选票,然后每个节点都有个投票箱,自己计算

2.收到选举请求后,如果该请求的事务日志编号小于自己的机器,那么就不会投票. 要当leader的机器,最起码掌握的东西得比我多吧(log)

3.收到选举请求后,如果该选举编号(term)小于自己的term,那么就不会投票. 我们都在老xi时代了,你还在选举第二任?

4.当一台机器收到多数投票,代表选举成功.

 

注意一个点.当集群中一个节点出现网络分区.那么他会去选举,增加自己的term.但是还是会超时,因此一直去增加term,发起更多轮的选举.一旦.这个节点网络恢复.那么其他节点就会收到选举请求,发下term大于自己,那么会触发重新选举. 由于事务id小,当然不会成为leader,但是还是影响了集群.

因此.有一个preVote阶段,

在PreVote算法中,Candidate首先要确认自己能赢得集群中大多数节点的投票,这样才会把自己的term增加,然后发起真正的投票。其他投票节点同意发起选举的条件是(同时满足下面两个条件):

  • 没有收到有效领导的心跳,至少有一次选举超时。
  • Candidate的日志足够新(Term更大,或者Term相同raft index更大)。

 

 

2.3 rpc消息:

raft中有两个rpc:

1.选举rpc:是由候选人在选举期间发起,通知各节点进行投票

2.日志复制rpc:是由领导者发起,用来复制日志和提供心跳消息.

 

2.4 term

1.如果一个机器收到,任期编号大的机器发来的心跳,那么他就应该知道自己已经out了.要赶紧同步数据.化为follower.

2.当节点收到任期编号比我小的怎么办?丢弃.

 

2.5 总结:

与multiPaxos相比

1.通过日志编号确定数据是否最新,因此要求数据是连续的

当确认leader之后,leader会同步数据给follower.

3.日志复制:

流程

正常情况:

  1. client向leader发出一个添加请求
  2. leader写入appendEntryLog.并标记为uncommit状态.
  3. 调用appendEntryRpc,拷贝日志项到follower.
  4. follower接收到日志项,并append到本机.返回给leader success
  5. leader收到success后,将日志置为committed状态.并apply到状态机.返回给client success.

异常流程:

当拷贝到follower,follower没有响应成功时,有可能是没有拷贝过去,也有可能ack丢失了.  leader会不断重试,直到满足过半.  当然这时候,client可能会超时.这条日志可能最后会丢失,也可能会committed,客户端需要实现幂等

 

先说问题:

1.leader把数据复制到follower,follower大部分返回成功后,leader会apply此条数据到状态机.并返回给client成功的响应.

注意这里:状态机是对于上层来说的,比如你写成功后,再来读取数据,就是从状态机的数据中读,如果你返回给用户成功,但是状态机数据还是旧的,那么就不对了.

那我能不能在leader复制后,就update状态机呢?

不能,如果直接update,返回成功给用户,但是此时leader挂了.咋整.所以需要等大部分都提交,才返回成功.因为大部分提交后,即使此时leader挂了.重新选举时,根据raft协议.只有提议id大的才可以当leader,

就可以保证,之后当选的leader会有这条数据.因此这里也可以知道.选举时比较的日志id,是复制的日志id,而不是commitedId.

2.raft将两阶段提交,优化为1阶段提交. 

当一条请求发送给leader时,leader会写一条日志(uncommit),之后复制给follower(uncommit),当过半后,leader会变为committed,那么follower啥时候变为committed,并apply到状态机呢???

如果不是实时的,岂不是在读取的时候,读不出来??

a:通过AppendEntriesRPC同步数据,细节前面已经说了.此时就会把数据应用到状态机.大概逻辑就是:心跳/同步数据 rpc,会携带最新日志id,如果少的话,那么需要进行同步.appendEntryRpc互携带最新日志以及前一条日志(preEntryTerm,preEntryLogId),就可以回溯到 到底从哪条日志不一样了,然后再同步给follower.

b:注意:一致性是说 对于client,发出的读请求,可以获取到集群最新的数据,而不是说,集群每个节点数据都是一致的!!.这个是很容易错误理解的!! 例如etcd,早期是通过读写都走leader节点来保证一致性.3.2版本后,通过readIndex来保证.readIndex是什么,可以参考etcd源码分析系列;

所以一般来讲, leader append log(uncommit)--->copy to followers-->leader apply log &commit the log--->下次心跳,同步数据

4.raft算法的缺陷

我们可以在网上看到很多比较 Paxos 和 Raft 的文章,它们都会提到在复制效率上 Raft 会差一些,主要原因就是 Raft 必须“顺序投票”,不允许日志中出现空洞。

  1. Leader 收到客户端的请求。
  2. Leader 将请求内容(即 Log Entry)追加(Append)到本地的 Log。
  3. Leader 将 Log Entry 发送给其他的 Follower。
  4. Leader 等待 Follower 的结果,如果大多数节点提交了这个 Log,那么这个 Log Entry 就是 Committed Entry,Leader 就可以将它应用(Apply)到本地的状态机。
  5. Leader 返回客户端提交成功。
  6. Leader 继续处理下一次请求。

以上是单个事务的运行情况。那么,当多事务并行操作时,又是什么样子的呢?我画了张图来演示这个过程。我们设定这个 Raft 组由 5 个节点组成,T1 到 T5 是先后发生的 5 个事务操作,被发送到这个 Raft 组。

事务 T1 的操作是将 X 置为 1,5 个节点都 Append 成功,Leader 节点 Apply 到本地状态机,并返回客户端提交成功。

事务 T2 执行时,虽然有一个 Follower 没有响应,但仍然得到了大多数节点的成功响应,所以也返回客户端提交成功。

现在,轮到 T3 事务执行,没有得到超过半数的响应,这时 Leader 必须等待一个明确的失败信号,比如通讯超时,才能结束这次操作。因为有顺序投票的规则,T3 会阻塞后续事务的进行。

T4 事务被阻塞是合理的,因为它和 T3 操作的是同一个数据项,但是 T5 要操作的数据项与 T3 无关,也被阻塞,显然这不是最优的并发控制策略。

同样的情况也会发生在 Follower 节点上,第一个 Follower 节点可能由于网络原因没有收到 T2 事务的日志,即使它先收到 T3 的日志,也不会执行 Append 操作,因为这样会使日志出现空洞。

Raft 的顺序投票是一种设计上的权衡,虽然性能有些影响,但是节点间日志比对会非常简单。在两个节点上,只要找到一条日志是一致的,那么在这条日志之前的所有日志就都是一致的。这使得选举出的 Leader 与 Follower 同步数据非常便捷,开放 Follower 读操作也更加容易。要知道,我说的可是保证一致性的 Follower 读操作,它可以有效分流读操作的访问压力。

性能优化方法

缺点:顺序投票,只能串行apply,因此高并发场景下性能差。

TiDB:

1.批操作(Batch)。Leader 缓存多个客户端请求,然后将这一批日志批量发送给 Follower。Batch 的好处是减少的通讯成本。牺牲了写操作的耗时

2.流水线(Pipeline)。Leader 本地增加一个变量(称为 NextIndex),每次发送一个 Batch 后,更新 NextIndex 记录下一个 Batch 的位置,然后不等待 Follower 返回,马上发送下一个 Batch。如果网络出现问题,Leader 重新调整 NextIndex,再次发送 Batch。当然,这个优化策略的前提是网络基本稳定。

3.并行追加日志(Append Log Parallelly)。Leader 将 Batch 发送给 Follower 的同时,并发执行本地的 Append 操作。因为 Append 是磁盘操作,开销相对较大,而标准流程中 Follower 与 Leader 的 Append 是先后执行的,当然耗时更长。改为并行就可以减少部分开销。当然,这时 Committed Entry 的判断规则也要调整。在并行操作下,即使 Leader 没有 Append 成功,只要有半数以上的 Follower 节点 Append 成功,那就依然可以视为一个 Committed Entry,Entry 可以被 Apply。

4.异步应用日志(Asynchronous Apply)。Apply 并不是提交成功的必要条件,任何处于 Committed 状态的 Log Entry 都确保是不会丢失的。Apply 仅仅是为了保证状态能够在下次被正确地读取到,但多数情况下,提交的数据不会马上就被读取。因此,Apply 是可以转为异步执行的,同时读操作配合改造. 这里是牺牲了读取的一致性了

其实,Raft 算法的这四项优化并不是 TiDB 独有的,CockroachDB 和一些 Raft 库也做了类似的优化。比如,SOFA-JRaft 也实现了 Batch 和 Pipeline 优化。

     

参考:

https://zhuanlan.zhihu.com/p/27207160

https://raft.github.io/raft.pdf

https://time.geekbang.org/column/article/277028

http://blog.sina.com.cn/s/blog_3fc85e260102wr0c.html

 

 

 

 

 

 

 

 

这篇关于[etcd]raft总结/选举/数据同步,协议缺陷与解决/Multi Raft的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1101557

相关文章

一文教你Python如何快速精准抓取网页数据

《一文教你Python如何快速精准抓取网页数据》这篇文章主要为大家详细介绍了如何利用Python实现快速精准抓取网页数据,文中的示例代码简洁易懂,具有一定的借鉴价值,有需要的小伙伴可以了解下... 目录1. 准备工作2. 基础爬虫实现3. 高级功能扩展3.1 抓取文章详情3.2 保存数据到文件4. 完整示例

解决IDEA报错:编码GBK的不可映射字符问题

《解决IDEA报错:编码GBK的不可映射字符问题》:本文主要介绍解决IDEA报错:编码GBK的不可映射字符问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录IDEA报错:编码GBK的不可映射字符终端软件问题描述原因分析解决方案方法1:将命令改为方法2:右下jav

使用Java将各种数据写入Excel表格的操作示例

《使用Java将各种数据写入Excel表格的操作示例》在数据处理与管理领域,Excel凭借其强大的功能和广泛的应用,成为了数据存储与展示的重要工具,在Java开发过程中,常常需要将不同类型的数据,本文... 目录前言安装免费Java库1. 写入文本、或数值到 Excel单元格2. 写入数组到 Excel表格

MyBatis模糊查询报错:ParserException: not supported.pos 问题解决

《MyBatis模糊查询报错:ParserException:notsupported.pos问题解决》本文主要介绍了MyBatis模糊查询报错:ParserException:notsuppo... 目录问题描述问题根源错误SQL解析逻辑深层原因分析三种解决方案方案一:使用CONCAT函数(推荐)方案二:

python处理带有时区的日期和时间数据

《python处理带有时区的日期和时间数据》这篇文章主要为大家详细介绍了如何在Python中使用pytz库处理时区信息,包括获取当前UTC时间,转换为特定时区等,有需要的小伙伴可以参考一下... 目录时区基本信息python datetime使用timezonepandas处理时区数据知识延展时区基本信息

Qt实现网络数据解析的方法总结

《Qt实现网络数据解析的方法总结》在Qt中解析网络数据通常涉及接收原始字节流,并将其转换为有意义的应用层数据,这篇文章为大家介绍了详细步骤和示例,感兴趣的小伙伴可以了解下... 目录1. 网络数据接收2. 缓冲区管理(处理粘包/拆包)3. 常见数据格式解析3.1 jsON解析3.2 XML解析3.3 自定义

SpringMVC 通过ajax 前后端数据交互的实现方法

《SpringMVC通过ajax前后端数据交互的实现方法》:本文主要介绍SpringMVC通过ajax前后端数据交互的实现方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价... 在前端的开发过程中,经常在html页面通过AJAX进行前后端数据的交互,SpringMVC的controll

IntelliJ IDEA 中配置 Spring MVC 环境的详细步骤及问题解决

《IntelliJIDEA中配置SpringMVC环境的详细步骤及问题解决》:本文主要介绍IntelliJIDEA中配置SpringMVC环境的详细步骤及问题解决,本文分步骤结合实例给大... 目录步骤 1:创建 Maven Web 项目步骤 2:添加 Spring MVC 依赖1、保存后执行2、将新的依赖

Spring 中的循环引用问题解决方法

《Spring中的循环引用问题解决方法》:本文主要介绍Spring中的循环引用问题解决方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录什么是循环引用?循环依赖三级缓存解决循环依赖二级缓存三级缓存本章来聊聊Spring 中的循环引用问题该如何解决。这里聊

Pandas统计每行数据中的空值的方法示例

《Pandas统计每行数据中的空值的方法示例》处理缺失数据(NaN值)是一个非常常见的问题,本文主要介绍了Pandas统计每行数据中的空值的方法示例,具有一定的参考价值,感兴趣的可以了解一下... 目录什么是空值?为什么要统计空值?准备工作创建示例数据统计每行空值数量进一步分析www.chinasem.cn处