摘要:简略介绍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)有礼!