这是我参与「第四届青训营 」笔记创作活动的第21天
什么是分布式一致性
在分布式系统中,为了消除单点提高系统可用性,通常会使用副本来进行容错,但这会带来另一个问题,即如何保证多个副本之间的一致性?
所谓的强一致性(线性一致性)并不是指集群中所有节点在任一时刻的状态必须完全一致,而是指一个目标,即让一个分布式系统看起来只有一个数据副本,并且读写操作都是原子的,这样应用层就可以忽略系统底层多个数据副本间的同步问题。也就是说,我们可以将一个强一致性分布式系统当成一个整体,一旦某个客户端成功的执行了写操作,那么所有客户端都一定能读出刚刚写入的值。即使发生网络分区故障,或者少部分节点发生异常,整个集群依然能够像单机一样提供服务。
共识算法(Consensus Algorithm)就是用来做这个事情的,它保证即使在小部分(≤ (N-1)/2)节点故障的情况下,系统仍然能正常对外提供服务。共识算法通常基于状态复制机(Replicated State Machine)模型,也就是所有节点从同一个 state 出发,经过同样的操作 log,最终达到一致的 state。
共识算法是构建强一致性分布式系统的基石,Paxos 是共识算法的代表,而 Raft 则是其作者在博士期间研究 Paxos 时提出的一个变种,主要优点是容易理解、易于实现,甚至关键的部分都在论文中给出了伪代码实现。
Paxos 协议与Raft协议
Paxos 协议 Paxos有个很特别的就是协调者(proposer)只需等到超过1/2(多数派)的节点同意而不是全部节点,这样只有当1/2的节点同时出现故障整个系统才会有问题,加上同时这个限定条件后,这个系统的故障概率是极低极低的。
Raft协议 是目前工业界广泛使用的分布式一致性协议, 被广泛应用在分布式系统中, 例如 ETCD、TiKV、Consul 等著名开源软件都使用了 raft 协议来实现分布式系统中的强一致性, paxos 算法作为分布式一致性算法的鼻祖, 其以难以理解和很难工程化著称, raft 的作者希望设计一种更简洁的算法来替代 paxos, 使其在保证正确性和可靠性的前提下能够容易理解和实现。 Raft算法是对Paxos算法的简化和改进。
Raft 基本概念
Raft 使用 Quorum 机制来实现共识和容错,我们将对 Raft 集群的操作称为提案,每当发起一个提案,必须得到大多数(> N/2)节点的同意才能提交。
那么当我们向 Raft 集群发起一系列读写操作时,集群内部究竟发生了什么
首先,Raft 集群必须存在一个主节点(leader),我们作为客户端向集群发起的所有操作都必须经由主节点处理。所以 Raft 核心算法中的第一部分就是选主(Leader election) ——没有主节点集群就无法工作,先票选出一个主节点,再考虑其它事情。
其次,主节点需要承载什么工作呢?它会负责接收客户端发过来的操作请求,将操作包装为日志同步给其它节点,在保证大部分节点都同步了本次操作后,就可以安全地给客户端回应响应了。这一部分工作在 Raft 核心算法中叫日志复制(Log replication)。
然后,因为主节点的责任是如此之大,所以节点们在选主的时候一定要谨慎,只有符合条件的节点才可以当选主节点。此外主节点在处理操作日志的时候也一定要谨慎,为了保证集群对外展现的一致性,不可以覆盖或删除前任主节点已经处理成功的操作日志。所谓的“谨慎处理”,其实就是在选主和提交日志的时候进行一些限制,这一部分在 Raft 核心算法中叫安全性(Safety) 。 Raft 核心算法其实就是由这三个子问题组成的:选主(Leader election)、日志复制(Logreplication)、安全性(Safety)。这三部分共同实现了 Raft 核心的共识和容错机制。
除了核心算法外,Raft 也提供了几个工程实践中必须面对问题的解决方案。
第一个是关于日志无限增长的问题。Raft 将操作包装成为了日志,集群每个节点都维护了一个不断增长的日志序列,状态机只有通过重放日志序列来得到。但由于这个日志序列可能会随着时间流逝不断增长,因此我们必须有一些办法来避免无休止的磁盘占用和过久的日志重放。这一部分叫日志压缩(Log compaction)。
第二个是关于集群成员变更的问题。一个 Raft 集群不太可能永远是固定几个节点,总有扩缩容的需求,或是节点宕机需要替换的时候。直接更换集群成员可能会导致严重的脑裂问题。Raft 给出了一种安全变更集群成员的方式。这一部分叫集群成员变更(Cluster membership change)。
一些基本概念