分布式系统的一致性算法

"如何解决集群内共识的问题"

Posted by yueLng on 2017-11-30

一致性协议或共识算法所针对的问题,简单来说就是要保证

即使发生网络或节点异常,整个集群依然能够像单机一样提供一致的服务,即在每次成功操作时都可以看到其之前的所有成功操作按顺序完成。
用 2PC 是因为要支持分布式事务,用 Raft 是要支持强一致的复制。这两个解决的不是一个问题 — 申砾

故障模型

  • Crash-stop Failures:故名思议,一旦发生故障,节点就停止提供服务,并且不会恢复。这种故障模型中的节点都按照正确的逻辑运行,可能宕机,可能网络中断,可能延迟增加,但结果总是正确的;
  • Crash-recovery Failures:相对于crash-stop failures,这种故障模型允许节点在故障发生后恢复,恢复时可能需要一些持久化的数据恢复状态(Omission Failures);
  • Byzantine Failures:这种故障模型需要处理拜占庭问题,因此也是最难应对的,相对于上面的两种故障模型,不仅仅节点宕机或网络故障会发生,节点还有可能返回随机或恶意的结果,甚至有可能影响其他节点的正常运行。

Paxos、Raft基于Crash-recovery Failures,因此适用于安全的网络环境,被在内网服务的存储或协调服务大量使用;PBFT(Practical Byzantine Fault Tolerance)和区块链则是基于Byzantine Failures,面向更复杂的网络环境。

Raft

Raft 使用的是 Log Replication + State Machine 的方式来处理分布式数据的一致性问题,这也是现在的通用做法

  1. Leader Election
    如何保证任何时候最多只有一个Leader节点,以及当Leader节点异常时如何尽快的选择出新的Leader节点。这里由raft的状态机保证

  2. Log Replication

    为了能保证后续Leader节点变化后依然能够使得整个集群对外保持一致

  • Follower以与Leader节点相同的顺序依次执行每个成功提案;
  • 每个成功提交的提案必须有足够多的成功副本,来保证后续的访问一致

上图描述了一个raft提案的执行过程

  • Leader收到Client的请求,写入本地Log,之后并行地向所有Follower通过AppendEntry请求发送该Log Entry;
  • Follower对收到的Entry进行验证,包括验证其之前的一条Log Entry项是不是和Leader相同,验证成功后写入本地Log并返回Leader成功;
  • Leader收到超过半数的Follower答复成功后,将当前Log Commit(如写入状态机),之后返回客户端成功;
  • 后续的AppendEntry及HeartBeat都会携带主的Commit位置,Follower会提交该位置之前的所有Log Entry。
  1. Safety
  • Raft限制新Leader一定是当前Log最新的节点,即其拥有最多最大term的Log Entry
  • 我们在写入数据的时候,一定会保证至少 quorum 的节点都成功被写入了数据,才会认为这次写入是成功的。
  1. other memo

三个节点允许一个节点失败,五个节点允许两个节点失败

所有操作都是通过Leader节点,所以etcd可能会有单点问题?

raft节点的状态转换

为什么raft算法在确定可用节点数量时不用考虑拜占庭将军问题

拜占庭问题中提出,允许n个节点宕机还能提供正常服务的分布式架构,需要的总节点数量为3n+1,而Raft只需要2n+1就可以了。其主要原因在于,拜占庭将军问题中存在数据欺骗的现象,而etcd中假设所有的节点都是诚实的。etcd在竞选前需要告诉别的节点自身的term编号以及前一轮term最终结束时的index值,这些数据都是准确的,其他节点可以根据这些值决定是否投票。另外,etcd严格限制Leader到Follower这样的数据流向保证数据一致不会出错。

参考资料

raft动画
The Raft Consensus Algorithm
寻找一种易于理解的一致性算法(论文中文版)
Raft和它的三个子问题
深入浅出 Raft - Membership Change
深入浅出 Raft - 基本概念
深入浅出 Raft - Optimization
深入浅出 Raft - Leader 选举
Raft 深入浅出 Raft - 基本概念
go-raft实现 – 再见
etcd/raft at master · coreos/etcd · GitHub
GitHub - hashicorp/raft: Golang implementation of the Raft consensus protocol
从Paxos到区块链