浅谈 Raft 分布式一致性协议|图解 Raft
前言
大家好,这里是白泽。本文是一年多前参加字节训练营针对 Raft 自我整理的笔记。
本篇文章将模拟一个KV数据读写服务,从提供单一节点读写服务,到结合分布式一致性协议(Raft)后,逐步扩展为一个分布式的,满足一致性读写需求的读写服务的过程。
其中将配合引入Raft协议的种种概念:选主、一致性、共识、安全等,通篇阅读之后,将帮助你深刻理解什么是分布式一致性协议。
一、 单机KV数据读写服务
DB Engine这里可以简单看成对数据的状态进行存储(比如B+树型的组织形式),负责存储KV的内容 ,并假设这个KV服务将提供如下接口:
- Get(key) —> value
- Put([key, value])
思考此时KV服务的可靠性:
- 容错:单个数据存储节点,不具备容错能力。
- 高可用:服务部署在单节点上,节点宕机将无法提供服务。
思考此时KV服务的正确性:
- 单进程,所有操作顺序执行,可以保证已经存储的数据是正确的
数据规模不断增加,我们需要扩大这个KV服务的规模,将其构建成一个分布式的系统
二、一致性与共识算法
2.1 从复制开始
既然一个节点会挂,那么我们就多准备一个节点!
我们这里只考虑主副本负责接收数据,而从副本只负责同步接收主副本数据的模式,如果主从都开放数据接收能力,将要考虑更多高可用和数据一致性的问题。
2.2 如何复制
- 主副本定期拷贝全量数据到从副本(代价太高)
- 主副本拷贝操作日志到从副本:如果你了解MySQL的主从同步,你就会想起它也是通过从副本监听主副本当中的
bin log
操作日志变化,进行集群数据同步的,因此这种方式也是更主流的选择。
2.3 具体的写流程
-
主副本把所有的操作打包成
Log
- 所有的
Log
写入都是持久化的,保存在磁盘上
- 所有的
-
应用包装成状态机(也就是DB Engine部分),只接收
Log
作为Input
-
主副本确认
Log
已经成功写入到从副本机器上,当状态机apply
后,返回客户端(关于写入之后,请求返回客户端的时机,是可以由应用控制的,可以是Log
写入从副本之后,就从主副本机器返回,也可以等Log
完成落盘之后,再返回)
2.4 具体的读流程
- 方案一:直接读状态机(这里指的是DB),要求上一步写操作进入状态机后再返回client(数据已落盘)
- 方案二:写操作复制
Log
完成后直接返回,读操作Block
等待所有pending log
进入状态机
- 如果不遵循上述两种方案:可能存在刚刚写入的值读不到的情况(在Log中)
2.5 什么是一致性
对于我们的KV服务,要像操作一台机器一样,对用户来说在写入成功后,就应该能读到最近写入的值,而不关心具体底层是如何分布式实现。
一致性是一种模型(或语义),约定一个分布式系统如何向外界提供服务,KV服务中常见的一致性模型有以下两种:
- 最终一致性:读取可能暂时读不到但是总会读到
- 线性一致性:最严格,线性时间执行(写完KV确保就能读到),是最理想中的状态
2.6 复制协议-当失效发生
上述用到的添加了一个从副本节点的方式,我们暂且将其称为山寨版分布式一致性协议——复制协议(因为它依赖于主从副本间的复制操作)
那么当主副本失效时,以这个复制协议为基础的KV服务的运行情况如何呢:
- 容错能力:没有容错能力,因为主副本停了,KV服务就停了
- 高可用:或许有,取决于我们发现主副本宕机后多快将从副本切换为主副本(手动切换)
- 正确性:正确,因为操作只从一台机器发起,可以控制所有操作返回前都已经复制到另一台机器了
衍生出的一些的问题:
- 如何保证主副本是真的失效了,切换的过程中如果主副本又开始接收
client
请求,则会出现Log覆盖写的情况 - 如果增加到3个乃至更多的节点,每次PUT数据的请求都等待其他节点操作落盘性能较差
- 能否允许少数节点挂了的情况下,仍然可以保持服务能工作(具备容错能力)
2.7 共识算法
什么是共识:一个值一旦确定,所有人都认同
共识协议不等于一致性:
-
应用层面不同的一致性,都可以用共识算法来实现
- 比如可以故意返回旧的值(共识算法只是一个彼此的约定,只要数据存储与获取符合需求,达成共识即可)
-
简单的复制协议也可以提供线性一致性(虽然不容错)
一般讨论共识协议时提到的一致性,都指线性一致性
- 因为弱一致性往往可以使用相对简单的复制算法实现
三、一致性协议案例:Raft
3.1 Ratf概述
-
2014年发表
-
易于理解作为算法设计的目标
- 使用了RSM、Log、RPC的概念
- 直接使用RPC对算法进行了描述
- Strong Leader-based(所有操作都是Leader发起的)
- 使用了随机的方法减少约束(比如选主时Follower谁先发现Leader失联就发起选主)
3.2 复制状态机(RSM)
-
RSM(replicated state machine)
- Raft中所有的共识机制(consensus)都是直接使用Log作为载体:简单说就是client将Log提供给Raft(也就是上图的Consensus Module),Raft确定这个Log已经Commit之后(写入Log文件),将Log提供给用户的状态机
- 注意此时状态机是一个内存中的存储概念,当然后续每个节点数据还涉及到落盘持久化,这是具体应用层面的问题了,这里讨论的更多是Raft协议的原理概念,使用状态机对此进行抽象
-
Commited Index(这里先有个概念,用于标识Log中哪些数据可以提交给状态机)
- 一旦Raft更新Commited Index,意味着这个Index前所有的Log都可以提交给状态机了
- Commited Index是不持久化的,状态机也是volatile的(断电清空),重启后从第一条Log开始恢复状态机,Raft协议工作过程中每个节点的Commited Index可能是不一致的,但是Ratf可以保证最终所有节点都是一致的
3.3 Raft角色
- Leader是所有操作的发起者,会把Log同步给Follower,确定哪个Log已经Commit了
- 接受Leader的请求,并且在发现Leader消失,则转换为Candidate,向其他Follower申请投票,如果获得票数过半,则晋升为Leader
3.4 Raft日志复制
接下来讲解一下Raft协议中Log是如何在节点间同步的:
- 第一张图s2是当前Leader,紫色的小箭头指向每个节点已经确认commit的Log位置
- 第二张图s2又append了一条新的Log,用K标识
- 第三张图表示,s2将自己收到的K以及自己的Commited Index发送给所有Follower节点。Follower都同步Leader的Log以及Commited Index,并且Follower的状态机检测到自己的Commited Index又向前推进了,说明有新的Log值达成了共识,就会把Commited Index没读取的Log值应用给状态机
- 第四张图,当Follower完成步骤三的操作之后,会告诉Leader自己已经拿到这个Log值了,Leader在收到多数派的确认消息后,就可以确定这个K已经永远的持久化,也就是已经Commit了,因此将Commited Index向后推一位
这里有一个细节就是,虽然Raft协议使节点间的数据最终达成了一致,但是节点Log数据落盘时间是有先后的(因为Commited Index在各节点之间存在差异,所以Log与状态机是有时差的)
3.5 Raft从节点失效
- 图一假设s3失效了很久,Log有很多数据没有同步
- 图二表示此时的Leader是s2,将自己Log中的K和自己的Commited Index复制给s1
- 图三中s2的K完成了commit(流程参考Raft日志复制),因为s1、s2共两个节点已经形成了多数派(此时已经有容错能力了)
- 图四中,s3尝试重新同步数据,在Raft协议中,s3会向s2逆向迭代的去获取Log数据(K、QK、TQK、XTQK),直到与s3当前Log相对齐则完成数据同步(当然Raft具体实现中应用对此过程进行了优化)
3.6 Raft Term
Term(周期的概念),在Raft中相当于一个逻辑的时钟,下面介绍:
- 每个Leader服务于一个term:一旦发现更高的term,那么表示我已经不是Leader了
- 每个term至多只有一个leader:选出新的Leader,会使得term增加
- 每个节点存储当前的term
- 每个节点term从一开始,只增不减
- 所有的rpc的request response 都携带term
- 只commit本term内的log
3.7 Raft主节点失效
-
Leader定期发送AppendEntries RPCs 给其余所有的节点
-
如果Follower 有一段时间没有收到Leader的AppendEntries,则转换身份为Candidate
-
Candidate自增自己的term,同时使用RequestVote PRCs 向剩余节点请求投票
- raft在检查是否可以投票时,会检查log是否outdated,至少不比本身旧才会投票给对应的Candidate
-
如果多数派投给它,则成为该term的leader
3.8 Raft Leader failure
接下来模拟一下主节点失效后的流程,并且注意此时图一当中,s1的Log内容比较特殊,为XW,这个场景是可以实现的,比如一开始有s1、s2、s3,其中s1是Leader,首先将X同步给了s2和s3,并且接受到了新的W,但是突然s1节点卡死了,s2和s3重新开始选举Leader为s2,最终s2和s3的Log变成了XTQ(这只是一种情况,一切皆有可能),然后s1又恢复了。
关于为什么s3落后s2两条Commited Index,有可能是s2一次同步了两条Log给s3,而s3的状态机还没来得及同步数据,但是s3接收到在标识TQ的Log后,将其commit到自己的Log之中,就返回确认响应给了s2,s2将自己的Commited Index向后推进到Q的位置。
- 从图二开始,s3发现s2挂了,没有Leader,则将自己的term+1变为3,并且向s1请求Vote
- 图三中,s1发现RequestVote是来自term等于3的,大于自己的term,因此将自己的term也更新为3,同时答应给s3投票,达成多数派条件,s3成为新的Leader
- 图四,s3开始同步自己的Log给s1,但是发现s1中的Log起初为XW,而s3中为XTQY,因此还是会按照从后往前迭代的方式将数据同步给s1(Y、QY、TQY、XTQY),最后s1虽然与同步状态发生了冲突,但是由于Raft是
Strong Leader-based
的,会完全按照Leader的内容覆盖自己的Log
3.9 Raft 安全性 - 同 Term
-
对于Term内的安全性
- 目标:对于所有已经commited的<term, index>位置上至多只有一条log
-
由于Raft的多数派选举,我们可以保证在一个term中只有一个leader
- 我们可以证明一条更严格的声明:在任何<term, index>位置上,至多只有一条log
3.10 Raft 安全性 - 跨 Term
-
对于跨Term的安全性
- 目标:如果一个log被标记commited,那这个log一定会在未来所有的leader中出现
-
可以证明这个property:
- Raft选举时会检查Log是否outdated,只有最新的才能当选Leader
- 选举需要多数派投票,而commited log也已经在多数派当中(必有overlap)
- 新Leader一定持有commited log,且Leader永远不会overwrite log
四、回到KV
4.1 重新打造KV服务
结合Raft算法,更新一下我们的KV
回顾一下一致性读写的定义:
-
方案一:
- 写log被commit了,返回客户端成功
- 读操作也写入一条log(因为读的数据可能还没从Log写入状态机,无法读取状态机数据),状态机apply log后返回client
- 增加Log量
-
方案二:
- 写log被commit了,返回客户端成功
- 读操作先等待所有commited log apply,再读取状态机
- 优化写时延
-
方案三:
- 写log被状态机apply,返回给client
- 读操作直接读状态机
- 优化读时延
多个副本只有单个副本可以提供服务(一个Leader万一忙不过来怎么办)
-
服务无法水平扩展
-
增加更多Raft组(不多展开)
- 如果操作跨Raft组(对key进行分组,每一个Raft负责读写一个range的key)
4.2 回到共识算法
-
Raft:关于log
- 论文中就给出的方案,当过多的Log产生后,启动snapshot,替换掉Log
- 如果对于持久化的状态机,如何快速的产生Snapshot(略)
- 多组Raft的应用中,Log如何合流(略)
-
关于configuration change(节点规模变化)
- 论文中就给出的joint-consensus以及单一节点变更两种方案(很复杂,略)
4.3 共识算法的未来
-
Raft Paxos相互移植
-
Raft有很多成熟的实现
-
研究主要关注在Paxos上
-
如何关联两种算法:
- On the Parallels between Paxos and Raft, and how to Port Optimizations[1]
- Paxos vs Raft: Have we reached consensus on distributed consensus?[2]
-
感兴趣可以关注公众号 「白泽talk」,白泽目前也打算打造一个氛围良好的行业交流群,文章的更新也会提前预告,欢迎加入:622383022。
相关文章:

浅谈 Raft 分布式一致性协议|图解 Raft
前言 大家好,这里是白泽。本文是一年多前参加字节训练营针对 Raft 自我整理的笔记。 本篇文章将模拟一个KV数据读写服务,从提供单一节点读写服务,到结合分布式一致性协议(Raft)后,逐步扩展为一个分布式的…...

4_【Linux版】重装数据库问题处理记录
1、卸载已安装的oracle数据库。 2、知识点补充: 3、调整/dev/shm/的大小 【linux下修改/dev/shm tmpfs文件系统大小 - saratearing - 博客园 (cnblogs.com)】 mount -o remount,size100g /dev/shm 4、重装oracle后没有orainstRoot.sh 【重装oracle后没有orains…...

隧道应用2-netsh端口转发监听Meterpreter
流程介绍: 跳板机 A 和目标靶机 B 是可以互相访问到的,在服务器 A 上可以通过配置 netsh 端口映射访问 B 服务器。如果要拿 B 服务器的权限通常是生成正向后门,使用 kali 的 msf 正向连接B服务器,进而得到 Meterpreter,…...

《Spring》--使用application.yml特性提供多环境开发解决方案/开发/测试/线上--方案1
阿丹有话说: 有不少同志有疑问说我正常开发的时候,需要自己搭建项目的时候。总是出现配置文件环境切换出现问题。多环境系列会出两个文章解决给搭建重点解决一下这个问题。给与两种解决的方案。正确让大家只需要按照步骤操作就可以完成。 原理…...
统计项目5000+,出具报表5分钟......捷顺科技数据中台怎么做?
捷顺创立于1992年,以智慧车行、人行出入口软硬件产品为依托,致力于智慧停车生态建设和运营,是出入口智能管理和智慧生态环境建设的开创者和引领者。 历经近三十年的发展,已经成为国内智慧停车领域的领军企业。公司集研、产、销一…...

力扣(105. 从前序与中序遍历序列构造二叉树,106. 从中序与后序遍历序列构造二叉树)
题目1链接 题目1: 思路:使用前序确定根,使用中序分左右子树,分治法。 难点:如何控制递归确定左右子树。 /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* T…...

网络安全技术新手入门:在docker上安装dvwa靶场
前言 准备工作:1.已经安装好kali linux 步骤总览:1.安装好docker 2.拖取镜像,安装dvwa 一、安装docker 输入命令:sudo su 输入命令:curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key …...

Docker 介绍 及 支持的操作系统
Docker组成: Docker主机(Host): 一个物理机或虚拟机, 用于运行Docker服务进程和容器, 也成为宿主机, node节点。 Docker服务器端(Server): Docker守护进程, 运行Docker容器。 Docker客户端(Client): 客户端使用docker命令或其他工…...

大模型实战营Day5 LMDeploy大模型量化部署实践
模型部署 定义 产品形态 计算设备 大模型特点 内存开销大 动态shape 结构简单 部署挑战 设备存储 推理速度 服务质量 部署方案:技术点 (模型并行 transformer计算和访存优化 低比特量化 Continuous Batch Page Attention)方案(…...

py连接sqlserver数据库报错问题处理。20009
报错 pymssql模块连接sqlserver出现如下错误: pymssql._pymssql.OperationalError) (20009, bDB-Lib error message 20009, severity 9:\nUnable to connect: Adaptive Server is unavailable or does not exist (passwordlocalhost)\n) 解决办法: 打…...

LTESniffer:一款功能强大的LTE上下行链路安全监控工具
关于LTESniffer LTESniffer是一款功能强大的LTE上下行链路安全监控工具,该工具是一款针对LTE的安全开源工具。 该工具首先可以解码物理下行控制信道(PDCCH)并获取所有活动用户的下行链路控制信息(DCI)和无线网络临时…...

SQL语句详解二-DDL(数据定义语言)
文章目录 操作数据库创建:Create查询:Retrieve修改:Update删除:Delete使用数据库 操作表常见的几种数据类型创建:Create复制表 查询:Retrieve修改:Update删除:Delete 操作数据库 创…...

web前端算法简介之链表
链表 链表 VS 数组链表类型链表基本操作 创建链表:插入操作:删除操作:查找操作:显示/打印链表:反转链表:合并两个有序链表:链表基本操作示例 JavaScript中,instanceof环形链表 判断…...
C++函数对象
任何定义了函数调用操作符的对象都是函数对象。C 支持创建、操作新的函数对象,同时也提供了许多内置的函数对象。 函数包装器 std::function 提供存储任意类型函数对象的支持。 function (C11) 包装具有指定函数调用签名的任意类型的可调用对象 (类模板) bad_funct…...

插件化简单介绍
关于作者:CSDN内容合伙人、技术专家, 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 ,擅长java后端、移动开发、商业变现、人工智能等,希望大家多多支持。 未经允许不得转载 目录 一、导读二、概览三、常见的插件化方案…...

[Beego]1.Beego简介以及beego环境搭建,bee脚手架的使用,创建,运行项目
一.Beego介绍 Beego是一个开源的基于Golang的MVC框架,主要用于Golang Web开发,Beego可以用来快速开发API、Web、后端服务等各种应用。 Golang 的Web开发框架有很多,从 github star 数量来看Gin>Beego>lris>Echo>Revel>Buffalo 目前国内用的比较多的就…...
Tomcat 静态资源访问与项目根路径设置(AI问答)
一个静态文件,放在Tomcat中,希望能够通过网络访问,应该放在哪里? 在Apache Tomcat中,如果想要部署静态文件(例如HTML、CSS、JavaScript、图片等)并能通过网络访问,通常需要将这些文…...

Docker实战09|使用AUFS包装busybox
前几篇文章中,重点讲解了如何实现构建容器,需要回顾的小伙伴可以看以下文章: 《Docker实战06|深入剖析Docker Run命令》《Docker实战07|Docker增加容器资源限制》《Docker实战08|Docker管道及环境变量识别…...
什么是uni.request()?如何使用它?
uni.request()是uni-app提供的一个用于发起网络请求的API。 使用uni.request()的步骤如下: 在需要发起网络请求的页面中引入uni.request()方法。 调用uni.request()方法,并传入相应的参数,包括请求地址、请求方法、请求头部和请求数据等。 …...

用React给XXL-JOB开发一个新皮肤(一):环境搭建和项目初始化
目录 一. 简述二. Fork 项目三. 搭建开发环境四. 初始化皮肤项目五. 添加相关依赖六. 预览 一. 简述 大名鼎鼎的 xxl-job 任务调度中心我们应该都使用过,项目地址:xxl-job。它是一个分布式任务调度平台,其核心设计目标是开发迅速、学习简单…...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...
在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:
在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档,…...
基于服务器使用 apt 安装、配置 Nginx
🧾 一、查看可安装的 Nginx 版本 首先,你可以运行以下命令查看可用版本: apt-cache madison nginx-core输出示例: nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...
基于Uniapp开发HarmonyOS 5.0旅游应用技术实践
一、技术选型背景 1.跨平台优势 Uniapp采用Vue.js框架,支持"一次开发,多端部署",可同步生成HarmonyOS、iOS、Android等多平台应用。 2.鸿蒙特性融合 HarmonyOS 5.0的分布式能力与原子化服务,为旅游应用带来…...
C++中string流知识详解和示例
一、概览与类体系 C 提供三种基于内存字符串的流,定义在 <sstream> 中: std::istringstream:输入流,从已有字符串中读取并解析。std::ostringstream:输出流,向内部缓冲区写入内容,最终取…...
数据库分批入库
今天在工作中,遇到一个问题,就是分批查询的时候,由于批次过大导致出现了一些问题,一下是问题描述和解决方案: 示例: // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...
Python ROS2【机器人中间件框架】 简介
销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...

基于Springboot+Vue的办公管理系统
角色: 管理员、员工 技术: 后端: SpringBoot, Vue2, MySQL, Mybatis-Plus 前端: Vue2, Element-UI, Axios, Echarts, Vue-Router 核心功能: 该办公管理系统是一个综合性的企业内部管理平台,旨在提升企业运营效率和员工管理水…...
第八部分:阶段项目 6:构建 React 前端应用
现在,是时候将你学到的 React 基础知识付诸实践,构建一个简单的前端应用来模拟与后端 API 的交互了。在这个阶段,你可以先使用模拟数据,或者如果你的后端 API(阶段项目 5)已经搭建好,可以直接连…...

WebRTC调研
WebRTC是什么,为什么,如何使用 WebRTC有什么优势 WebRTC Architecture Amazon KVS WebRTC 其它厂商WebRTC 海康门禁WebRTC 海康门禁其他界面整理 威视通WebRTC 局域网 Google浏览器 Microsoft Edge 公网 RTSP RTMP NVR ONVIF SIP SRT WebRTC协…...