Paxos 存在的问题

Paxos 算法的描述偏学术化,缺失了很多细节,无法直接应用于工程领域。实际工程应用中的分布式算法大多是 Paxos 的变种,验证这些算法的正确性也成为了一个难题。

举个例子:上一篇文章的 最后 介绍了一个应用 Paxos 算法的工程模型,这个模型存在明显的写性能瓶颈:

  • 使用多主架构,写入冲突的概率高
  • 每次更新操作都需要至少 2 轮以上的网络通信,通信开销大

如果要提高该模型的性能,仍需要在很多细节上做进一步调整,最终实现出来的算法已经和原始的版本的 Paxos 相去甚远。

为了解决以上问题,另一个高性能且易于理解的一致性算法横空出世:Raft

基本概念

Raft 算法基于 复制状态机Replicated State Machine模型,本质上就是一个管理 日志复制 的算法。

Raft 集群采用 Single Leader 架构,集群中有唯一的 Leader 进程负责管理日志复制,其职责有:

  • 接受 Client 发送的请求
  • 将日志记录同步到其他进程
  • 告知其他进程的何时能够提交日志

复制状态机

复制状态机的本质就是:Paxos + WAL

每个进程维护一个状态机State Machine,并且使用一个日志存储其所要执行指令。
如果两个状态机执行按照相同的顺序,执行相同的指令,则这两个进程最终能够收敛到同个状态。如果能保证所有进程的日志一致,则每个进程的状态必定也是一致的。

任期

为了减少不必要的网络通信,日志追加顺序由集群唯一的 Leader 决定,无须与其他节点协商。通信开销从最低 2 次降为固定的 1 次,从而大幅提高了算法的性能。

出于可用性考虑,当前 Leader 下线后,集群需要从存活的节点中挑选一个新的 Leader,这个过程被称为选举 election

每次选举都会产生一个新的任期号 term(单调递增),如果选举中产生了一个新的 Leader,那么这个任期号会伴随这个 Leader 直到其下线。

每个 参与者 进程都会维护一个 current_term 用于表示已知的最新任期,进程之间通过彼此交换该值来感知 Leader 变化。

/**
* 基础信息
*/
public abstract class RaftMember implements RaftParticipant { // 响应 RPC 前需要持久化以下两个属性
protected final long currentTerm; // 已知的最新的 term(初始为 0)
protected final ID lastCandidate; // 最近一次赞成投票的 candidate protected RaftMember(long term, ID candidate) {
this.currentTerm = term;
this.lastCandidate = candidate;
stableStorage().persist(currentTerm, lastCandidate);
} /**
* @see RaftParticipant#currentTerm()
*/
@Override
public long currentTerm() {
return currentTerm;
} /**
* @see RaftParticipant#votedFor()
*/
@Override
public ID votedFor() {
return lastCandidate;
} }

日志

日志是 Raft 的核心概念。Raft 保证日志是连续且一致的,并且最终能够被所有进程按照日志索引的顺序提交。

每条日志记录包含:

  • 任期term:生成该条记录的 Leader 对应的任期
  • 索引index:其在日志中的顺序
  • 命令command:可执行的状态机指令

一旦某条日志中的命令被状态机执行了,那么我们称这条记录为已提交committed,Raft 保证已提交的记录不会丢失。

角色

Raft集群中每个进程只能担任其中一个角色:

  • Leader:发送心跳、管理日志复制与提交
  • Follower:被动响应其他节点发送过来的请求
  • Candidate:主动发起并参与选举

Raft 进程间使用 RPC 的方式进行通信,实现最基础的共识算法只需 两种RPC

  • RequestVote:用于选举产生 Leader
  • AppendEntries:复制日志与发送心跳
/**
* RPC 接口
* */
public interface RaftService { /**
* 复制日志+发送心跳(由 leader 调用)
* @param term leader 任期
* @param leaderId leader 在集群中的唯一标识
* @param prevLogIndex 紧接在新的之前的日志条目索引
* @param prevLogTerm prevLogIndex 对应的任期
* @param entries 日志条目(发送心跳时为空)
* @param leaderCommit leader 已经提交的日志条目索引
* @return 当 follower 中的日志包含 prevLogIndex 与 prevLogTerm 匹配的日志条目返回 true
* */
Async<RaftResponse> appendEntries(
long term, ID leaderId,
long prevLogIndex, long prevLogTerm,
Entry[] entries, long leaderCommit) throws Exception; /**
* 选主(由 candidate 调用)
* @param term candidate 任期
* @param candidateId candidate 在集群中的唯一标识
* @param lastLogIndex candidate 最后一条日志条目的索引
* @param lastLogTerm candidate 最后一条日志条目的任期
* @return 当收到赞成票时返回 true
* */
Async<RaftResponse> requestVote(
long term, ID candidateId,
long lastLogIndex, long lastLogTerm) throws Exception; }

算法流程

基于 Single Leader 模型,Raft 将一致性问题分解为 3 个独立的子问题:

  • Leader 选举Election:Leader 进程失效后能够自动选举出一个新的 Leader
  • 日志复制Replication:Leader 保证其他节点的日志与其保持一致
  • 状态安全 Safety:Leader 保证状态机执行指令的顺序与内容完全一致

为了方便理解,下面结合 动画 进行介绍。

选举

使用 心跳超时heartbeat timeout机制来触发 Leader 选举:

  • 节点启动时默认处于 Follower 状态,如果 Follower 超时未收到 Leader 心跳信息,会转换为 Candidate 并向其他节点发起 RequestVote 请求。
  • 当 Candidate 收到半数以上的选票之后成为 Leader,开始定时向其他节点发起 AppendEntries 请求以维持其 Leader 的地位。
  • Leader 失效之后停止发送心跳,Follower 的心跳超时机制又会被触发,开始新一轮的选举。

复制

未提交日志
已提交日志

集群中只有 Leader 对外提供服务:

客户端与 Leader 进行通信时,每个请求包含一条可以被状态机执行的命令。

当 Leader 在接收到命令之后,首先会将命令转换为一条对应的 日志记录log entry,并追加到本地的日志中。然后调用 AppendEntries 将这条日志复制到其他节点的日志中。

当日志被复制到过半数节点上时,Leader 会将这条日志中包含的命令 提交commit状态机执行,最后将执行结果告知客户端。

网络分区

使用 过半数majority机制来处理脑裂:

发生网络分区后,集群中可能同时出现多个 Leader,复制机制保证了最多只有一个 Leader 能够正常对外提供服务。

如果日志无法复制到多数节点,Leader 会拒绝提交这些日志,当网络分区消失后,集群会自动恢复到一致的状态。

安全性保证

选举时…

保证新的 Leader 拥有所有已经提交的日志

  • 每个 Follower 节点在投票时会检查 Candidate 的日志索引,并拒绝为日志不完整的 Candidate 投赞成票
  • 半数以上的 Follower 节点都投了赞成票,意味着 Candidate 中包含了所有可能已经被提交的日志

提交日志时…

Leader 只主动提交自己任期内产生的日志

  • 如果记录是当前 Leader 所创建的,那么当这条记录被复制到大多数节点上时,Leader 就可以提交这条记录以及之前的记录
  • 如果记录是之前 Leader 所创建的,则只有当前 Leader 创建的记录被提交后,才能提交这些由之前 Leader 创建的日志

总结

一致性算法的本质:一致性与可用性之间的权衡

Raft 的优点:Single Leader 的架构简化日志管理

所有日志都由 Leader 流向其他节点,无需与其他节点进行协商。其他节点只需要记录并应用 Leader 发送过来日志内容即可,将原来的两阶段请求优化为一次 RPC 调用。

Raft 的缺点:对日志的连续性有较高要求

为了简化日志管理,Raft 的日志不允许存在空隙,限制了并发性。某些应用场景下,需要通过Multi-Raft的模式对无关的业务进行解耦,从而提高系统的并发度。

限于篇幅限制,本文只展示了 Raft 算法大致的轮廓。

此前在学习算法的过程中,使用 Java 实现了一个简化版的 Raft 协议:rafting

代码忠实于论文原文,包含了其中的众多算法细节,希望对各位学习 Raft 的朋友有所帮助。

Raft 协议的更多相关文章

  1. Raft协议实战之Redis Sentinel的选举Leader源码解析

    这可能是我看过的写的最详细的关于redis 选举的文章了, 原文链接 Raft协议是用来解决分布式系统一致性问题的协议,在很长一段时间,Paxos被认为是解决分布式系统一致性的代名词.但是Paxos难 ...

  2. MIT-6.824 Raft协议

    摘要 raft是一种比paxos容易理解的一致性算法,实现起来比paxos简单许多.本文前部分描述算法的细节,后部分尝试探讨下该算法的原理. 算法描述 raft算法之所以简单的原因之一是它将问题分解成 ...

  3. Raft协议学习笔记

    目录 目录 1 1. 前言 1 2. 名词 1 3. 什么是分布式一致性? 3 4. Raft选举 3 4.1. 什么是Leader选举? 3 4.2. 选举的实现 4 4.3. Term和Lease ...

  4. [搜狐科技]由浅入深理解Raft协议

    由浅入深理解Raft协议 2017-10-16 12:12操作系统/设计 0 - Raft协议和Paxos的因缘 读过Raft论文<In Search of an Understandable ...

  5. Paxos、ZAB、RAFT协议

    这三个都是分布式一致性协议,ZAB基于Paxos修改后用于ZOOKEEPER协议,RAFT协议出现在ZAB协议之后,与ZAB差不多,也有很大区别. 1. Paxos 分布式节点分为3种角色, Prop ...

  6. Paxos算法与Zookeeper分析,zab (zk)raft协议(etcd) 8. 与Galera及MySQL Group replication的比较

    mit 分布式论文集 https://github.com/feixiao/Distributed-Systems wiki上描述的几种都明白了就出师了 raft 和 zab 是类似的,都是1.先选举 ...

  7. RocketMQ 多副本前置篇:初探raft协议

    目录 1.Leader选举 1.1 一轮投票中,只有一个节点发起投票的情况 1.2 一轮投票中,超过一个节点发起投票的情况 1.3 思考如何实现Raft选主 2.日志复制 Raft协议是分布式领域解决 ...

  8. 基于 raft 协议的 RocketMQ DLedger 多副本日志复制设计原理

    目录 1.RocketMQ DLedger 多副本日志复制流程图 1.1 RocketMQ DLedger 日志转发(append) 请求流程图 1.2 RocketMQ DLedger 日志仲裁流程 ...

  9. raft协议-分布式环境下的数据一致性问题

    阅读了一个有意思的ppt,是Standford大学发表的raft协议 网址:http://thesecretlivesofdata.com/raft/ 下面自己总结下咯: 1.raft是一个实现了解决 ...

  10. DLedger —基于 raft 协议的 commitlog 存储库

    “点击获取上云帮助文档” 尊敬的阿里云用户: 您好!为方便您试用开源 RocketMQ 客户端访问阿里云MQ,我们申请了专门的优惠券,优惠券可以直接抵扣金额.请填写下您公司账号信息,点击上图,了解更多 ...

随机推荐

  1. 使用Apache的反向代理会影响搜索引擎的收录和排名吗

    http://www.wocaoseo.com/thread-292-1-1.html 百度官方观点:Baiduspider对站点的抓取方式和普通用户访问一样,只要普通用户能访问到的内容,我们就能抓取 ...

  2. 网站seo优化有什么优缺点

    http://www.wocaoseo.com/thread-94-1-1.html       seo是什么?这个可能是刚刚知道网络营销或搜索引擎营销的朋友们问的话,笔者在这里装一下,呵呵.说真的现 ...

  3. 【Android】listview 嵌套gridview报错,代码:”during second layout pass: posting in next frame

    作者:程序员小冰,CSDN博客:http://blog.csdn.net/qq_21376985, QQ986945193 公众号:程序员小冰 说明:本人曾经在listview嵌套gridview出现 ...

  4. Unity3D中可重载虚函数的总结

    重载虚函数:Unity3D中所有控制脚本的基类MonoBehaviour有一些虚函数用于绘制中事件的回调,也可以直接理解为事件函数,例如大家都很清楚的Start,Update等函数,以下做个总结. A ...

  5. 初识ABP vNext(8):ABP特征管理

    Tips:本篇已加入系列文章阅读目录,可点击查看更多相关文章. 目录 前言 开始 定义特征 应用特征 用户数量 社交登录 最后 前言 上一篇提到了ABP功能管理(特征管理),它来自ABP的Featur ...

  6. 利用css3 transform实现一个时钟

    transform:rotate(1deg) <!DOCTYPE html> <html lang="en"> <head> <meta ...

  7. 微信小程序实现滚动视频自动播放(未优化)

    先看看大概效果 1.首先需要了解微信API:          wx.createIntersectionObserver(Object component, Object options) 创建并返 ...

  8. jenkins打包java项目缺少jar包问题解决

    java项目在使用jenkins打包时个别jar包可能会没有下载到本地,这时候就要用命令行本地安装一下 打包时基础jar包报错如图: 黑框里依次为: 组ID:-DgroupId=com.azazar ...

  9. Ubuntu中的launcher

    最近在ubuntu系统中下载了最新版的eclipse,在一个临时文件夹中解压了eclipse压缩包,然后打开eclipse,按平时常规做法,我在launcher里右键点击eclipse,选择“锁定到启 ...

  10. 《Head First 设计模式》:组合模式

    正文 一.定义 组合模式允许你将对象合成树形结构来表现"整体/部分"层次结构.组合能让客户以一致的方式处理组合对象以及个体对象. 组合对象:包含其他组件的组件. 个体对象(叶节点对 ...