raft--分布式一致性协议
0. 写在前面的话
一直从事分布式对象存储工作,在分布式对象存储的运营,开发等工作中,数据一致性是至关重要的。因此想写一篇关于分布式一致性的文章。一来,可以和大家分享。二来,可以提高自己的文字提炼能力也可以当作备忘。
本篇文章并不是raft的一篇科普文,不着重介绍raft的具体过程,这些具体过程raft论文中都详细阐述,在此不再赘述,而是着重于raft中选举以及日志复制过程如何保证数据的一致性的阐述。
#如果对raft不了解的同学,建议搜索raft论文译文+原文对照看
#分享的都是个人体会和思考的过程可能不是很严谨
#不求全,可能有些细节会被省略,但求能够把raft最主要的东西说清楚
1. 什么是分布式一致性协议
#分布式:多个节点互为副本,副本之间可以是平等关系也可以是主从关系
#一致性:保证各个节点上的数据,只要是提交的数据一定是保证一致的
2. 为什么需要分布式一致性协议
在分布式对象存储系统中,数据的安全性是通过多份冗余副本的保证的,这就要求系统能够保证多份副本上的数据一致,比如对于对象存储系统的master管理着整个集群的副本关系,block状态等元数据信息。为了防止master单点,一般会采取一主多从的方式,而在存储系统中由于故障导致的数据迁移,空间回收等场景都会导致元数据发生改变,这些元数据的改变如何同步到其他备机上对提升整个分布式的数据安全性有着至关重要的作用。
3. raft是如何做到保证数据一致性
raft作为一种比较好理解的分布式一致性算法(相对于paxos来说啦,其实要理解还是有点难度的!),是通过选举机制,日志复制来保证系统数据的一致性
笔者认为,raft协议理解的难度并不在于raft定义的那些操作,而是在于raft定义的操作和raft系统遵守的原则之间的关系,以及为什么系统遵循这些原则就能保证数据的一致性呢?所以刚开始了解raft协议时给人的感觉就是比较散乱而不清晰。
因此下面将会着重试着从raft操作是如何保证系统始终遵守这些原则之间,以及这些原则为什么能保证数据的一致性来阐述
#raft基本概念:
提交的日志索引(commitIdx):已经同步至大部分节点的log的下标,表明数据是安全状态,是不会再被修改的日志数据。
任期(term):相当于系统的逻辑时钟,每一个任期中只能有一个leader,可以理解为系统每进入一轮选举时任期+1。可以把任期想象成一个朝代,一朝天子一朝臣,leader就是这朝的皇帝,从节点就是这朝的臣子。term是理解raft的一个关键点。
日志索引(logIdx):raft日志索引是一个的下标连续&单调递增的,例如(1,2,3,4,5,..,n,n+1)。
状态机执行日志索引(applyIdx):发送给状态机(是个抽象的概念可以是对象存储系统,也可以是其他的系统)执行的日志下标。
#raft基本操作
#选举:
功能描述:
请求其他节点为自己竞选leader投票,超过半数节点投票给自己就会转化成leader节点
发起方:
角色:候选者
操作名称:RequestVote
操作参数:
#当前最新的日志条目索引(当前日志的term+logIdx)【规则b会用到】
#当前的term号 【规则a会用到】
#自身的ID,用于告诉选民我是谁
响应方:
角色:所有
响应规则:
a.请求方term大于自身的term。(小于的话,就好比生在新中国的我们来了个古代人来参选主席,你说你会答应吗?)
b.请求方的日志条目包含自身的日志条目。(通过笔记两方的日志条目索引来判断,后面会介绍为什么这种判断可以保证)。对方知道的东西还不如你多,说要当你领导你干吗?)
c.在同一个任期内只能投一次票,多个竞选者按照先来先得的投票原则。
在上面a,b,c条件都满足的情况下,会投出神圣的一票给对方,否则不投。
#日志同步:
发起方:
功能描述:
#leader把客户端写到leader的日志的条目复制给从节点
· #在leader上任时会进行一次主从数据的同步(可以理解为当权者掌权后清除异己,主把从上和自己不同的记录都给删掉,直到保持一致!)
#充当心跳报文,维持leader的存在,抑制从节点进入竞选。(leader刷存在感的方式)
发起方:
角色:leader
操作名称:appendEntrtries
操作参数:
#当前任期:term
#entries[]:日志数据数组,记录将要复制给从节点的日志条目
#prevLogIndex / prevLogTerm:entries[0]日志的前一个日志对应的logIdx / prevLogIndex对应日志条目的任期号(很多raft介绍文章是:最新日志之前的日志的索引值,但是本人参考raft原文以及逻辑推理觉的并不是最新日志之前)
#leaderId
#CommitIdx:leader上已经提交的日志索引
内部维护的数据结构:
#nextIndex[]:维护每一个从节点下一次需要复制的日志条目索引数组
响应方:
角色:非leader角色
响应规则:
a.进行term检查,如果term小于自身,拒绝更新日志,直接返回False。(上一届领导的命令肯定不能听)
b.进行一致性检测:如果在prevLogIndex
处的日志的任期号与prevLogTerm
不匹配时,返回 false
c.如果一条已经存在的日志与新的冲突(index 相同但是任期号 term 不同),则删除已经存在的日志和它之后所有的日志。
d.添加任何在已有的日志中不存在的条目
e.更新commitidx,如果主的commitidx>自身的commitidx,则 自身的commitidx = 主的commitidx。(本人认为在实际raft系统中由于满足领导人完全原则所以不会存在从的commitidx>主的commitidx情况)
#raft遵循的原则
#领导人只增加原则:领导人永远不会覆盖或者删除自己的日志,它只会增加条目
#领导人完全原则:再一个任期提交的日志一定,出现在任期更大的领导人日志中。
#选举安全原则:一个任期内最多只能有一个leader
#日志匹配原则:如果两个日志在相同的索引位置上的日志条目的任期号相同,那么我们就认为这个日志从头到这个索引位置之间的条目完全相同
#状态机安全原则:如果一个服务器已经将给定索引位置的日志条目应用到状态机中,则所有其他服务器不会在该索引位置应用不同的条目
#raft和遵循原则的之间的因果关系
#领导人只增加原则:
raft系统中日志的修改来自两方面,1.appendEntrtries,根据appendEntrtries的响应描述可知,日志可以append,删除修改。2,客户端日志写入,是append操作
在raft的appendEntrtries操作定义中响应方是非leader,所以leader只能介绍客户段的append日志操作-->原则得证
#选举安全原则:
在选举操作响应规则c中规定在同一个任期内只能投一次票
证明:同一个任期内只能投一次票:则这个任期中的投票次数和节点个数相等的(2N+1),其中大多数票投个A,成为leader,B就不能得到大多数的投票。
#领导人完全原则:
在选举的操作中,响应规则b,要求候选者的日志条目包含自身的日志条目,才可以投票给该候选者。
一个候选者得到大多数的节点的投票才能成为leader -->leader的日志能够包含大多数节点(Node_Majority_set)的日志条目,并且当前leader的任期一定大于Node_Majority_set日志中的term。
证明:
反证法:假设存在一条日志已经提交了但是在Node_Majority_set_1中不存在节点包含这条记录
由于已经提交的日志是已经存在于大多数节点(Node_Majority_set_2)中的日志,Node_Majority_set_1&Node_Majority_set_2 != NULL,因此必定存在节点包含这条记录,所以得出矛盾,结论正确
因此已经提交的日志条目一定包含在Node_Majority_set_1的节点中(不一定全包含,可能是部分节点),而前面的论证leader的日志能够包含大多数节点的日志条目(节点已提交的日志条目是所有日志条目的子集),所以新leader的日志一定包含所有已经提交的日志条目。
#日志匹配原则:
命题:如果两个日志在相同的索引位置上的日志条目的任期号相同-->该日志索引处前面的索引上对应的日志条目完全相同
根据appendEntrtries响应规则中b的描述:在在prevLogIndex
处的日志的任期号与prevLogTerm
不匹配时,返回 false
归纳证明:初始化时所有的节点的在LogIdx=0处的任期号自然相同。
当LogIdx=N处的任期号相同时,appendEntrtries leader同步复制 LogIdx = N+1的日志时,会比对LogIdx=N的进行任期相同的比较,如果相同会写入 LogIdx = N+1的日志条目,由于所有的从节点复制来自同一个主节点所以任期相同
因此归纳证明可得结论
#状态机安全原则:
首先发送给状态机的日志必须是已经提交的日志,如果一个日志log1已经提交,那么在该日志的索引位置处不会存在另一条log2已经提交但是和该日志条目不一样的日志
假设存在log2已经被提交,说明在log2处的索引的日志在大多数节点保持一致,同理log1在log1索引处的日志条目在大多多数节点保持一致。
(log1_idx == log2_idx) 所以必然得到 log2 == log1
#这些原则为什么能保证数据的一致性
# 领导人只增加原则+选举安全原则:保证raft系统中最多只有一个leader,并且日志复制只从leader单向流动到从节点(个人任务系统命名为raft中文漂流,是不是就是因为日志复制是单向的流动的呢)。
# 领导人完全原则:能够保证新的leader中包含所有已经提交的日志,已经提交的日志是不会再修改的,从而保证新的leader产生也不会对已经提交的日志产生修改操作。
#根据日志匹配原则可以保证如果两个日志在相同的索引位置上的日志条目的任期号相同-->该日志索引处前面的索引上对应的日志条目完全相同:
在appendEntrtries操作的响应规则c规定如果一条已经存在的日志与新的冲突(index 相同但是任期号 term 不同),则删除已经存在的日志和它之后所有的日志。然后复制eader的同步的日志条目,和leader保持一致。
如果不冲突:根据日志匹配原则可以判断前面的日志一定也是和leader保持一致的,把新的日志条目添加后,和leader保持一致。
综上所述:这些原则能够保证系统中最多只存在一个leader,而且leader包含之前任期的所有已提交的日志条目,日志条目只从leader流向从节点,在主从日志同步阶段能够保证日志的一致。
raft--分布式一致性协议的更多相关文章
- 浅谈 Raft 分布式一致性协议|图解 Raft
前言 本篇文章将模拟一个KV数据读写服务,从提供单一节点读写服务,到结合分布式一致性协议(Raft)后,逐步扩展为一个分布式的,满足一致性读写需求的读写服务的过程. 其中将配合引入Raft协议的种种概 ...
- 分布式一致性协议Raft原理与实例
分布式一致性协议Raft原理与实例 1.Raft协议 1.1 Raft简介 Raft是由Stanford提出的一种更易理解的一致性算法,意在取代目前广为使用的Paxos算法.目前,在各种主流语言中都有 ...
- 搞懂分布式技术2:分布式一致性协议与Paxos,Raft算法
搞懂分布式技术2:分布式一致性协议与Paxos,Raft算法 2PC 由于BASE理论需要在一致性和可用性方面做出权衡,因此涌现了很多关于一致性的算法和协议.其中比较著名的有二阶提交协议(2 Phas ...
- [转帖]分布式一致性协议介绍(Paxos、Raft)
分布式一致性协议介绍(Paxos.Raft) https://www.cnblogs.com/hugb/p/8955505.html 两阶段提交 Two-phase Commit(2PC):保证一个 ...
- [转帖]图解分布式一致性协议Paxos
图解分布式一致性协议Paxos https://www.cnblogs.com/hugb/p/8955505.html Paxos协议/算法是分布式系统中比较重要的协议,它有多重要呢? <分 ...
- Zookeeper——分布式一致性协议及Zookeeper Leader选举原理
文章目录 一.引言 二.从ACID到CAP/BASE 三.分布式一致性协议 1. 2PC和3PC 2PC 发起事务请求 事务提交/回滚 3PC canCommit preCommit doCommit ...
- 使用GO实现Paxos分布式一致性协议
什么是Paxos分布式一致性协议 最初的服务往往都是通过单体架构对外提供的,即单Server-单Database模式.随着业务的不断扩展,用户和请求数都在不断上升,如何应对大量的请求就成了每个服务都需 ...
- 分布式一致性协议之:Raft算法
一致性算法Raft详解 背景 熟悉或了解分布性系统的开发者都知道一致性算法的重要性,Paxos一致性算法从90年提出到现在已经有二十几年了,而Paxos流程太过于繁杂实现起来也比较复杂,可能也是以为过 ...
- 分布式一致性协议 Raft
分布式领域,CP模型下 数据一致性协议至关重要,不然两边数据不一致容易出现数据读混乱问题.像Etcd Consul zookeeper Eureka ,Redis集群方案这些中间件 都有一致性算法来 ...
- 分布式一致性协议-2PC与3PC(二)
一.分布式一致性 一个事务需要跨多个分布式节点,又要保持事务的ACID特性,需要引入协调者来统一调度所有分布式节点的执行逻辑,被调度的节点称为参与者. 协调者负责调用参与者,并决定最终是否提交事务.基 ...
随机推荐
- 乘风破浪:LeetCode真题_037_Sudoku Solver
乘风破浪:LeetCode真题_037_Sudoku Solver 一.前言 这次我们对于上次的模型做一个扩展并求解. 二.Sudoku Solver 2.1 问题 2.2 分析与解决 这道题 ...
- Local policy - User rights assignment 对照表
"SeCreateTokenPrivilege" --> "Create a token object" "SeAssignPrimaryTo ...
- android开发之一如何升级SDK
看了很多文章,都没有成功,下面这篇才是正解,学海无涯苦作舟. Fetching https://dl-ssl.google.com/android/repository/addons_list-2.x ...
- 《面向对象程序设计》c++第六次作业___calculator SE
c++第五次作业 Calculator SE 代码 PS:这次作业延迟了很久,人要是迷茫啊----唉------ 新增GUI界面,使用Qt creator编写,纯代码生成控件.写坐标. 感觉Qt cr ...
- 张高兴的 .NET Core IoT 入门指南:(一)环境配置、Blink、部署
如何在 Raspberry Pi 的 Raspbian 上构建使用 GPIO 引脚的 IoT 程序?你可能会回答使用 C++ 或 Python 去访问 Raspberry Pi 的引脚.现在,C# 程 ...
- python第三十二课——队列
队列:满足特点 --> 先进先出,类似于我们生活中的买票.安检 [注意] 对于队列而言:python中有为其封装特定的函数,在collections模块中的deque函数就可以获取一个队列对象; ...
- PHP常用算法和数据结构示例
<?php header("content-type:text/html;charset=utf-8"); $arr=array(3,5,8,4,9,6,1,7,2); ec ...
- linux 的常用命令---------第六阶段
磁盘管理 IDE 硬盘 (了解)硬盘接口 : SATA 硬盘 SCSI 硬盘 SAS 硬盘 分区付的认识:(笔试题) MBR :硬盘主引导记录,共512字节,由三部分组成 主引导程序 :占446个 ...
- oracle 查看删除重复数据
1.查询重复数据select * from 表名 where 重复字段(一般为主键)in (select 重复字段 from 表名 group by 重复字段 having count(WF_OID) ...
- [转]OpenGL 使用 PBO 高速复制屏幕图像到内存或者纹理中
如果你想给游戏做个截图功能,或者想把屏幕图像弄成一个纹理,你就非常需要 PBO 了 通常情况下,你想把屏幕图像的像素数据读到内存需要用 glReadPixels 然后 pixels 参数传进去一块内存 ...