前言

在Redis集群中我们讲到了,主机断开后,我们得手动设置另一个从机变成主机!这是不智能的!在实际工作中,我们都是用哨兵模式来自动切换主机。通俗点讲,就是自己去选择‘大哥’!

概述

主从切换技术的方法是:当主服务器宕机后,需要手动把一台从服务器切换为主服务器,这就需要人工干预,费事费力,还会造成一段时间内服务不可用。这不是一种推荐的方式,更多时候,我们优先考虑 哨兵模式 。Redis从2.8开始正式提供了Sentinel(哨兵) 架构来解决这个问题。

谋朝篡位 的自动版,能够后台监控主机是否故障,如果故障了根据投票数自动将从库转换为主库。

哨兵模式是一种特殊的模式,首先Redis提供了哨兵的命令,哨兵是一个独立的 进程 ,作为进程,它会独立运行。其原理是哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例。

配置哨兵

  1. 添加哨兵配置文件 sentinel.conf

20210126163100824

内容如下:

1
2
# sentinel monitor 被监控的名称 host port 1 (代表自动投票选举大哥!)
sentinel monitor myredis 127.0.0.1 6379 1
  1. 启动哨兵

命令:

1
redis-sentinel dyjConfig/sentinel.conf   #和启动Redis一致

启动成功后如下图:

image-20230131174831462
  1. 前提准备条件:

开启一台主机,两台从机,一主二从时最基本的!

image-20230131175108812 image-20230131175120036 image-20230131175131411
  1. 测试主机宕机后自动选取大哥,如果主机此时回来了,只能归并到新的主机下,当做从机,这就是哨兵模式的规则!
image-20230131175152317

等待哨兵的默认配置时间时 30 秒!

image-20230131175231450

再次查看redis信息:

image-20230131175250725

可以发现8381变成主机,8380依旧是从机!

我们将老大哥主机连接试试!可以发现6379变成从机了,由大哥变为小弟!

image-20230131175313231

而6381成功成为主机大哥大!

image-20230131175339993

Sentinel 优化配置

​ 在公共的 sentinel.conf 文件中,还可以通过修改一些其它属性的值来达到对 Sentinel 的配置优化。

  1. sentinel down-after-milliseconds

image-20250508135013855

​ 每个 Sentinel 会通过定期发送 ping 命令来判断 master、slave 及其它 Sentinel 是否存活。如果 Sentinel 在该属性指定的时间内没有收到它们的响应,那么该 Sentinel 就会主观认为该主机宕机。默认为 30 秒。

  1. sentinel parallel-syncs

image-20250508135238013

​ 该属性用于指定,在故障转移期间,即老的 master 出现问题,新的 master 刚晋升后,允许多少个 slave 同时从新 master 进行数据同步。默认值为 1 表示所有 slave 逐个从新 master进行数据同步。

  1. sentinel failover-timeout

image-20250508135315697

​ 指定故障转移的超时时间,默认时间为 3 分钟。该超时时间的用途很多:

  • 由于第一次故障转移失败,在同一个 master 上进行第二次故障转移尝试的时间为该failover-timeout 的两倍
  • 新 master 晋升完毕,slave 从老 master 强制转到新 master 进行数据同步的时间阈值。
  • 取消正在进行的故障转换所需的时间阈值。
  • 新 master 晋升完毕,所有 replicas 的配置文件更新为新 master 的时间阈值。
  1. sentinel deny-scripts-reconfig

image-20250508135403704

​ 指定是否可以通过命令 sentinel set 动态修改 notification-script 与 client-reconfig-script 两个脚本。默认是不能的。这两个脚本如果允许动态修改,可能会引发安全问题。

  1. 动态修改配置

​ 通过 redis-cli 连接上 Sentinel 后,通过 sentinel set 命令可动态修改配置信息。例如,下面的命令动态修改了 sentinel monitor 中的 quorum 的值。

​ 下表是 sentinel set 命令支持的参数:

image-20250508135500179

哨兵机制原理

三个定时任务

​ Sentinel 维护着三个定时任务以监测 Redis 节点及其它 Sentinel 节点的状态。

  1. info 任务

​ 每个 Sentinel 节点每 10 秒就会向 Redis 集群中的每个节点发送 info 命令,以获得最新的Redis 拓扑结构。

  1. 心跳任务

​ 每个 Sentinel 节点每 1 秒就会向所有 Redis 节点及其它 Sentinel 节点发送一条 ping 命令,以检测这些节点的存活状态。该任务是判断节点在线状态的重要依据。

  1. 发布/订阅任务

​ 每个 Sentinel 节点在启动时都会向所有 Redis 节点订阅_ _sentinel_ _:hello 主题的信息,
当 Redis 节点中该主题的信息发生了变化,就会立即通知到所有订阅者。

​ 启动后,每个 Sentinel 节点每 2 秒就会向每个 Redis 节点发布一条_ _sentinel_ _:hello
题的信息,该信息是当前 Sentinel 对每个 Redis 节点在线状态的判断结果及当前 Sentinel 节点信息。

​ 当 Sentinel 节点接收到_ _sentinel_ _:hello 主题信息后,就会读取并解析这些信息,然后
主要完成以下三项工作:

  • 如果发现有新的 Sentinel 节点加入,则记录下新加入 Sentinel 节点信息,并与其建立连接。
  • 如果发现有 Sentinel Leader 选举的选票信息,则执行 Leader 选举过程。
  • 汇总其它 Sentinel 节点对当前 Redis 节点在线状态的判断结果,作为 Redis 节点客观下线的判断依据。

Redis 节点下线判断

​ 对于每个 Redis 节点在线状态的监控是由 Sentinel 完成的。

  1. 主观下线

​ 每个 Sentinel 节点每秒就会向每个 Redis 节点发送 ping 心跳检测,如果 Sentinel 在down-after-milliseconds 时间内没有收到某 Redis 节点的回复,则 Sentinel 节点就会对该 Redis节点做出"下线状态"的判断。这个判断仅仅是当前 Sentinel 节点的"一家之言",所以称为主观下线。

  1. 客观下线

​ 当 Sentinel 主观下线的节点是 master 时,该 Sentinel 节点会向每个其它 Sentinel 节点发送 sentinel is-master-down-by-addr 命令,以询问其对 master 在线状态的判断结果。这些Sentinel 节点在收到命令后会向这个发问 Sentinel 节点响应 0(在线)或 1(下线)。当 Sentinel收到超过 quorum 个下线判断后,就会对 master 做出客观下线判断。

Sentinel Leader 选举

​ 当 Sentinel 节点对 master 做出客观下线判断后会由 Sentinel Leader 来完成后续的故障转移,即 Sentinel 集群中的节点也并非是对等节点,是存在 Leader 与 Follower 的。

​ Sentinel 集群的 Leader 选举是通过 Raft 算法实现的。Raft 算法比较复杂,后面会详细学习。这里仅简单介绍一下大致思路。

​ 每个选举参与者都具有当选 Leader 的资格,当其完成了"客观下线"判断后,就会立即"毛遂自荐"推选自己做 Leader,然后将自己的提案发送给所有参与者。其它参与者在收到提案后,只要自己手中的选票没有投出去,其就会立即通过该提案并将同意结果反馈给提案者,后续再过来的提案会由于该参与者没有了选票而被拒绝。当提案者收到了同意反馈数量大于等于 max(quorum,sentinelNum/2+1)时,该提案者当选 Leader。

​ 说明:

  • 在网络没有问题的前提下,基本就是谁先做出了"客观下线"判断,谁就会首先发起Sentinel Leader 的选举,谁就会得到大多数参与者的支持,谁就会当选 Leader。
  • Sentinel Leader 选举会在次故障转移发生之前进行。
  • 故障转移结束后 Sentinel 不再维护这种 Leader-Follower 关系,即 Leader 不再存在。

master 选择算法

​ 在进行故障转移时,Sentinel Leader 需要从所有 Redis 的 Slave 节点中选择出新的 Master。其选择算法为:

  1. 过滤掉所有主观下线的,或心跳没有响应 Sentinel 的,或 replica-priority 值为 0 的 Redis节点
  2. 在剩余 Redis 节点中选择出 replica-priority 最小的的节点列表。如果只有一个节点,则直接返回,否则,继续
  3. 从优先级相同的节点列表中选择复制偏移量最大的节点。如果只有一个节点,则直接返回,否则,继续
  4. 从复制偏移值量相同的节点列表中选择动态 ID 最小的节点返回

故障转移过程

Sentinel Leader 负责整个故障转移过程,经历了如上步骤:

  1. Sentinel Leader 根据 master 选择算法选择出一个 slave 节点作为新的 master
  2. Sentinel Leader 向新 master 节点发送 slaveof no one 指令,使其晋升为 master
  3. Sentinel Leader 向新 master 发送 info replication 指令,获取到 master 的动态 ID
  4. Sentinel Leader 向其余 Redis 节点发送消息,以告知它们新 master 的动态 ID
  5. Sentinel Leader 向其余 Redis 节点发送 slaveof <mastIp> <masterPort>指令,使它们成为新master 的 slave
  6. Sentinel Leader 从所有 slave 节点中每次选择出 parallel-syncs 个 slave 从新 master 同步数据,直至所有 slave 全部同步完毕
  7. 故障转移完毕

节点上线

​ 不同的节点类型,其上线的方式也是不同的。

  1. 原 Redis 节点上线

​ 无论是原下线的 master 节点还是原下线的 slave 节点,只要是原 Redis 集群中的节点上线,只需启动 Redis 即可。因为每个 Sentinel 中都保存有原来其监控的所有 Redis 节点列表,Sentinel 会定时查看这些 Redis 节点是否恢复。如果查看到其已经恢复,则会命其从当前master 进行数据同步。

​ 不过,如果是原 master 上线,在新 master 晋升后 Sentinel Leader 会立即先将原 master节点更新为slave,然后才会定时查看其是否恢复。

  1. 新 Redis 节点上线

​ 如果需要在 Redis 集群中添加一个新的节点,其未曾出现在 Redis 集群中,则上线操作只能手工完成。即添加者在添加之前必须知道当前 master 是谁,然后在新节点启动后运行slaveof 命令加入集群。

  1. Sentinel 节点上线

​ 如果要添加的是 Sentinel 节点,无论其是否曾经出现在 Sentinel 集群中,都需要手工完成。即添加者在添加之前必须知道当前 master 是谁,然后在配置文件中修改 sentinel monitor属性,指定要监控的 master。然后启动 Sentinel 即可。

总结

  1. 优点

①哨兵集群,基于主从复制模式 ,所有的主从配置优点,它全有
②主从可以切换,故障可以转移 ,系统的 可用性 就会更好
③哨兵模式就是主从模式的升级,手动到自动,更加健壮!

  1. 缺点

①Redis 不好在线扩容 的,集群容量一旦到达上限,在线扩容就十分麻烦!
②实现哨兵模式的配置其实是很 麻烦 的,里面有很多选择!

  1. 注意点:以上所有的配置因为条件所限都是基于单机集群的前提下!有兴趣的可以自己搭建下正式集群下的多哨兵模式来监控!如下图:
image-20230131175427122
  1. 哨兵的配置文件解析:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# Example sentinel.conf 

# 哨兵sentinel实例运行的端口 默认26379
port 26379

# 哨兵sentinel的工作目录
dir /tmp

# 哨兵sentinel监控的redis主节点的 ip port
# master-name 可以自己命名的主节点名字 只能由字母A-z、数字0-9 、这三个字符".-_"组成。
# quorum 配置多少个sentinel哨兵统一认为master主节点失联 那么这时客观上认为主节点失联了
# sentinel monitor <master-name> <ip> <redis-port> <quorum> sentinel monitor mymaster 127.0.0.1 6379 2

# 当在Redis实例中开启了requirepass foobared 授权密码 这样所有连接Redis实例的客户端都要提供 密码
# 设置哨兵sentinel 连接主从的密码 注意必须为主从设置一样的验证密码
# sentinel auth-pass <master-name> <password>
sentinel auth-pass mymaster MySUPER--secret-0123passw0rd

# 指定多少毫秒之后 主节点没有应答哨兵sentinel 此时 哨兵主观上认为主节点下线 默认30秒
# sentinel down-after-milliseconds <master-name> <milliseconds>
sentinel down-after-milliseconds mymaster 30000

# 这个配置项指定了在发生failover主备切换时最多可以有多少个slave同时对新的master进行 同步
#这个数字越小,完成failover所需的时间就越长,
# 但是如果这个数字越大,就意味着越 多的slave因为replication而不可用。
#可以通过将这个值设为 1 来保证每次只有一个slave 处于不能处理命令请求的状态。
# sentinel parallel-syncs <master-name> <numslaves>
sentinel parallel-syncs mymaster 1


# 故障转移的超时时间 failover-timeout 可以用在以下这些方面:
#1. 同一个sentinel对同一个master两次failover之间的间隔时间。
#2. 当一个slave从一个错误的master那里同步数据开始计算时间。直到slave被纠正为向正确的master那 里同步数据时。
#3.当想要取消一个正在进行的failover所需要的时间。
#4.当进行failover时,配置所有slaves指向新的master所需的最大时间。不过,即使过了这个超时, slaves依然会被正确配置为指向master,但是就不按parallel-syncs所配置的规则来了
# 默认三分钟 # sentinel failover-timeout <master-name>
sentinel failover-timeout mymaster 180000


# SCRIPTS EXECUTION #配置当某一事件发生时所需要执行的脚本,可以通过脚本来通知管理员,例如当系统运行不正常时发邮件通知 相关人员。
#对于脚本的运行结果有以下规则:
#若脚本执行后返回1,那么该脚本稍后将会被再次执行,重复次数目前默认为10 #若脚本执行后返回2,或者比2更高的一个返回值,脚本将不会重复执行。
#如果脚本在执行过程中由于收到系统中断信号被终止了,则同返回值为1时的行为相同。
#一个脚本的最大执行时间为60s,如果超过这个时间,脚本将会被一个SIGKILL信号终止,之后重新执行。
#通知型脚本:当sentinel有任何警告级别的事件发生时(比如说redis实例的主观失效和客观失效等等), 将会去调用这个脚本,这时这个脚本应该通过邮件,SMS等方式去通知系统管理员关于系统不正常运行的信 息。调用该脚本时,将传给脚本两个参数,一个是事件的类型,一个是事件的描述。如果sentinel.conf配 置文件中配置了这个脚本路径,那么必须保证这个脚本存在于这个路径,并且是可执行的,否则sentinel无 法正常启动成功。
#通知脚本
# shell编程
# sentinel notification-script <master-name> <script-path>
sentinel notification-script mymaster /var/redis/notify.sh


# 客户端重新配置主节点参数脚本
# 当一个master由于failover而发生改变时,这个脚本将会被调用,通知相关的客户端关于master地址已 经发生改变的信息。
# 以下参数将会在调用脚本时传给脚本:
# <master-name> <role> <state> <from-ip> <from-port> <to-ip> <to-port>
# 目前<state>总是"failover",
# <role>是"leader"或者"observer"中的一个。
# 参数 from-ip, from-port, to-ip, to-port是用来和旧的master和新的master(即旧的slave)通 信的
# 这个脚本应该是通用的,能被多次调用,不是针对性的。
# sentinel client-reconfig-script <master-name> <script-path>
sentinel client-reconfig-script mymaster /var/redis/reconfig.sh
# 一般都是由运维来配置!

CAP 定理

概念

​ CAP 定理指的是在一个分布式系统中,一致性 Consistency、可用性 Availability、分区容错性 Partition tolerance,三者不可兼得。

  • 一致性(C):分布式系统中多个主机之间是否能够保持数据一致的特性。即,当系统数据发生更新操作后,各个主机中的数据仍然处于一致的状态。
  • 可用性(A):系统提供的服务必须一直处于可用的状态,即对于用户的每一个请求,系统总是可以在有限的时间内对用户做出响应。
  • 分区容错性(P):分布式系统在遇到任何网络分区故障时,仍能够保证对外提供满足一致性和可用性的服务。

定理

​ CAP 定理的内容是:对于分布式系统,网络环境相对是不可控的,出现网络分区是不可避免的,因此系统必须具备分区容错性。但系统不能同时保证一致性与可用性。即要么 CP,要么 AP。

BASE 理论

​ BASE 是 Basically Available(基本可用)、Soft state(软状态)和 Eventually consistent(最终一致性)三个短语的简写,BASE 是对 CAP 中一致性和可用性权衡的结果,其来源于对大规模互联网系统分布式实践的结论,是基于 CAP 定理逐步演化而来的。

BASE 理论的核心思想是:即使无法做到强一致性,但每个系统都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性。

  1. 基本可用

​ 基本可用是指分布式系统在出现不可预知故障的时候,允许损失部分可用性。

  1. 软状态

​ 软状态,是指允许系统数据存在的中间状态,并认为该中间状态的存在不会影响系统的整体可用性,即允许系统主机间进行数据同步的过程存在一定延时。软状态,其实就是一种灰度状态,过渡状态。

  1. 最终一致性

​ 最终一致性强调的是系统中所有的数据副本,在经过一段时间的同步后,最终能够达到一个一致的状态。因此,最终一致性的本质是需要系统保证最终数据能够达到一致,而不需要保证系统数据的实时一致性。

CAP 的应用

​ 下面将生产中常见到的一些中间件与服务器集群的 CAP 特性进行分析。

  1. Zookeeper 与 CAP

​ Zookeeper 遵循的是 CP 模式,即保证了一致性,但牺牲了可用性。

​ 当 Leader 节点中的数据发生了变化后,在 Follower 还没有同步完成之前,整个 Zookeeper集群是不对外提供服务的。如果此时有客户端来访问数据,则客户端会因访问超时而发生重试。不过,由于 Leader 的选举非常快,所以这种重试对于用户来说几乎是感知不到的。所以说,Zookeeper 保证了一致性,但牺牲了可用性。

  1. Consul 与 CAP

​ Consul 遵循的是 CP 模式,即保证了一致性,但牺牲了可用性。

  1. Redis 与 CAP

​ Redis 遵循的是 AP 模式,即保证了可用性,但牺牲了一致性。

  1. Eureka 与 CAP

​ Eureka 遵循的是 AP 模式,即保证了可用性,但牺牲了一致性。

  1. Nacos 与 CAP

​ Nacos 在做注册中心时,默认是 AP 的。但其也支持 CP 模式,但需要用户提交请求进行转换。

Raft 算法

基础

​ Raft 算法是一种通过对日志复制管理来达到集群节点一致性的算法。这个日志复制管理发生在集群节点中的 Leader 与 Followers 之间。Raft 通过选举出的 Leader 节点负责管理日志复制过程,以实现各个节点间数据的一致性。

角色、任期及角色转变

image-20250508142159852

​ 在 Raft 中,节点有三种角色:

  • Leader:唯一负责处理客户端写请求的节点;也可以处理客户端读请求;同时负责日志复制工作
  • Candidate:Leader 选举的候选人,其可能会成为 Leader。是一个选举中的过程角色
  • Follower:可以处理客户端读请求;负责同步来自于 Leader 的日志;当接收到其它Cadidate 的投票请求后可以进行投票;当发现 Leader 挂了,其会转变为 Candidate 发起Leader 选举

leader 选举

​ 通过 Raft 算法首先要实现集群中 Leader 的选举。

  1. 我要选举

​ 若 follower 在心跳超时范围内没有接收到来自于 leader 的心跳,则认为 leader 挂了。此时其首先会使其本地 term 增一。然后 follower 会完成以下步骤:

  • 此时若接收到了其它 candidate 的投票请求,则会将选票投给这个 candidate
  • 由 follower 转变为 candidate
  • 若之前尚未投票,则向自己投一票
  • 向其它节点发出投票请求,然后等待响应
  1. 我要投票

​ follower 在接收到投票请求后,其会根据以下情况来判断是否投票:

  • 发来投票请求的 candidate 的 term 不能小于我的 term
  • 在我当前 term 内,我的选票还没有投出去
  • 若接收到多个 candidate 的请求,我将采取 first-come-first-served 方式投票
  1. 等待响应

​ 当一个 Candidate 发出投票请求后会等待其它节点的响应结果。这个响应结果可能有三种情况:

  • 收到过半选票,成为新的 leader。然后会将消息广播给所有其它节点,以告诉大家我是新的 Leader 了
  • 接收到别的 candidate 发来的新 leader 通知,比较了新 leader 的 term 并不比自己的 term小,则自己转变为 follower
  • 经过一段时间后,没有收到过半选票,也没有收到新 leader 通知,则重新发出选举
  1. 选举时机

​ 在很多时候,当 Leader 真的挂了,Follower 几乎同时会感知到,所以它们几乎同时会变为 candidate 发起新的选举。此时就可能会出现较多 candidate 票数相同的情况,即无法选举出 Leader。

​ 为了防止这种情况的发生,Raft 算法其采用了 randomized election timeouts 策略来解决这个问题。其会为这些 Follower 随机分配一个选举发起时间 election timeout,这个 timeout在 150-300ms 范围内。只有到达了 election timeout 时间的 Follower 才能转变为 candidate,否则等待。那么 election timeout 较小的 Follower 则会转变为 candidate 然后先发起选举,一般情况下其会优先获取到过半选票成为新的 leader。

数据同步

​ 在 Leader 选举出来的情况下,通过日志复制管理实现集群中各节点数据的同步。

  1. 状态机

​ Raft 算法一致性的实现,是基于日志复制状态机的。状态机的最大特征是,不同 Server中的状态机若当前状态相同,然后接受了相同的输入,则一定会得到相同的输出。

image-20250508142653447

  1. 处理流程

image-20250508142720196

​ 当 leader 接收到 client 的写操作请求后,大体会经历以下流程:

  • leader 在接收到 client 的写操作请求后,leader 会将数据与 term 封装为一个 box,并随着下一次心跳发送给所有 followers,以征求大家对该 box 的意见。同时在本地将数据封装为日志
  • follower 在接收到来自 leader 的 box 后首先会比较该 box 的 term 与本地记录的曾接受过的 box 的最大 term,只要不比自己的小就接受该 box,并向 leader 回复同意。同时会将该 box 中的数据封装为日志。
  • 当 leader 接收到过半同意响应后,会将日志 commit 到自己的状态机,状态机会输出一个结果,同时日志状态变为了 committed
  • 同时 leader 还会通知所有 follower 将日志 commit 到它们本地的状态机,日志状态变为了 committed
  • 在 commit 通知发出的同时,leader 也会向 client 发出成功处理的响应
  1. AP 支持

image-20250508142830885

​ Log 由 term index、log index 及 command 构成。为了保证可用性,各个节点中的日志可以不完全相同,但 leader 会不断给 follower 发送 box,以使各个节点的 log 最终达到相同。即 raft 算法不是强一致性的,而是最终一致的。

脑裂

​ Raft 集群存在脑裂问题。在多机房部署中,由于网络连接问题,很容易形成多个分区。而多分区的形成,很容易产生脑裂,从而导致数据不一致。

​ 由于三机房部署的容灾能力最强,所以生产环境下,三机房部署是最为常见的。下面以三机房部署为例进行分析,根据机房断网情况,可以分为五种情况:

  1. 不确定

image-20250508142939795

​ 这种情况下,B 机房中的主机是感知不到 Leader 的存在的,所以 B 机房中的主机会发起新一轮的 Leader 选举。由于 B 机房与 C 机房是相连的,虽然 C 机房中的 Follower 能够感知到 A 机房中的 Leader,但由于其接收到了更大 term 的投票请求,所以 C 机房的 Follower也就放弃了 A 机房中的 Leader,参与了新 Leader 的选举。

​ 若新 Leader 出现在 B 机房,A 机房是感知不到新 Leader 的诞生的,其不会自动下课,所以会形成脑裂。但由于 A 机房 Leader 处理的写操作请求无法获取到过半响应,所以无法完成写操作。但 B 机房 Leader 的写操作处理是可以获取到过半响应的,所以可以完成写操作。故,A 机房与 B、C 机房中出现脑裂,且形成了数据的不一致。

​ 若新 Leader 出现在 C 机房,A 机房中的 Leader 则会自动下课,所以不会形成脑裂。

  1. 形成脑裂

image-20250508143054815

​ 这种情况与情况一基本是一样的。不同的是,一定会形成脑裂,无论新 Leader 在 B 还是 C 机房。

  1. 无脑裂

image-20250508143128191

​ A、C 可以正常对外提供服务,但 B 无法选举出新的 Leader。由于 B 中的主机全部变为了选举状态,所以无法提供任何服务,没有形成脑裂。

  1. 无脑裂

image-20250508143200955

​ A、B、C 均可以对外提供服务,不受影响。

  1. 无脑裂

image-20250508143232209

​ A 机房无法处理写操作请求,但可以对外提供读服务。

​ B、C 机房由于失去了 Leader,均会发起选举,但由于均无法获取过半支持,所以均无法选举出新的 Leader。

Leader 宕机处理

  1. 请求到达前 Leader 挂了

​ client 发送写操作请求到达 Leader 之前 Leader 就挂了,因为请求还没有到达集群,所以这个请求对于集群来说就没有存在过,对集群数据的一致性没有任何影响。Leader 挂了之后,会选举产生新的 Leader。

​ 由于 Stale Leader 并未向 client 发送成功处理响应,所以 client 会重新发送该写操作请求。

  1. 未开始同步数据前 Leader 挂了

​ client 发送写操作请求给 Leader,请求到达 Leader 后,Leader 还没有开始向 Followers发出数据 Leader 就挂了。这时集群会选举产生新的 Leader。Stale Leader 重启后会作为Follower 重新加入集群,并同步新 Leader 中的数据以保证数据一致性。之前接收到 client 的数据被丢弃。

​ 由于 Stale Leader 并未向 client 发送成功处理响应,所以 client 会重新发送该写操作请求。

  1. 同步完部分后 Leader 挂了

​ client 发送写操作请求给 Leader,Leader 接收完数据后向所有 Follower 发送数据。在部分 Follower 接收到数据后 Leader 挂了。由于 Leader 挂了,就会发起新的 Leader 选举。

  • 若 Leader 产生于已完成数据接收的 Follower,其会继续将前面接收到的写操作请求转换为日志,并写入到本地状态机,并向所有 Flollower 发出询问。在获取过半同意响应后会向所有 Followers 发送 commit 指令,同时向 client 进行响应。
  • 若 Leader 产生于尚未完成数据接收的 Follower,那么原来已完成接收的 Follower 则会放弃曾接收到的数据。由于 client 没有接收到响应,所以 client 会重新发送该写操作请求。
  1. commit 通知发出后 Leader 挂了

​ client 发送写操作请求给 Leader,Leader 也成功向所有 Followers 发出的 commit 指令,并向 client 发出响应后,Leader 挂了。

​ 由于 Stale Leader 已经向 client 发送成功接收响应,且 commit 通知已经发出,说明这个写操作请求已经被 server 成功处理。

Raft 算法动画演示

​ 在网络上有一个关于 Raft 算法的动画,其非常清晰全面地演示了 Raft 算法的工作原理。