(动力节点)动力哥Redis7笔记-第五章
5 Redis主从集群
通过解决Redis系统中单一实例可能导致的问题来提高可靠性,在实际应用中我们可以建立一个Redis集群,并将数据分散存储在集群的其他节点中以实现冗余存储功能。如果任何一个Redis服务出现故障,则由集群中的其他服务承担起负载。
5.1 主从集群搭建
Redis采用了一主多从架构实现读写分离。在该架构中,Master节点不仅承担客户端的所有read与writes,Slave节点则仅专注于接收并处理客户端所有的read请求。主要原因是数据库集群中write压力通常较低,且大部分 write 请求来源于 read 操作。因此,只需一个节点专门处理 write 任务即可满足需求
5.1.1 伪集群搭建与配置
基于单线程IO模型,在一个主机上部署多台Redis以提升处理器利用率的情况下
5.1.1.1 复制redis.conf
在Redis安装目录中创建一个名为cluster的新目录,并非必须遵循特定命名规则。随后将 redis.conf 文件复制到这个新创建的 cluster 目录下。需要注意的是,在该配置文件中必须定义每个Redis节点共有的属性设置,并确保这些设置会在后续的配置文件引用时被正确继承。

5.1.1.2 修改redis.conf
在redis.conf中做如下几项修改:
5.1.1.2.1 masterauth

由于我们需要构建主从集群,并且其中任何一个主机都有可能充当Master角色(即主节点),因此建议不要配置requirepass属性用于密码验证。如果必须设置的话,则必须确保所有主机的密码都相同。在这种情况下,请在每个配置文件中详细配置两个完全相同的参数:requirepass和masterauth这两个参数的作用如下:requirepass属性用于指定当前主机的访问密码;而masterauth则用于指示当前slave节点向主节点提交访问密码的信息。
5.1.1.2.2 repl-disable-tcp-nodelay

此特性可指定是否启用tcp-nodelay。当启用时(即设为yes),该特性将启用tcp-nodelay并可能导致通信延迟;若不启用(设为no),则会减少通信延迟但可能增加数据包的数量以及所占带宽。相比之下,在启用状态下 master 和 slave 之间的通信可能会出现一定的延迟问题。
tcp-nodelay:以最大限度利用网络带宽的速度传输信息时,在接收端通常会将较大的数据包进行拆分并立即转发给相关接收方。这一策略基于一种名为Nagle的协议实现。该协议的工作原理是,在接收方暂不将收集到的数据立即转发出去之前会先将这些数据缓存起来。只有当累积的数据量达到一定规模(由TCP特性决定)时才开始一次性转发给所有相关接收方。这种做法使得有效传输的比例得到了显著提高的同时也减少了无效传输的数据大幅减少从而实现了对带宽资源的有效利用和系统压力的缓解
| tcp-nodelay则是TCP协议中Nagle算法的开头。 |
|---|
5.1.1.3 新建redis6380.conf
新建一个redis配置文件redis6380.conf,该配置文件中的Redis端口号为6380。

5.1.1.4 再复制出两个conf文件
再次使用 redis6380.conf 生成两个新的 conf 文件:分别为 redis6381.conf 和 redis6382.conf。接下来,请对这两个新生成的 conf 文件中的配置内容进行调整。

修改redis6381.conf的内容如下:

修改redis6382.conf的内容如下:

5.1.1.5 启动三台Redis
分别按照 redis6380.conf、redis6381.conf 和 redis6382.conf 的设置参数依次配置并启动这三个 Redis 服务实例

5.1.1.6 设置主从关系
再次创建三个对话框,并通过客户端与三台Redis进行连接。随后,利用slavesof命令将6380号Redis配置为主从节点。

5.1.1.7 查看状态信息
通过info replication命令可查看当前连接的Redis的状态信息。

5.1.2 分级管理
当Redis主从集群中的Slave数量较多时,在数据同步操作上会使得Master承受显著的性能负担。此时可以通过对这些Slave实施分级策略来优化管理。

设置方式较为简便, 只需让低级Slaves配置它们的slaveof参数指向其上级Slaves即可. 尽管如此, 上级Slaves的状态仍为Slave, 其仅仅是更上一层的Master’s Slave. 例如, 指定6382主机为6381主机的Slave, 而6381主机仍是真正的Master’s Slave.

此时会发现,Master的Slave只有6381一个主机。
5.1.3 容灾冷处理
在 Redis 集群中的主从模式下,请问当主节点出现故障时该怎么办?主要有两种解决方案:一种是通过手动角色轮换机制将从节点提升为主节点以进行冷启动;另一种是采用哨兵模式实现高可用性 HA(HA),从而实现热启动 HA(HA)。无论 Redis 主从节点是否发生故障,在 HA 过程中,默认会将自身没有指定哨兵的从节点提升为主节点以完成 HA(主)任务。当该从节点已经拥有下一个层级的从节点存在时,则会直接承担起这些子从节点真正的主责。

5.2 主从复制原理
5.2.1 主从复制过程
当Redis主从节点接收到 masterof命令时(slave节点),系统会在其能够从 master节点持续复制数据之前完成一系列操作


5.2.1.1 保存master地址
当一个slave接收到slaveof指令后时(即),它会立即把新的master地址存储到内存中去。
5.2.1.2 建立连接
该slavelin在运行一个定时任务。这个定时任务会持续尝试通过socket与master进行通信。若无法成功建立socket连接,则该定时任务会持续进行重试操作直至通信成功或检测到’slaveof no one’命令的到来为止。
5.2.1.3 slave发送ping命令
在主节点成功建立连接后,从属节点将发起ping命令以进行首次通信.若从属节点未能收到主节点的响应,则该节点将自动断开与主节点的连接,并在下一次定时任务中重新尝试连接.
5.2.1.4 对slave身份验证
当master接收到来自slave的ping请求时,并不会立刻做出回应而是会在此之前必须完成身份认证步骤。若在此过程中发现认证失败则会转向对方发送拒绝连接的信息;若确认无误则会将连接成功的反馈告知给slave节点。
5.2.1.5 master持久化
在首次通信取得成功之后,slave会向master发起数据同步请求。当master接收到该请求时,并会启动一个子进程,并以异步方式进行持久化处理。
5.2.1.6 数据发送
完成持久化的 master 会启动异步子进程,并采用异步通信机制向 slave 发送这些更新信息。
然后 slave 将接收到的所有更新信息持续地记录在本地持久化的文件里。
在_slave_data_synchronization_process 中, master 主进程中仍然持续接收客户端的数据流。
并且不仅还在 master 内存里存储新增的数据副本,并将其同步至缓存。
等到 master 的 persistence 文件里的所有内容都已传输完毕之后,
随后 master 从缓存里提取最新的增量信息并重新发送给 slave。
这些新增量会被 slave 处理并记录到本地对应的 persistence 文件里。
整个过程至此完成。
5.2.1.7 slave恢复内存数据
解析
5.2.1.8 持续增量复制
每当 slave 接受外部服务请求时, master 会持续不断地将新的数据分批传递给 slave 进行更新, 从而确保 master 和 slave 之间的数据一致性得到维护.
5.2.2 数据同步演变过程
5.2.2.1 sync同步
在Redis版本低于2.8的情况下,在通信建立之后, slave将会向 master 发起同步请求。随后 master 将向 slave 转发其全部数据,这些数据将被保存至slaves的本地持久化文件中,即所谓全量复制流程。值得注意的是,在此过程中可能会因网络抖动导致复 制中断的发生。当网络恢复时,重获连接后, slave 将再次发起同步请求,随后从新开始执行全 量复 制流程。此过程所需的时间较长,因此期间可能出现频繁发生网络抖动现象的情况;而复 制中断 后再次执行完整的复 制流程不仅需要消耗大量的系统资源、网络带宽,并且可能导致资源消耗和时间上的额外负担情况出现
5.2.2.2 psync同步
Redis 2.8版本之后应用了psync(Partial Sync)同步策略,在全量复制过程中因网络抖动导致复制中断时,在重新连接成功后可以继续从断开位置恢复工作流程。即无需从头开始重新复制工作流程以提升效率。为了达成这一目标,在系统架构上实现了三项关键性改进:首先引入了高效的异步消息队列传输机制;其次,在集群节点之间实现了分布式互斥锁管理;最后,在集群层面实现了基于日志重排的日志持久化存储方案。
5.2.2.2.1 复制偏移量
系统对需要传输的数据赋予了序号, 每个字节都分配了一个编号. 这个序号被称为复制偏移量. 参与数据复制的主节点和副节点都会维护这个复制偏移量.

每当 master 发送一个字节数据时, 该 master 会进行累计计算. 统计信息可通过 info replication 中的 master_repl_offset 属性获取. 此外, 当 master 接收到 slave 的复制偏移更新时, master 会相应地保存该复制偏移量 offset.

当 master 向 slave 发送数据时, slime 会积累偏移量. 统计信息可通过 info replication 的 slime_repl_offset 查询得到.
5.2.2.2.2 主节点复制ID
当 Master 运行后会自动生成一个长度为 40 位的十六进制字符串用作当前复制 ID。此编号用于识别和确认 master 在数据同步过程中的作用。通过 master_replid 属性信息 replication 可获取此编号。
5.2.2.2.3 复制积压缓冲区
如果 master 与一个连通的 slave 相连时,则 master 会持续维护一个队列 backlog,默认容量设定为 1 MB(即 1 MB),该队列被命名为复制积压缓冲区。当 master 接收到 write operations data 时,则不仅会被存储在 master 主存中,并且会被分配至每个 slave 对应的发送缓存中;同时也会被存储到复制积压缓冲区中。这个机制的主要功能是临时存储最近的操作数据,并在 '断点续传' 过程中进行数据补足以防止数据丢失。
5.2.2.2.4 psync同步过程

psync是一个由slave执行的一个命令,在其运行过程中需要提供特定参数以完成数据复制任务。具体来说,在psync命令中需要包含两个关键参数:master_replid和repl_offset。其中,master_replid用于指明该操作应向哪个数据库实例发送请求以获取数据副本信息;而repl_offset则用于指定当前已接收数据块末尾的位置偏移量。这个机制确保了在断点续传场景下的高效数据同步过程。
在初次启动时(即第一次执行该操作),由于slave尚未获取到master实例的动态ID信息(dynamic ID),因此必须从 master 数据库实例中的初始位置(即offset为-1的位置)开始执行断点续传操作。具体而言,在首次运行时,默认情况下 slave 将会生成一个带有问号(?)作为 master_replid值并设置repl_offset为-1的psync命令:PSYNC ? -1。
一旦在复制过程中出现中断现象,并且在随后的操作中成功实现了与 master 实例的数据连接,则需要再次提交psyn命令以继续前传过程。此时的新psyn命令将包含相应的replica偏移量作为参数值(即replica offset)。这种机制使得即使是在分布式系统环境下也能够实现高效的并行数据同步功能。
其实,并不是slave提交了psyn命令后就可以立即从master处开始复制,而是需要master给出响应结果后,根据响应结果来执行。master根据slave提交的请求及master自身情况会给出不同的响应结果。响应结果有三种可能:
代码解读
- FULLRETRY <master_replid> <repl_offset>:被通知该主节点当前运行着一个动态ID,并且可以在指定位置开始全量复制操作;其中<repl_offset>参数通常设置为0值
- CONTINUE:被指示可以在指定偏移位置之后的位置上继续执行后续操作
- ERR:建议在当前Redis版本下不再支持主节点同步操作的情况下,请切换到全量复制模式
5.2.2.2.5 psync存在的问题
- 在psync数据同步过程中, 当slave发生重启时, 在其内存中维护的关于master的动态ID和后续传输偏移量都会被丢失,"断点续传"操作无法继续执行, 因此在这种情况下,默认的操作只能是执行全量复制以恢复数据一致性, 并由此带来资源消耗问题。
- 在psync数据同步过程中, 当master发生故障后, 在其失效后 slave节点会切换到新的 master节点(即"易主"), 这一行为会导致 slave节点必须重新从新 master节点执行全量复制操作才能恢复数据完整性, 并由此引发额外的资源消耗问题。
5.2.2.3 psync同步的改进
Redis 4.0对psync进行了改进,提出了“同源增量同步”策略。
5.2.2.3.1 解决slave重启问题
针对“slave重启时master动态ID丢失问题”,改进后的psync将master的动态ID直接写入到了slave的持久化文件中。
代码解读
当 slave 重新启动时直接从本地存储的持久化文件中提取 master 节点的动态 ID 后, 立即向 master 发送获取复制偏移量的请求. 当 master 接收到来自 slave 的请求时, 将检索并确认 master 节点上已有的复制偏移信息, 并通过发送 FULLRESYNC 指令及提供 master 节点 ID 和偏移量值的方式, 将这一信息传递给 slave, 通知其即将开始的数据传输位置. 随后, master 将执行断点续传操作以完成数据同步任务.
5.2.2.3.2 解决slave易主问题
slave易主后需要和新master进行全量复制,本质原因是新master不认识slave提交的psync请求中“原master的动态ID”。如果slave发送 PSYNC <原master_replid> <repl_offset>命令,新master能够识别出该slave要从原master复制数据,而自己的数据也都是从该master复制来的。那么新master就会明白,其与该slave“师出同门”,应该接收其“断点续传”同步请求。
代码解读
在新版本中精确保留的是"原版本的所有动态信息"。改进后的同步机制确保每个从属实体都完整存储了当前版本的所有动态数据。因此,在升级为主版本时,该实体仍然保留着上一版本的所有动态信息。这一特性也为解决"从属性更换"问题提供了关键依据。通过查看主版本的信息复制模块中的 master_replid2字段即可获取相关信息。若当前尚未发生任何更换操作,则该字段记录的数据量为40个零字节。
5.2.2.4 无盘操作
Redis 6.0对同步过程又进行了改进,提出了“无盘全量同步”与“无盘加载”策略,避免了耗时的IO操作。
代码解读
- 非磁 disk 全量同步意味着 master 节点的一个子进程从其内存中传输全部数据至 slave 节点,并未涉及任何硬盘操作。
- 非磁 disk 加载策略表明 ,当 master 节点向 slave 转发数据时 ,该节点不会将内容存储至硬盘文件中 ,而是立即存放到内存空间中 ,从而使 slave 完成快速的数据恢复。
5.2.2.5 共享复制积压缓冲区
Redis 7.0版本对复制积压缓冲区进行了改进,让各个slave的发送缓冲区共享复制积压缓冲区。这使得复制积压缓冲区的作用,除了可以保障数据的安全性外,还作为所有slave的发送缓冲区,充分利用了复制积压缓冲区。
代码解读
5.3 哨兵机制实现
5.3.1 简介
当Master节点发生故障时采用冷启动策略无法保证系统的高可用性。自Redis 2.6版本起新增了一种确保系统高可用性的机制:Sentinel哨兵。该机制通过在原有的集群基础上增加一个哨兵节点来充当监视角色,在此过程中 Master 节点一旦出现故障将自动被降级为Slave角色,并由另一个未受影响的 Slave 节点接替其职责。值得注意的是 Sentinels 也并非无懈可击:如果其中任何一个哨兵出现故障将导致整个集群陷入瘫痪。为了应对这一问题 另一种解决方案是构建一个新的哨兵集群来管理现有的 Sentinels 单体。具体来说 Sentinels 如何判断当前监视对象 Master 的状态呢?
5.3.2 Redis高可用集群搭建
鉴于资金充裕的情况下, 可以让 sole Sentinels独自占据一台独立的主机, 即让 Redis 服务仅在 Redis 服务器上运行, 在单独配置的 sole Sentinels 服务器上运行 sole Sentinels 服务。接下来将构建基于 ' 一主二从三哨兵 ' 架构的一台高可用伪集群, 其中我们仅需配置 sole Sentinels组件即可实现基本功能; 所有这三个角色将在同一台服务器上部署并运行; 其中 ' 一主二从 ' 将采用前述介绍的主要从架构模式。
| 角色 | 端口号 | 角色 | 端口号 | |
|---|---|---|---|---|
| master | 6380 | sentinel | 26380 | |
| slave | 6381 | sentinel | 26381 | |
| slave | 6382 | sentinel | 26382 |
5.3.2.1 复制sentinel.conf
用于将Redis安装目录下的sentinel.conf配置文件转移至指定位置,并将其放置在指定位置以便访问其内部设置信息。该配置文件位于指定位置,并包含一些公共设置信息。
用于将Redis安装目录下的sentinel.conf配置文件转移至指定位置,并将其放置在指定位置以便访问其内部设置信息。该配置文件位于指定位置,并包含一些公共设置信息。
5.3.2.2 修改sentinel.conf
修改cluster/sentinel.conf配置文件。
5.3.2.2.1 sentinel monitor

此配置旨在确定 Sentinels 应监控哪个 master 以及为其分配名称,并在此过程中生成一个专有名称,在后续多个环境中反复应用。此外还决定了 Sentinels 在判定 master 的「客观下线状态」时所需的最低阈值(法定 sentinel 数量)。其另一用途涉及 Sentinels 的主选举过程。为了确保主选举能够顺利进行,则需保证参与投票的 Sentinels 数量至少达到 max(quorum, sentinelNum/2 + 1)。此处已关闭此配置以避免潜在冲突。
5.3.2.2.2 sentinel auth-pass

当Redis主从集群中的服务器设置好了访问密码时,则必须配置 master 服务器的名称及其密码信息以便 sentinel 更容易地监控 master 服务的状态。
5.3.2.3 新建sentinel26380.conf
在Redis安装目录下的cluster目录中创建sentinel26380.conf配置文件作为Sentinel的配置文件,并在其中输入如下内容:

sentinel 监控属性用于设置当前监控节点的 master 节点的 IP 和 Port 参数,并在集群中的 master 节点上配置一个标识符 mym aster 以便其他相关属性的操作。参数 quorum 的具体数值通常设置为 2 以满足系统需求;该参数有两个主要作用:一是确保只有当 quorum 个 sentinel 节点一致确认 master 节点故障时才启动故障转移机制;二是与 sentinel 节点的 Leader 选举过程密切相关,在选举时需确保至少有 max(quorum, sentinelNum/2+1) 个 sentinel 参与其中。同时,在配置时需确保 master 节点的数量达到一定的数量级;并且在 election 过程中也需要满足相应的条件要求。
5.3.2.4 再复制两个conf文件
请依据以下步骤操作:
- 复制现有文件
$src/sentinel/seg conf/sentinel26380.conf。 - 在复制过程中生成两个新的conf文件
$src/sentinel/seg conf/sentinel26381.conf和$src/sentinel/seg conf/sentinel26382.conf。
随后对这些新生成的文件进行内容修改。
更新至$sentimental{src}/seg conf/sentimental{src}/seg conf/sentimental{src}/senditl-{{n}}.conf版本。

修改sentinel26382.conf。

5.3.3 Redis高可用集群的启动
5.3.3.1 启动并关联Redis集群
首先要启动三台Redis,然后再通过slaveof关联它们。


5.3.3.2 启动Sentinel集群
5.3.3.2.1 启动命令
位于/usr/local/bin目录中的redis-sentinel命令被指定用于启动Sentinel服务。然而,在调查过程中我们注意到,在/usr/local/bin目录中存在的redis-sentinel命令实际上仅是一个指向redis-server进程的软链接。这背后的原因可能与配置或系统初始化流程有关。

在Redis安装目录中查看源代码目录下的redis-server与redis-sentinel两个指令组时会发现它们在规模上几乎相同。经过对比分析后发现这两组指令在规模上几乎相同

主要原因在于,在启动时所加载的配置文件存在差异。因此,在启动Sentinel时,请务必明确指定sentinel.conf配置文件。
5.3.3.2.2 两种启动方式
Redis-server和redis-sentinel这两个命令本质上属于同一命令,并且因此采用其中一个即可启动Sentinel服务。
- 方式一,请采用Redis-Sentinel命令运行:$ redis-sentinel sentinel26380.conf
- 方式二,请启动Redis-Server服务以配置哨兵:$ redis-server sentinel26380.conf --sentinel
5.3.3.2.3 启动三台Sentinel



5.3.3.3 查看Sentinel信息
运行中的Sentinel相当于一个特殊的Redis实例。同样可以通过客户端进行连接操作,并且能够调用info函数来获取相关信息。

5.3.3.4 查看sentinel配置文件
打开任意sentinel的配置文件,发现其配置内容中新增加了很多配置。

5.3.4 Sentinel优化配置
在公共sentinel.conf文件中,此外还可以通过调整某些其他属性来实现对Sentinel的配置优化
5.3.4.1 sentinel down-after-milliseconds

每个Sentinels会定期向其 master, slave 以及其他 Sentinels 发送 ping 命令以检测其存活状态。若某个 Sentinels 在指定的时间段内未能收到相应节点的响应,则该 Sentinels 将主观判定其所在的主机出现故障。默认时间为 30 秒。
5.3.4.2 sentinel parallel-syncs

该属性用于配置,在发生故障时即老的 master 出现问题时, 新的 master 刚被晋升之后, 允许多少个 slave 能够从新 master 进行数据同步. 默认设置为 1 表示所有 slave 会依次从新 master 处进行数据同步.
5.3.4.3 sentinel failover-timeout

指定故障转移的超时时间,默认时间为3分钟。该超时时间的用途很多:
- 因为第一次故障转移失败,在同一主节点上再次尝试会占用该failover-timeout两倍的时间。
- 当新主节点完成升级后,在老主节点被迫迁移至新主节点以同步数据。
- 终止当前正在进行的故障转换操作所需的时间阈值。
- 当新主节点完成升级后,所有副本将配置文件更新为新的主节点。
5.3.4.4 sentinel deny-scripts-reconfig

是否可以使用命令sentinel来控制notification-script和client-reconfig-script这两个脚本的动态修改?
通常情况下不允许这样做。
即使被允许进行动态修改也可能带来安全风险。
5.3.4.5 动态修改配置

通过使用Redis CLI工具连接至Sentinel服务后,在此基础上即可实现配置信息的动态更改。举个例子,在执行以下命令时
| 参数 | 示例 |
|---|---|
| quorum | sentinel set mymaster quorum 2 |
| down-after-milliseconds | sentinel set mymaster down-after-milliseconds 50000 |
| failover-timeout | sentinel set mymaster failover-timeout 300000 |
| parallel-syncs | sentinel set mymaster parallel-syncs 3 |
| notification-script | sentinel set mymaster notification-script /var/redis/notify.sh |
| client-reconfig-script | sentinel set mymaster client-reconfig-script /var/redis/reconfig.sh |
| auth-pass | sentinel set mymaster auth-pass 111 |
5.4 哨兵机制原理
5.4.1 三个定时任务
Sentinel负责执行三个定时周期性作业以监控Redis节点和其他Sentinel设备的状态变化
5.4.1.1 info任务
每一个Sentinel节点每隔10秒都会触发一次向Redis集群中的所有节点发送info命令的行为,并用于获取当前状态信息。
5.4.1.2 心跳任务
每个Sentinel节点每隔一秒都会向所有的Redis集群成员和其它Sentinel节点发起一次ping命令,以便确认这些服务成员的状态信息。此任务对于确认网络设备在线状态具有重要意义。
5.4.1.3 发布/订阅任务
每个 sentinel 节点一启动都会向所有 redis 注册订阅_sentinel_:hello主题的信息。
当 redis 节点中该主题的状态发生变化时, 会立即通知所有订阅者。
每隔两秒系统就会向各个 redis 节点发送一条_sentinel_:hello主题的信息。
这些信息主要包括各个 redis 节点是否在线的状态判断结果以及当前 sentinel 的相关信息。
- 检测到新的Sentinel节点已接入网络,捕获并存储其相关信息,随后与其建立通信连接.
- 检测到存在针对Sentinel Leader角色的选举票数统计,启动相应的选举操作流程.
- 整合其他所有Sentinel节点对当前Redis服务实例运行状态的评估结果,将其作为该Redis实例进行离线判定的基础依据.
5.4.2 Redis节点下线判断
对于每个Redis节点在线状态的监控是由Sentinel完成的。
5.4.2.1 主观下线
每隔一秒,每一个Sentinel节点都会向每一个Redis服务器发送Ping心跳报文.若某个Sentinel在指定时间段内未收到对应Redis服务器的响应,则该Sansuine会判定该Redis服务器处于'离线状态'.此判定仅代表当前单个Sansuine node的观点,因此被称作主观断开.
5.4.2.2 客观下线
当某Sentinel节点处于主观下线状态且该节点为主时,在此情况下该Node将向所有其他Node发送sentinel is-master-down-by-addr命令以了解它们对主Node在线状态的看法。这些受询的Node在接收到命令后将返回响应:0表示在线、1表示下线。若某受询的SENTINEL接收到超过 quorum数量的‘已 offline’反馈,则该SENTINEL将确认主Node已 offline。
5.4.3 Sentinel Leader选举
当Sentinel集群中的一个节点对master进行客观下线判断时,则由Sentinel集群中的Leader负责后续故障转移。
采用Raft算法实现Sentinel集群的Leader选举机制。由于Raft算法较为复杂,在后续章节中将详细介绍其核心工作原理。
所有参与选举的节点都有资格竞选Leader。一旦完成“客观下线”判定后即可立即向全体成员发起竞选申请,并提交自己的候选人方案。
在收到提案后若仍持有未投选票,则立即支持该提案并将同意结果反馈给提案者。
若某次提交方案获得同意票数达到max(quorum, sentinelNum/2+1),则此时提案者将被确定为新的Leader。
在没有网络问题的情况下,在基本情况下就是由谁最先做出"客观下线"判断的人就会最先发起Sentinel Leader选举,并会获得大多数参与者的支持从而被选为Leader。
Sentinel Leader选举将在次故障转移发生之前举行。
故障转移结束后Sentinel将不再维持这种Leader-Follower关系即Leader将不再存在。
5.4.4 master选择算法
当发生故障转移时,在Redis集群中负责将故障转移到其他节点的任务由Sentinel Leader承担。该主节点需从所有Slave节点中确定其所属的主节点以完成切换过程。其采用的算法如下:
- 排除所有处于主观下线状态、无Sentinel心跳且replica-priority值等于零的所有Redis节点
- 从剩余Redis集群中筛选出replica-priority最低的候选集合;若候选数量为一,则直接确定该集群;否则继续执行下一步骤
- 在优先级一致的候选集合中选取复制偏移量最大的实例;若实例数量为一,则确定该实例;否则继续执行下一步骤
- 在复制偏移值一致的候选集合中挑选动态ID最小者作为最终结果
5.4.5 故障转移过程
Sentinel Leader负责整个故障转移过程,经历了如上步骤:
- Sentinel Leader按照 master 选择算法确定并指派一个 slave 节点作为新的 master 节点。
- Sentinel Leader 向新 master 发送升级指令,并将其晋升为主节点。
- Sentinel Leader 发布复制信息指令,并获取当前 master 节点的动态标识符。
- Sentinel Leader 将动态 ID 信息广播给其他 Redis 节点。
- Sentinel Leader 向其他 Redis 节点发送指定为 master 服务端的操作指令。
- Sentinel Leader 从所有 slave 结点中依次挑选 parallel-syncs 数量的 slave 结点进行数据同步操作,并最终完成所有 slave 结点的数据同步。
- 故障转移操作顺利完成。
5.4.6 节点上线
不同的节点类型,其上线的方式也是不同的。
5.4.6.1 原Redis节点上线
无论是在原始状态下的 master 节点还是在原始状态下的 slave 节点,在 original Redis 集群中的任何 node 上线时
Sentinel Leader 会立即将其更新为主 From Node 并切换角色
5.4.6.2 新Redis节点上线
在尝试为Redis集群中的现有节点新增一个新节点时(即该新增节点之前从未加入过此Redis集群),该上线操作需由人工执行。随后,在新节点启动前需确认当前 master 节点的身份,并在其启动后运行slaveof命令以完成加入集群的过程。
5.4.6.3 Sentinel节点上线
若需添加Sentinel节点,则无论该节点之前是否曾参与过Sentinel集群中的服务均需手动完成配置。具体操作包括:首先确定当前的Master机器;随后修改sentinel monitor属性文件,并指定希望监控的目标Master;最后启动Sentinel服务即可使其生效。
5.5 CAP定理
5.5.1 概念
CAP定理定义为在分布式系统环境中讨论的一致性、可用性和分区容错性的权衡关系。
- 一致性(C):分布式系统中多个主机是否能够维持数据的一致性特性?即,在执行更新操作后, 各节点的数据状态能否保持一致?
- 可用性(A):系统的各项服务始终保持在可响应状态, 并对用户的每一个请求进行处理.
- 分区容错性(P):面对任何网络分区故障情况时, 分布式系统仍能同时保障一致性与可用性的服务.
5.5.2 定理
CAP定理的核心内容在于针对分布式系统的特性,在面对不可控的网络环境和可能存在的网络分区时,设计者通常需要采用分区容错的设计策略。然而由于一致性与可用性的本质冲突,在实际应用中无法同时完美实现这两种特性。因此分布式系统通常需要采用分区容错的设计策略。这意味着一致性与可用性的保障不可能同时达到完美状态。具体来说,则需要根据系统的具体需求选择实现一致性(CP)还是实现可用性(AP)。这使得设计者在实现分布式系统时面临一个重要的权衡。
5.5.3 BASE理论
BASE是Basically Available(基本可用)、Soft state(软状态)和Eventually consistent(最终一致性)三个短语的简写,BASE是对CAP中一致性和可用性权衡的结果,其来源于对大规模互联网系统分布式实践的结论,是基于CAP定理逐步演化而来的。
BASE理论的核心思想是:即使无法做到强一致性,但每个系统都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性。
5.5.3.1 基本可用
系统容错性指的是,在分布式架构下,在无法预见的故障发生时,系统能够容忍一定程度的不可用性。
5.5.3.2 软状态
软状态是指系统中允许存在系统数据的中间态并且假设这种中间态的存在对系统的整体可用性无影响即意味着系统主机间的数据显示同步过程会不可避免地产生延迟现象。这种软态本质上是一种渐变的状态或者说是一种过渡阶段的状态
5.5.3.3 最终一致性
系统中的每个数据副本在经历一段同步操作后将处于一致状态。其核心在于系统需确保最终数据达成统一,并非必须实现实时同步。
5.5.4 CAP的应用
下面将生产中常见到的一些中间件与服务器集群的CAP特性进行分析。
5.5.4.1 Zookeeper与CAP
Zookeeper采用了CP模式,并基于此实现了对一致性的一致维护。当Leader节点的数据发生变化时,在Follower尚未完成同步前期间,整个Zookeeper集群将暂时停止对外提供服务.若在此时有客户端试图访问数据,则因为网络连接超时而导致请求被重试.然而,由于Leader节点的选举过程非常迅速,因此这种等待周期对实际用户来说几乎察觉不到.由此可见,Zookeeper虽然在一致性维护方面表现出了极强的优势,但在系统可用性的保障上却略显不足.
5.5.4.2 Consul与CAP
Consul遵循的是CP模式,即保证了一致性,但牺牲了可用性。
5.5.4.3 Redis与CAP
Redis遵循的是AP模式,即保证了可用性,但牺牲了一致性。
5.5.4.4 Eureka与CAP
Eureka遵循的是AP模式,即保证了可用性,但牺牲了一致性。
5.5.4.5 Nacos与CAP
在构建Nacos注册中心时,默认会采用API(AP)风格。然而该平台同时也支持基于服务(CP)架构模式,并非强制性要求用户切换至后者;转换至基于服务架构(CP)需经由用户发起相应的转换请求。
5.6 Raft算法
5.6.1 基础
Raft算法是一种基于日志复制管理以实现集群节点一致性的重要算法。该日志复制管理涉及集群节点中的Leader与Followers之间的操作。由选出来的Leader节点负责组织并执行日志复制过程,从而确保各节点间数据的一致性。
5.6.2 角色、任期及角色转变

在Raft中,节点有三种角色:
- Leader:主要承担客户端发送请求的任务,并且能够接收客户端的数据查询。不仅管理日志记录的工作,还参与日志复制过程。
- Candidate:候选者将在选票统计阶段竞争领导职位,并可能成功当选成为新一任的Leader。在选举过程中扮演着关键的角色。
- Follower:主要负责接收并处理来自客户端的数据查询任务。该组件还会同步收集并存储 Leader 发出的日志信息。每当其他候选者收到相应的选票时会自动响应并记录投篮情况,并根据当前状态动态调整策略以确保高效决策。
5.6.3 leader选举
通过Raft算法首先要实现集群中Leader的选举。
5.6.3.1 我要选举
如果follower在其心跳超时范围内未接收到来自leader的心跳,则推断leader已故障。此时它将首先增加本地term的计数。随后,follower将执行以下操作:
一旦收到其他候选人(即该候选人)的投票请求,则会将选票投给这个候选人。
该用户将从follower角色转变为candidate。
如果此前未投过,则会给自己投上一票。
随后由该用户向其它节点发出投票请求,并等待其回应。
5.6.3.2 我要投票
follower在接收到投票请求后,其会根据以下情况来判断是否投票:
在处理候选人的投票请求时,在任期内每个候选人的任期不得低于我的任期长度。
在当前时间段内,在任期内尚未投出过任何一票。
当接收到多个候选人的请求时,在任期内将优先处理并按照先到先得的原则进行选举。
5.6.3.3 等待响应
在提交投票请求后会等待其它节点的响应结果。该响应结果可能有以下几种情况:其中一种是...另外一种是...还有一种是...
- 获得至少一半的选票后成为新的领导角色,并随后通过消息向所有其他节点传达这一信息。
- 接收到其他候选人发送的新候选人当选的通知后进行比较分析,在比较结果中发现新候选人的当选时间不短于自身时间之后便决定转为从属于该领导角色。
- 经过一段时间后未再获得过半数选票也未接收到新候选人当选的通知因而触发重新发起选举程序。
5.6.3.4 选举时机
在多数情况下,在Leader发生故障(或挂掉)时(或挂掉时),Follower几乎同时会察觉到这一情况并迅速做出反应。这种情况下往往会导致多个候选节点拥有相同的选票数量(即无法确定一个唯一的Leader)。为了避免这种情况的发生,Raft算法采用了随机化的选举超时机制来解决这一问题。该算法通过为这些Follower分配一个随机的选举启动时间( election timeout)来实现这一点——这个参数设置在一个150到300毫秒的范围内。只有达到该election timeout阈值的Follower能够立即转变为候选角色并开始新的投票过程;如果未达到该阈值,则需等待直到满足条件后才能行动。值得注意的是,在大多数情况下(或一般情况下),拥有较小election timeout设置的Follower更快地转变为候选角色,并优先发起新的选举轮次。
5.6.4 数据同步
当Leader选举完成时,在完成日志复制机制后,则实现了集群内各个节点的数据同步。
5.6.4.1 状态机
Raft算法的一致性达成过程主要依赖于日志复制的状态机。
在不同服务器上的状态机处于相同的状态并接收相同的输入时,则会一致地产生输出。

5.6.4.2 处理流程

当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发出成功处理的响应
5.6.4.3 AP支持

该系统中的日志包含term index、log index和command三项。尽管各节点的日志可能并不完全一致,在主节点通过持续向从属节点发送消息包(boxes),从而能够维持日志的一致性。这表明Raft算法不具备强一致性
5.6.5 脑裂
该系统采用Raft集群架构时容易出现"分片过载"现象。在多区域部署中,在网络连接不稳定的情况下容易出现多个分片。而当出现多个分片时,则可能导致系统出现"分片过载"的问题。
相比于其他架构方案,在生产环境中的容灾能力最强。
为了便于后续分析和讨论,请问您希望了解哪些方面的详细信息?
基于不同区域的断网情况,请问您希望了解哪些具体的分析内容?
5.6.5.1 情况一–不确定

这种情况下,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则会自动下课,所以不会形成脑裂。
5.6.5.2 情况二–形成脑裂

这种情形本质上与情形一一致。值得注意的是,在这种情况下必然会导致脑裂的发生,并不论新Leader部署于B区还是C区。
5.6.5.3 情况三–无脑裂

A及C均可正常对外提供服务,但目前仅B方无法选出新的领导层.因为B中的所有主机均已进入选举模式,导致其目前仍无法提供任何服务,并未形成完整的脑裂
5.6.5.4 情况四–无脑裂

A、B、C均可以对外提供服务,不受影响。
5.6.5.5 情况五–无脑裂

A机房不具备处理写操作请求的能力但仍可向外界提供读服务。
B与C机房因缺少领导层成员故需进行选举程序但在候选人得票率未达到半数时因此未能选出新的领导者。
5.6.6 Leader宕机处理
5.6.6.1 请求到达前Leader挂了
在 client 发送 write 操作请求至 Leader 之前, 集群中的 Leader 已经退出服务, 这是因为该 write 请求尚未抵达集群节点. 因此, 在集群层面看来这个 write 请求从未出现过, 对集群数据的一致性没有造成任何影响. 当 Leader 节点故障后, 系统会自动选举出新的 Leader 节点. 由于 Stale Leader 节点未成功向客户端返回处理响应, 因此在等待新的 Leader 上线期间, 客户端必须重新发起该 write 操作请求.
5.6.6.2 未开始同步数据前Leader挂了
client向Leader提交一个写操作请求,在Leader开始发送数据之前_leader已因故退出集群系统。此时系统会自动选举新的Leader角色。当旧 Leader 重新加入集群并完成与新 Leader 的数据同步操作后_原有的数据会被丢弃以保证集群的整体一致性。由于旧 Leader 没有成功返回处理结果给client_后续 client 会重新发起该操作请求。
5.6.6.3 同步完部分后Leader挂了
客户端发起写操作请求至leader,在其接收到该请求并完成相应处理后会将该信息传播至全体follower节点。随后,在某些follower节点接收到该信息的过程中 leader出现故障无法继续运行。当出现这种情况时系统会自动启动新的leader选举流程以确保系统的正常运转。
- 若Leader源于已完成数据接收的Follower,则该Follower会继续将之前所接收的所有write操作记录为日志形式,并将此记录插入到本地状态机中,并向所有Follower发出询问。
- 在获得超过半数的同意响应后,该系统会向全体Follower发送提交提交指令,并同时向客户端发送响应信息。
- 若Leader源于尚未完成数据接收的Follower,则原本已经完成数据接收的Follower将放弃曾接收到的所有数据。
- 由于客户端尚未收到任何响应信息, 因此客户端将重新发送该write操作请求。
5.6.6.4 commit通知发出后Leader挂了
客户端发起write操作请求向主节点发送,并在完成响应后提交了commit指令。
因为静默主节点已成功接收客户端的响应,并且提交的通知已发布。
5.6.7 Raft算法动画演示
有关该协议的具体运作机制,在网络上有一个较为详实的视频资源对其进行了详细说明
