5. Redis故障转移

  • 6.1 故障发现
    • 6.1.1 主观下线
    • 6.1.2 客观下线
  • 6.2 故障恢复
    • 6.2.1 资格检查
    • 6.2.2 准备选举时间
    • 6.2.3 发起选举
      • 1.更新配置纪元
    • 6.2.4 选举投票
    • 6.2.5 替换主节点
  • 6.3 故障转移时间(重点部分)
  • 6.4、参考文献

6.1 故障发现

6.1.1 主观下线

每个节点的clusterState结构都需要保存其他节点信息,用于从自身视角判断其他节点的状态。结构关键属性如下:

 

 属性flags,用于标示该节点对应状态,取值范围如下:

 主观下线的伪代码如下: 

6.1.2 客观下线 

当某个节点判断另一个节点主观下线后,相应的节点状态会跟随消息在集群内传播。ping/pong消息的消息体会携带集群1/10的其他节点状态数据,当接受节点发现消息体中含有主观下线的节点状态时,会在本地找到故障节点的ClusterNode结构,保存到下线报告链表中。结构如下:

*fail_reports记录了所有其他节点对该节点的下线报告,Gossip通过消息传播,集群内节点不断收集到故障节点的下线报告。当半数以上持有槽的主节点都标记某个节点是主观下线时,触发客观下线流程。

为什么半数以上处理槽的主节点?必须半数以上是为了应对网络分区等原因造成的集群分割情况,被分割的小集群因为无法完成从主观下线到客观下线这一关键过程,从而防止小集群完成故障转移之后继续对外提供服务。

每个节点ClusterNode结构中都会存在一个下线链表结构,保存了其他主节点针对当前节点的下线报告,结构如下:

每个下线报告都存在有效期,每次在尝试触发客观下线时,都会检测下线报告是否过期,对于过期的下线报告将被删除。如果在cluster-node-time*2的时间内该下线报告没有得到更新则过期并删除,处理逻辑的源码如下图所示:

下线报告的有效期限是server.cluster_node_timeout*2,主要是针对故障误报的情况。例如节点A在上一小时报告节点B主观下线,但是之后又恢复正常。现在又有其他节点上报节点B主观下线,根据实际情况之前的属于误报不能被使用。

6.2 故障恢复

故障恢复流程:

1)资格检查

2)准备选举时间

3)发起选举

4)选举投票

5)替换主节点

6.2.1 资格检查

每个从节点都要检查最后与主节点断线时间,判断是否有资格替换故障的主节点。如果从节点与主节点断线时间超过cluster-node-time*cluster-slave-validity-factor,则当前从节点不具备故障转移资格。参数cluster-slave-validity-factor用于从节点的有效因子,默认为10。

6.2.2 准备选举时间

当从节点符合故障转移资格后,更新触发故障选举的时间,只有到达该时间后才能执行后续流程。故障选举时间相关字段如下:

这里之所以采用延迟触发机制,主要是通过对多个从节点使用不同的延迟选举时间来支持优先级问题。复制偏移量越大说明从节点延迟越低,那么它应该具有更高的优先级来替换故障主节点。

使用优先级排名,更新选举触发时间,代码如下:

6.2.3 发起选举

当从节点定时任务检测到达故障选举时间(failover_auth_time)到达后,发起选举流程如下:

1.更新配置纪元

配置纪元是一个只增不减的整数,每个主节点自身维护一个配置纪元(clusterNode.configEpoch)标示当前主节点的版本,所有主节点的配置纪元都不相等,从节点会复制主节点的配置纪元。整个集群又维护一个全局的配置纪元(clusterState.current Epoch),用于记录集群内所有主节点配置纪元的最大版本。执行cluster info命令可以查看配置纪元信息:

配置纪元会跟随ping/pong消息在集群内传播,当发送方与接收方都是主节点且配置纪元相等时代表出现了冲突,nodeId更大的一方会递增全局配置纪元并赋值给当前节点来区分冲突,代码如下:

 配置纪元的主要作用:

·标示集群内每个主节点的不同版本和当前集群最大的版本。

·每次集群发生重要事件时,这里的重要事件指出现新的主节点(新加入的或者由从节点转换而来),从节点竞争选举。都会递增集群全局的配置纪元并赋值给相关主节点,用于记录这一关键事件。

·主节点具有更大的配置纪元代表了更新的集群状态,因此当节点间进行ping/pong消息交换时,如出现slots等关键信息不一致时,以配置纪元更大的一方为准,防止过时的消息状态污染集群。(重要部分)

配置纪元的应用场景有:

·新节点加入。

·槽节点映射冲突检测。

·从节点投票选举冲突检测。

开发注意事项:

之前在通过cluster setslot命令修改槽节点映射时,需要确保执行请求的主节点本地配置纪元(configEpoch)是最大值,否则修改后的槽信息在消息传播中不会被拥有更高的配置纪元的节点采纳。由于Gossip通信机制无法准确知道当前最大的配置纪元在哪个节点,因此在槽迁移任务最后的clustersetslot {slot} node {nodeId}命令需要在全部主节点中执行一遍。

从节点每次发起投票时都会自增集群的全局配置纪元,并单独保存在clusterState.failover_auth_epoch变量中用于标识本次从节点发起选举的版本。

2. 广播选举消息

在集群内广播选举消息(FAILOVER_AUTH_REQUEST),并记录已发送过消息的状态,保证该从节点在一个配置纪元内只能发起一次选举。消息内容如同ping消息只是将type类型变为FAILOVER_AUTH_REQUEST。

6.2.4 选举投票

只有持有槽的主节点才会处理故障选举消息(FAILOVER_AUTH_REQUEST),因为每个持有槽的节点在一个配置纪元内都有唯一的一张选票,当接到第一个请求投票的从节点消息时回复FAILOVER_AUTH_ACK消息作为投票,之后相同配置纪元内其他从节点的选举消息将忽略。

投票过程其实是一个领导者选举的过程,如集群内有N个持有槽的主节点代表有N张选票。由于在每个配置纪元内持有槽的主节点只能投票给一个从节点,因此只能有一个从节点获得N/2+1的选票,保证能够找出唯一的从节点,投票过程如下所示:

6.2.5 替换主节点

当从节点收集到足够的选票之后,触发替换主节点操作:

1)当前从节点取消复制变为主节点。

2)执行clusterDelSlot操作撤销故障主节点负责的槽,并执行clusterAddSlot把这些槽委派给自己。

3)向集群广播自己的pong消息,通知集群内所有的节点当前从节点变为主节点并接管了故障主节点的槽信息。

6.3 故障转移时间(重点部分

在介绍完故障发现和恢复的流程后,这时我们可以估算出故障转移时间:

1)主观下线(pfail)识别时间=cluster-node-timeout。

2)主观下线状态消息传播时间<=cluster-node-timeout/2。消息通信机制对超过cluster-node-timeout/2未通信节点会发起ping消息,消息体在选择包含哪些节点时会优先选取下线状态节点,所以通常这段时间内能够收集到半数以上主节点的pfail报告从而完成故障发现。

3)从节点转移时间<=1000毫秒。由于存在延迟发起选举机制,偏移量最大的从节点会最多延迟1秒发起选举。通常第一次选举就会成功,所以从节点执行转移时间在1秒以内。

根据以上分析可以预估出故障转移时间,如下:

1
2
failover-time(毫秒) ≤ cluster-node-timeout + cluster-node-timeout/2 + 1000
PS:公司目前的几套redis集群cluster-node-timeout=15s

cluster-node-timeout配置时可以根据业务容忍度做出适当调整,但不是越小越好,带宽消耗也是需要考虑的部分。

6.4、参考文献 

  1. 《Redis开发与运维》,付磊,张益军编著。
posted @   明明不平凡  阅读(450)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统
点击右上角即可分享
微信分享提示