我爱编程

分布式一致性协议在MongoDB选举中的应用

2018-01-16  本文已影响0人  cb9e58ff5a37

前言

之前在学习MongoDB复制集时发现网上的很多相关的分享都是针对r3.2.0以前的版本,新版本对选举机制做了较大的更改,但是在网上大多都是一笔带过。于是写过一篇《MongoDB选举机制》。当时主要结合了官网最新的文档和部分源码。由于官网介绍比较泛泛,源码阅读的时候又受限于个人能力与时间,最终只整理了选举相关的核心逻辑,对应复杂的条件检查和数据结构没有深入的了解。最近了解了一些分布式一致性算法之后,打算换一个角度从协议层面重新整理一下MongoDB的选举。本文简要介绍分布式系统一致性相关的概念,重点介绍Bully算法和Raft算法在MongoDB选举中的应用。

分布式系统简介

分布式系统是一个硬件或软件组件分布在不同的网络计算机上,彼此之间仅仅通过消息传递进行通信和协调的系统。分布式系统具有分布性、对等性、并发性、缺乏全局时钟、故障总会发生的特点,由于这些特点导致了分布式系统中的一致性难题。

CAP理论

分布式系统的CAP理论:

CAP理论告诉我们,一个分布式系统不可能同时满足一致性(C),可用性(A),分区容错性(P)这三个基本需求,最多只能同时满足其中两项。

由于分区容错性是分布式系统首先要保证的,所以只能在一致性和可用性之间找平衡。

BASE理论

BASE是Basically Available(基本可用)、Soft state(软状态)和Eventually consistent(最终一致)三个短语的简写,基于CAP发展而来,主要解决一致性和可用性之间的平衡。

Paxos,Raft,ZAB等常见的分布式一致性算法都是符合BASE理论。

MongoDB的选举机制

MongoDB复制集的目的是提供高可用性,如果一个写操作被复制到大多数节点,只要任意大多数节点可用,那么数据就是可靠的。复制集一般包含一个Primary和若干个Secondary节点。选举机制主要负责在集群初始化及节点发生变化时重新选举主节点,保证系统可用性。

MongoDB节点之间维护心跳检查,主节点选举由心跳触发。MongoDB复制集成员会向自己之外的所有成员发送心跳并处理响应信息,因此每个节点都维护着从该节点POV看到的其他所有节点的状态信息。节点根据自己的集群状态信息判断是否需要发起选举。

MongoDB r3.2.0以前选举协议是基于Bully算法,从r3.2.0开始默认使用基于Raft算法的选举策略。MongoDB选举过程比较复杂,下面的在介绍算法应用时重点关注与算法应用相关的逻辑。

Bully算法

算法描述

Bully算法是一种相对简单的协调者竞选算法。算法的主要思想是集群的每个成员都可以声明它是协调者并通知其他节点。别的节点可以选择接受这个声称或是拒绝并进入协调者竞争。被其他所有节点接受的节点才能成为协调者。节点按照一些属性来判断谁应该胜出。

Bully算法中有三类消息:

当一个节点从故障中恢复或者监测到主节点故障时,执行过程如下:

举例

下面是Bully算法选举过程的一个经典例子:

image

算法应用

MongoDB在实现时对Bully算法做了一些调整:

Raft算法

新版本的MongoDB用Raft取代了Bully。

算法描述

Raft将问题拆成数个子问题分开解决:

这里我们主要关注领袖选举。
Raft cluster中为服务器定义了三种角色:

在正常情况下,集群中只有一个 leader 并且其他的节点全部都是 follower 。Follower 都是被动的:他们不会发送任何请求,只是简单的响应来自 leader 和 candidate 的请求。三种角色的转换关系:

image

Raft用Term区分每一次任期(包含一次选举),Term在Raft中起到逻辑时钟(时序)的作用,Term是单调递增的。每个节点都保存当前的Term,并在服务器间通信中包含Term字段。
Raft为实现一致性定义了两种基本RPC:

Raft 用心跳来触发 leader 选举。当服务器程序启动时,他们都是 follower 。一个服务器节点只要能从 leader 或 candidate 处接收到有效的 RPC 就一直保持 follower 状态。Leader 周期性地向所有 follower 发送心跳(不包含日志条目的 AppendEntries RPC)来维持自己的地位。如果一个 follower 在一段选举超时时间内没有接收到任何消息,它就假设系统中没有可用的 leader ,然后开始进行选举以选出新的 leader 。

选举规则中有一些限制条件:

下面详细的介绍一下选举的过程:

Candidate 赢得选举
当一个 Candidate 获得集群中过半服务器节点针对同一个任期的投票,它就赢得了这次选举并成为 Leader 。对于同一个任期,每个服务器节点只会投给一个 Candidate ,按照先来先服务的原则。一旦 Candidate 赢得选举,就立即成为 Leader 。然后它会向其他的服务器节点发送心跳消息来确定自己的地位并阻止新的选举。

其他节点成为Leader
在等待投票期间,Candidate 可能会收到另一个声称自己是 leader 的服务器节点发来的 AppendEntries RPC 。如果这个 Leader 的任期号(包含在RPC中)不小于 candidate 当前的任期号,那么 Candidate 会承认该 Leader 的合法地位并回到 Follower 状态。 如果 RPC 中的任期号比自己的小,那么 Candidate 就会拒绝这次的 RPC 并且继续保持 Candidate 状态。

没有获胜者
第三种可能的结果是 Candidate 既没有赢得选举也没有输:如果有多个 follower 同时成为 candidate ,那么选票可能会被瓜分以至于没有 candidate 赢得过半的投票。当这种情况发生时,每一个候选人都会超时,然后通过增加当前任期号来开始一轮新的选举。然而,如果没有其他机制的话,该情况可能会无限重复。

举例

这里还是用Bully算法中的案例来说明:

算法应用

MongoDB在实现时对Raft算法做了一些调整:

对比

对比新旧版本的选举:

参考

深入理解NoSQL数据库分布式算法及策略
维基百科-Bully algorithm
《从Paxos到ZooKeeper 分布式一致性原理与实践》
《Time, Clocks, and the Ordering of Events in a Distributed System》
《MongoDB 中的Raft一致性协议》

上一篇 下一篇

猜你喜欢

热点阅读