摘要:简单介绍Raft协议的原理、以及存储节点(Pinetree)如何应用 Raft实现复制的一些工程实践经验。

1、引言

在华为分布式数据库的工程实践过程中,我们实现了一个计算存储分离、 底层存储基于Raft协议进行复制的分布式数据库系统原型。下面是它的架构图。

计算节点生成日志经过封装后通过网络下发到存储节点,在Raft层达成一致后日志被应用到状态机wal Engine,完成日志的回放和数据的存储管理。

下面简单介绍一下Raft的原理、以及存储节点(Pinetree)如何应用 Raft实现复制的一些工程实践经验。

2、Raft的原理

2.1 Raft的基本原理

Raft 算法一切以领导者为准,实现一系列值的共识和各节点日志的一致。下面重点介绍一下Raft协议的Leader选举、log复制 和 成员变更。

Raft的选举机制:

协议为每个节点定义了三个状态:Leader、Candidate、Follower,将时间定义为Term,每个Term有一个ID。Term类似逻辑时钟,在每个Term都会有Leader被选举出来。

Leader负责处理所有的写请求、发起日志复制、定时心跳,每个Term期间最多只能有一个Leader,可能会存在选举失败的场景,那么这个Term内是没有Leader。

Follower 处于被动状态,负责处理Leader发过来的RPC请求,并且做出回应。

Candidate 是用来选举一个新的Leader,当Follower超时,就会进入Candidate状态。

初始状态,所有的节点都处于Follower状态,节点超时后,递增current Term进入Candidate,该节点发送广播消息RequestVote RPC给其他Follower请求投票。当收到多数节点的投票后,该节点从Candidate进入Leader。Follower在收到投票请求后,会首先比较Term,然后再比较日志index,如果都满足则更新本地Current Term然后回应RequestVote RPC为其投票。每个Term期间,follower只能投一次票。

Raft的日志同步机制:

当Leader被选举出来后,就可以接受写请求。每个写请求即代表了用户需要复制的指令或Command。Raft协议会给写请求包装上Term和Index,由此组成了Raft的Log entry. Leader把Log entry append到日志中,然后给其它的节点发AppendEntries RPC请求。当Leader确定一个Log entry被大多数节点已经写入日志当中,就apply这条Log entry到状态机中然后返回结果给客户端。

Raft成员变更机制:

成员变更就意味着集群节点数的增加或减少以及替换。Raft协议定义时考虑了成员变更的场景,从而避免由于集群变化引起的系统不可用。Raft是利用上面的Log Entry和一致性协议来实现该功能。成员的变更也是由Leader发起的,Leader会在本地生成一个新的Log entry,同时将Log entry推送到其他的Follower节点。

Follower节点收到Log entry后更新本地日志,并且应用该log中的配置关系。多数节点应用后,Leader就会提交这条变更log entry。还要考虑新就配置的更替所带来的问题。更详细的不再赘述。

2.2 Raft的开源实现

Raft的实现有coreos的etcd/raft、kudu、consul、logcabin、cockroach等。

Etcd 、LogCabin 、Consul 实现的是单个Raft环,无法做到弹性伸缩。而kudu和cockroach实现了多个raft环。kudu的consensus 模块实现了副本的数据复制一致性,kudu将数据分片称之为Tablet, 是kudu table的水平分表,TabletPeer就是在Raft环里面的一个节点. 一个Tablet相当于一个Raft环,一个Tablet对应一个Raft Consensus,这些对应Raft里面的一个环,Consensus Round相当于同步的消息,一个环之间会有多个Consensus Round做同步。而cockroach则是基于etcd/raft实现的多Raft环,它维护了多个Raft实例,被称之为multiraft。

因为Etcd的Raft是目前功能较全的Raft实现之一,最早应用于生产环境,并且做到了很好的模块化。其中Raft内核部分实现了Raft大部分协议,而对外则提供了storage和transport所用的interface,对于使用者可以单独实现灵活性较高,用户还可以自主实现 snapshot、wal ,Raft非常便于移植和应用,因此存储节点Pinetree采用了开源的Etcd中的Raft实现来构建我们的原型系统,也便于后期向Multiraft演进。。

3、工程实践

3.1 实现Raft的存储接口和网络传输

Raft存储部分指的是raft- log的存储,是对日志条目进行持久化的存储,通过benchmark测试发现,raft-log引擎性能是影响整体ops的主要瓶颈,为了更灵活的支持底层存储引擎的快速替换,增加可插拔的存储引擎框架,我们对底层存储引擎进行解耦。Pinetree封装了第三方独立存储接口来适配etcd raft的log存储接口;

通讯部分即Raft Transport、snapShot传输等,采用GRPC+Protobuf来实现,心跳、日志传输AppendEntries RPC、选举RequestVote RPC等应用场景将GRPC设置为简单式,snapShot设置为流式的形式。

3.2 选举问题

Raft可以实现自我选举。但是在实践中发现缺点也很明显,Raft自主选主可能存在如下的问题:

1、不可控:可能随意选择一个满足Raft条件的节点

2、网络闪断导致Leader变动

3、节点忙导致的Leader变动

4、破坏性的节点

为了防止存储节点Leader在不同的AZ或者节点间进行切换,Pinetree采用的方案是由集群管理模块来指定 Leader。Pinetree中将electionTimeout设置为无穷大,关闭Follower可能触发的自动选举过程,一切选举过程由集群管理的建议选主模块来控制。

3.3 读一致性模型

在 Raft 集群中,一般会有 default、consistent、stale 三种一致性模型,如何实现读操作关乎一致性的实现。一般的做法是将一致性的选择权交给用户,让用户根据实际业务特点,按需选择,灵活使用。

Consistent具有最高的读一致性,但是实现上要求所有的读请求都要走一遍Raft 内核并且将会与写操作串行,会给集群造成一定的压力。stale具有很好的性能优势,但是读操作可能会落到数据有延迟的节点上。在Pinetree的设计中,集群管理负责维护存储节点的信息,管理所有节点的Raft主副本的状态,一方面可以对读请求进行负载均衡,另一方面可以根据AZ亲和性、副本上的数据是否有最新的log 来路由读请求。这样在性能和一致性之间进行了最大的tradeoff。

3.4 日志问题

Raft以Leader为中心进行复制需要考虑几个问题:

1、性能问题,如果leader为慢节点会导致长尾

2、日志的同步必须是有序提交

3、切换leader时有一段时间的不可用

问题3我们通过集群管理来最大程度的防止Leader的切换。

对于问题2,因为Pinetree的日志类似innodb的redo log ,采用LSN来编号的,所以应用到Pinetree存储层的的日志必须要保序,不能出现跳过日志段或日志空洞的情况。这就要求发给Raft的日志要做保序处理。计算层产生的wal log都对应一个LSN,LSN代表的是日志在文件中的偏移量,具有单调递增且不连续的特点。因此要求Wal log产生的顺序和apply到pinetree storage的顺序要保证一致。为了满足这一需求,我们在计算层和Raft层中间增加一个适配层,维护一个队列负责进行排序,同时为了应对计算层主备的切换,对消息增加Term以保证日志不会乱序。Raft指令还可能会被重复提交和执行,所以存储层要考虑幂等性的问题。因为Pinetree storage的日志用LSN进行编号,所以可以进行重复apply。

3.5 如何解决假主问题

计算节点需要获取某些元数据信息,每次都必须从Leader中读取数据防止出现备机延迟。在网络隔离的情况下,老的leader不会主动退出,会出现双主的情况,这个假主可能永远不知道自己其实已经不是真正的Raft主节点,导致真Leader和假Leader同时存在并提供读服务,这在无延迟系统是不允许的。

如果每次读请求都走一遍Raft协议可以识别出假主,但是将会严重的影响系统的性能。

Pinetree是通过租约(lease)的方式,让一个Pinetree主节点在提供服务之前,保守地检查自身在这一时刻是否拥有lease,再决定自身能不能提供读服务。因此,就算访问了一个Pinetree假主,假主也因为没有lease而不能提供服务。

3.6 性能问题

涉及到性能Pinetree考虑和优化的地方:

1 如果使用 Raft 算法 保证强一致性,那么读写操作都应该在领导者节点上进行。这样的话,读的性能相当于单机,不是很理想, 优化实现了基于leader+lease的方式来提供读服务即能保证一致性又不影响性能。

2 优化raft参数 :in-flight的数目;transport queue的数量

3 最大限度的异步化,例如:指令在raft达成一致完成持久化后传递给状态机存入消息队列立即返回,后续对消息进行异步并行解析。

4 最大限度的进行Batch和Cache。例如:把一个事务内的写操作缓存到客户端,在事务提交时,再把所有的写打包成一个batch与事务commit请求一起发送给服务端

#DevRun开发者沙龙# 9月15日20:00-21:00,特邀华为云数据库解决方案专家Sugar,为您打造专场直播“端到端安全可信,华为云数据库解决方案最佳实践”!华为云数据库服务,聚焦互联网、车企、金融、游戏、ISV、地图等行业痛点,满足企业用户多样性计算需求。提供端到端安全可信的解决方案,帮助企业应用全面云化和智能化。欢迎点击直播(http://live.vhall.com/206537223)围观,社区互动(https://bbs.huaweicloud.com/forum/thread-76193-1-1.html)有礼!

实践案例丨基于 Raft 协议的分布式数据库系统应用的更多相关文章

  1. 实践案例丨基于ModelArts AI市场算法MobileNet_v2实现花卉分类

    概述 MobileNetsV2是基于一个流线型的架构,它使用深度可分离的卷积来构建轻量级的深层神经网,此模型基于 MobileNetV2: Inverted Residuals and Linear ...

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

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

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

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

  4. 实践案例丨教你一键构建部署发布前端和Node.js服务

    如何使用华为云服务一键构建部署发布前端和Node.js服务 构建部署,一直是一个很繁琐的过程 作为开发,最害怕遇到版本发布,特别是前.后端一起上线发布,项目又特别多的时候. 例如你有10个项目,前后端 ...

  5. 实践案例丨利用小熊派开发板获取土壤湿度传感器的ADC值

    摘要:一文带你用小熊派开发板动手做土壤湿度传感器. 一.实验准备 1.实验环境 一块stm32开发板(推荐使用小熊派),以及数据线 已经安装STM32CubeMX 已经安装KeilMDK,并导入stm ...

  6. raft协议

    一.Raft一致性算法 Eureka:Peer To Peer,每个节点的地位都是均等的,每个节点都可以接收写入请求,每个节点接收请求之后,进行请求打包处理,异步化延迟一点时间,将数据同步给 Eure ...

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

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

  8. 基于Raft构建弹性伸缩的存储系统的一些实践

    基于Raft构建弹性伸缩的存储系统的一些实践 原创 2016-07-18 黄东旭 聊聊架构 最近几年来,越来越多的文章介绍了 Raft 或者 Paxos 这样的分布式一致性算法,但主要集中在算法细节和 ...

  9. Quartz应用实践入门案例二(基于java工程)

    在web应用程序中添加定时任务,Quartz的简单介绍可以参看博文<Quartz应用实践入门案例一(基于Web应用)> .其实一旦学会了如何应用开源框架就应该很容易将这中框架应用与自己的任 ...

随机推荐

  1. Homekit_Dohome_智能插座

    简介: 本款智能插座有三个版本可供选择,分别为Homekit版本,涂鸦版本,Dohome版本,各个版本的区别如下: DoHome版特点: 支持HomeKit 支持Amazon 支持Google ...

  2. MetadataCache更新

    MetadataCache什么时候更新 updateCache方法用来更新缓存的. 发起线程 controller-event-thread controller选举的时候 CLASS_NAME ME ...

  3. OpenCV Error - Core.hpp header must be compiled as C++

    在XCode 里编译OpenCV的时候,经常报如题类似的错误. 简单解决办法: 把 *.m 文件重命名为 *.mm 即可

  4. C/C++ 程序反调试的方法

    C/C++ 要实现程序反调试有多种方法,BeingDebugged,NtGlobalFlag,ProcessHeap,CheckRemoteDebuggerPresent,STARTUPINFO,Is ...

  5. 国人开源了一款超好用的 Redis 客户端,真香!!

    大家都知道,Redis Desktop Manager 是一款非常好用的 Redis 可视化客户端工具,但可惜的是 v0.9.4 版本之后需要收费了: 这个工具不再免费提供安装包了,要对所有安装包收费 ...

  6. ISO8601

    日期和时间的组合表示法 合并表示时,要在时间前面加一大写字母T,如要表示东八区时间2004年5月3日下午5点30分8秒,可以写成2004-05-03T17:30:08+08:00或20040503T1 ...

  7. SPSSAU新功能上线:高级公式、综合得分一键计算!

    一直关注我们的朋友们一定会发现,近期SPSSAU增添了很多新功能. 我们精挑细选出6个最常使用的功能,介绍给大家,看看这些新功能你有没有解锁成功呢? 01 一键删除无效样本 “无效样本”功能中,添加了 ...

  8. 区块链入门到实战(18)之以太坊(Ethereum) – 什么是智能合约

    作用:提供优于传统合约的安全方法,并减少与合约相关的其他交易成本. 以太坊网络基石:以太坊虚拟币和智能合约. 智能合约(Smart contract )是一种旨在以信息化方式传播.验证或执行合同的计算 ...

  9. ElasticSearch生命周期管理-索引策略配置与操作

    概述 本文是在本人学习研究ElasticSearch的生命周期管理策略时,发现官方未提供中文文档,有的也是零零散散,此文主要是翻译官方文档Policy phases and actions模块. 注: ...

  10. 01 . etcd简介原理,应用场景及部署,简单使用

    etcd简介 Etcd是CoreOS团队于2013年6月发起的开源项目,他的目标是构建一个高可用的分布式键值(key-value)数据库,etcd内部采用raft协议作为一致性算法,etcd基于Go语 ...