Redis哨兵模式是一种用于实现高可用性的解决方案,主要负责监控主从复制集群的运行状态,并在主节点出现故障时自动进行故障转移。以下是其设计与实现的关键点:
-
监控(Monitoring) :
- 哨兵不断地检查主节点和从节点是否可达。这是通过发送PING命令来实现的。如果一个实例在指定时间内没有响应,则被标记为“下线”(subjectively down)。
-
自动故障转移(Automatic Failover) :
- 如果主节点被多个哨兵确认不可用("客观下线"),一个哨兵将成为领头哨兵,负责执行故障转移。
- 故障转移包括选举新的主节点(通常是延迟最小的从节点),并更新其他从节点以追随新的主节点。
-
配置发布与订阅(Configuration Provider) :
- 哨兵负责向所有客户端提供集群的最新信息,包括当前的主节点地址。这是通过Redis自带的发布/订阅机制来实现的。
-
通知(Notification) :
- 当故障转移发生时,哨兵会通知应用程序,使其能够更新连接信息。
-
扩展性和一致性:
- 哨兵模式支持多个哨兵节点协同工作,以提高可靠性和一致性。哨兵之间采用Raft协议保证对特定事件的统一看法。
-
配置文件与启动:
- 哨兵通过一个配置文件启动,其中指定了需要监控的主节点信息及哨兵自身的ID等。
通过这些机制,Redis哨兵模式为Redis提供了自动化的高可用性管理,极大地提高了系统的可靠性和可恢复能力。在实际部署中,通常建议使用至少三个哨兵节点以确保在故障情况下仍能正常工作。
节点监听机制
Redis 哨兵通过定期发送 PING 命令来监控各个 Redis 实例(主节点和从节点)的状态。如果一个实例在指定的时间内没有响应 PING 请求,哨兵会将其标记为“主观下线”(Subjectively Down,简称 SDOWN)。这个标记是哨兵对该实例的单方面判断。
当哨兵发现某个主节点被标记为 SDOWN 时,它会与其他哨兵进行通信,以确认该节点是否真的不可用。如果多个哨兵都认为该节点不可用,这时该节点就会被标记为“客观下线”(Objectively Down,简称 ODOWN)。
具体实现原理包括以下几个步骤:
-
PING/PONG 机制:
- 哨兵每隔一定时间向所有被监控的 Redis 实例发送 PING 命令。
- 如果在规定的超时时间内收到 PONG 响应,则认为实例正常。
- 否则,将该实例标记为 SDOWN。
-
SDOWN 和 ODOWN 判定:
- 单个哨兵将一个实例标记为 SDOWN 后,会继续与其他哨兵通信以交换信息。
- 如果足够多的哨兵(达到配置的 quorum 数量)同意该节点不可用,则标记为 ODOWN。
-
故障转移:
- 当主节点被标记为 ODOWN 时,哨兵启动故障转移流程,选举新的主节点并通知系统更新配置。
-
通信协议:
- 哨兵之间通过 Redis 的发布/订阅机制以及命令通信来共享节点状态信息和决策。
-
Raft 共识算法(用于协调):
- 虽然哨兵模式使用的是简单的 quorum 机制来判定 ODOWN,但在实际的主从切换中可以看作是类似于分布式共识的过程,因为涉及到多个哨兵共同决策。
故障转移
在 Redis 哨兵模式中,当检测到主节点故障并确认其客观下线(ODOWN)后,哨兵将执行故障转移流程。具体步骤如下:
-
选举领头哨兵:
- 如果有多个哨兵在监控同一个主节点,一个哨兵会通过简单的投票机制被选为领头(Leader),负责协调接下来的故障转移过程。
-
选择新的主节点:
- 领头哨兵根据规则(优先级、复制偏移量等)选择一个合适的从节点作为新的主节点。
-
提升从节点:
- 被选中的从节点接收到命令后,自身提升为新的主节点。这包括停止从属复制,并接受写请求。
-
重新配置其他从节点:
- 领头哨兵通知集群中其他从节点,将它们的复制目标指向新的主节点。这确保整个集群保持一致的数据流动。
-
更新配置和传播信息:
- 哨兵更新其内部状态,并发布新的主节点信息给所有客户端。这使得客户端能够迅速切换到新的主节点进行连接。
-
恢复服务:
- 整个过程完成后,Redis 集群恢复正常操作,以新的主节点为核心继续处理请求。
-
日志记录与监控:
- 故障转移过程中,哨兵会详细记录每一步操作及其结果,方便后续的监控和审计。
选举领头哨兵
在 Redis 哨兵模式中,当主节点被判定为下线时,哨兵会通过选举机制来决定哪个哨兵实例成为“领头”(Leader),负责协调故障转移。以下是该选举机制的具体步骤:
-
发现主节点故障:
- 每个哨兵都会独立地对主节点进行健康检查。如果一个哨兵认为主节点已下线,会将其标记为“主观下线”(SDOWN)。
-
达成客观下线(ODOWN)共识:
- 当足够数量的哨兵(达到配置文件中的
quorum设定值)同意主节点下线后,该状态就被确认为“客观下线”。
- 当足够数量的哨兵(达到配置文件中的
-
发起领头选举:
- 一旦主节点被标记为客观下线,哨兵之间会进行一次选举,以确定哪一个哨兵将负责故障转移。
- 哨兵通过向其他哨兵发送
SENTINEL is-master-down-by-addr请求来询问其他哨兵的意见。
-
投票机制:
- 每个哨兵都有机会投票给自己或其他哨兵。它们会把投票信息广播给其他哨兵。
- 哨兵会选择第一个请求投票且没有对其他哨兵投过票的哨兵作为选票对象。
-
当选条件:
- 获得足够多的哨兵(至少
quorum)的投票支持后,某个哨兵就成为领头。 - 如果没有任何哨兵获得足够票数,选举过程会在短时间后重新开始。
- 获得足够多的哨兵(至少
-
担任领头角色:
- 被选为领头的哨兵会开始协调故障转移流程,选择新的主节点并执行提升操作。
- 领头哨兵负责通知其他从节点切换到新的主节点,并更新集群状态。
举例说明
假设有三个哨兵实例:Sentinel A、Sentinel B 和 Sentinel C,它们都监控同一个主节点。当主节点被标记为客观下线时,这些哨兵会进行选举。
投票步骤:
-
发起投票:
- 当 Sentinel A 发现主节点已客观下线后,它会请求其他哨兵进行选举,并投票给自己。
- Sentinel B 和 Sentinel C 也各自发起选举请求,并分别投票给自己。
-
接受投票请求:
- Sentinel A 向 Sentinel B 和 Sentinel C 发送
SENTINEL is-master-down-by-addr请求,并要求投票支持。 - 同样,Sentinel B 和 C 也向其他哨兵发送此请求。
- Sentinel A 向 Sentinel B 和 Sentinel C 发送
-
投票处理:
- Sentinel A 接收到来自 Sentinel B 的投票请求,如果它还没有给其他哨兵投票,就可以选择支持 Sentinel B。
- Sentinel B 接收到来自 Sentinel A 的请求,可能决定支持 Sentinel A。
- Sentinel C 由于最先接收到来自 Sentinel A 的请求,没有给其他哨兵投票,也选择支持 Sentinel A。
-
选举结果:
- 如果 Sentinel A 得到了两个投票(自己的投票和 Sentinel C 的支持),而 Sentinel B 和 C 各自只得到了一个,那么 Sentinel A 获得了多数票。
- Sentinel A 被选为领头,并开始执行故障转移任务。
-
故障转移协调:
- Sentinel A 作为领头,负责选择合适的从节点提升为新的主节点,并通知其他哨兵和从节点新主节点的信息。
新主节点选举机制
在 Redis 哨兵模式下,当主节点被确认客观下线(ODOWN)时,哨兵将选择一个从节点作为新的主节点。选择新主节点的具体流程如下:
-
资格检查:
- 首先,所有从节点都要通过几个基本的健康和状态检查。例如,从节点必须在线(没有被标记为下线),并且与之前的主节点保持良好的连接状态。
-
优先级排序:
- 每个从节点都有一个配置的优先级(
slave-priority)。哨兵首先会根据这个优先级来排序,从节点优先级值越低,优先级越高。 - 如果两个或多个从节点有相同的优先级,则下一步决定因素是其复制偏移量。
- 每个从节点都有一个配置的优先级(
-
同步偏移量:
- 在优先级相同时,哨兵会选择复制偏移量最大的从节点。复制偏移量表示从节点已经接收到的主节点数据的量,偏移量越大,表示与主节点的数据越接近最新。
-
ID比较:
- 如果优先级和偏移量都相同,那么哨兵会简单地比较从节点的运行 ID,并选择 ID 较小的那个。这一步通常用作最后的平局决策方式。
-
故障转移实施:
- 一旦选定了新的主节点,哨兵会发出命令,将该从节点提升为主节点,并通知其他从节点去复制新的主节点。
-
更新配置:
- 哨兵会更新集群状态,使所有相关方(包括客户端和其他哨兵)都知道新的主节点信息。
哨兵配置内容
Redis 哨兵的配置文件用于定义哨兵监控的主节点、从节点以及其他相关配置。下面是一个典型的哨兵配置文件的示例内容:
# 哨兵监听的端口
port 26379
# 哨兵监控的主节点信息,格式为: sentinel monitor <master-name> <ip> <redis-port> <quorum>
sentinel monitor mymaster 127.0.0.1 6379 2
# 配置哨兵触发故障转移之前需要同意的最少哨兵数量(即 quorum)
sentinel down-after-milliseconds mymaster 5000
# 哨兵在主节点故障后多久进行自动故障转移,单位为毫秒
sentinel failover-timeout mymaster 60000
# 指定多少个从节点需要确认同步新的主节点,才能认为故障转移完成
sentinel parallel-syncs mymaster 1
# 可选参数:通知脚本,当故障转移发生时调用此脚本
# sentinel notification-script mymaster /path/to/notification_script.sh
# 可选参数:客户端重新配置脚本,当故障转移发生时调用此脚本来更新客户端配置
# sentinel client-reconfig-script mymaster /path/to/client_reconfig_script.sh
配置说明:
-
端口:
port 26379是哨兵默认监听的端口,可以根据需要修改。 -
sentinel monitor:
mymaster是主节点的名字。127.0.0.1是主节点的 IP 地址。6379是主节点的端口。2是 quorum 值,表示至少需要多少个哨兵同意主节点下线才能触发故障转移。
-
down-after-milliseconds:
- 设置哨兵在几毫秒内未能与主节点通信后,会将其标记为“主观下线”。
-
failover-timeout:
- 定义执行故障转移的超时时间,过期后认为该次故障转移失败。
-
parallel-syncs:
- 指定有多少个从节点可以同时与新的主节点进行数据同步。
-
notification-script 和 client-reconfig-script:
- 这些是可选配置,用于在故障转移期间执行自定义脚本,以发送通知或者更新客户端配置。
思考题1:故障转移过程中是否会出现数据丢失问题,如何保证数据一致性
在 Redis 哨兵模式的故障转移过程中,数据丢失的可能性确实存在,但可以通过一些机制来减轻其影响并尽量保证数据一致性:
-
数据丢失原因:
- 主节点崩溃前未同步的数据:在主节点发生故障时,最后写入的一些数据可能尚未被复制到从节点,这些数据会丢失。
- 网络分区:如果由于网络问题导致主从之间通信中断,一些数据可能没有及时同步。
-
减少数据丢失的方法:
- 合理配置复制:通过调整参数如
min-slaves-to-write和min-slaves-max-lag来确保在一定数量的从节点确认接收到数据之前,主节点不会处理新的写请求。 - 持久化机制:启用 RDB 快照和 AOF 日志可以帮助在重启或切换后恢复大部分数据。不过,需要注意的是,AOF 重写期间也可能丢失部分数据。
- 合理配置复制:通过调整参数如
-
数据一致性保障:
- 同步偏移量选择:在选择新的主节点时,哨兵会优先选择复制偏移量最大的从节点,以确保新主节点拥有最新的数据。
- 故障检测和响应时间:通过优化哨兵参数(例如 PING 的频率和超时时间),可以更快速地检测故障并启动故障转移流程,从而减少潜在的数据丢失窗口。
-
客户端的重试机制:
- 配置客户端应用程序以具备自动重试功能,当感知到连接断开时能够重新找到新的主节点并继续操作。
思考题2:故障转移后,如何更新哨兵配置文件中主节点信息
在 Redis 哨兵模式中,哨兵会自动更新自身的配置文件以反映当前监控的主节点信息。这是通过哨兵的内部机制自动完成的,因此通常不需要手动更新配置文件。具体过程如下:
-
故障转移过程:
- 当一个新的从节点被提升为主节点后,哨兵会将该节点的信息记录下来,并通知其他哨兵和从节点。
-
自动更新配置:
- Redis 哨兵有一个自动保存配置的功能。当它检测到主节点发生变化时,会在内部维护一个状态,并将这些变化记录到哨兵配置文件中。
- 这个文件通常在哨兵启动时指定,通过
sentinel.conf文件路径加载。
-
配置持久化:
- 当哨兵配置发生变化(例如,进行故障转移并选择了新的主节点)时,哨兵会自动更新其配置文件,以确保在下一次重启时能够恢复当前状态。
思考题3: 故障转移后,从服务器如何从新的主服务器进行数据同步
在 Redis 的故障转移后,从服务器需要与新的主服务器进行数据同步。这个过程通常包括以下几个阶段:
1. 识别新主节点
当故障转移完成时,哨兵会通知所有从节点关于新的主节点的信息。每个从节点都会接收到哨兵发送的SLAVEOF命令,这个命令包含新主节点的 IP 地址和端口号。
2. 断开旧连接并重置复制状态
从节点首先会断开与旧主节点的连接,并重置其复制状态。这确保了从节点准备好建立新的复制关系。
3. 发起同步请求
从节点向新的主节点发送一个PSYNC命令。这个命令用于请求从某个特定的偏移量开始的增量数据同步。如果从节点之前已经是该主节点的从节点,并且有对应的复制历史记录,那么可能只需要进行部分同步。
4. 部分同步(如果可能)
如果新主节点包含从节点所需的复制历史(基于复制偏移量和 replication backlog),则可以进行部分同步。这种情况下,新主节点只需要发送从节点缺少的数据,而不是整个数据库快照。
5. 全量同步
如果无法进行部分同步(例如,从节点没有匹配的复制历史或者 backlog 不足),将进行全量同步:
- RDB 快照:新主节点会创建一个内存快照(RDB 文件),并将其发送给从节点。
- 清空旧数据:在接收到 RDB 文件后,从节点会清空当前数据库内容,以便用新的数据替换。
- 加载新数据:从节点加载接收到的 RDB 文件以更新其数据集。
6. 命令传播
在进行全量或部分同步期间,新主节点还会持续向从节点发送写命令,这些命令是在同步过程中通过网络链接实时传输的。这确保从节点能够一直保持最新状态。
7. 同步完成
一旦 RDB 文件被完全加载,从节点就处于正常操作模式,持续监听来自新主节点的命令流(命令传播机制),以保证数据的一致性。