探索Redis集群:(三)故障发现与转移机制

97 阅读7分钟

前情提要:

计划接下来用两周的时间,搞清楚Redis Cluster的相关内容。既然要深入了解集群,自然就需要解答以下几个基本问题:

  1. 集群中各节点的表现形式是什么?节点的增加和减少是怎样进行的?
  2. 数据在集群中是如何分布的?集群是如何处理读写操作的?在节点增减的时候,如何确保读写操作的正常进行?
  3. 集群是如何发现故障的?一旦发现故障,又是怎样进行处理的?
  4. 这种集群方式有没有什么局限?使用时有什么注意事项?
  5. 实际怎么搭建一个Redis集群?怎么使用?

之前完成了Redis集群的第二部分:探索Redis集群:(二)数据分布与节点增减

今天继续讨论:Redis集群 -(三)故障发现与转移机制

本文将对Redis集群的故障检测过程、如何故障转移、如何选举新的主节点等方面进行介绍。

故障检测过程

讨论故障发现和转移,首先要解决的就是故障是怎么检测的?什么时候认为发生了故障?

1. PING/PONG消息交换

集群中的每个节点定期向其他节点发送PING消息,以检测对方是否在线。如果一个节点未能在规定时间内回复PONG消息,则发送PING消息的节点会将其标记为疑似下线(PFAIL)。

节点A、B、C尝试与节点D进行PING/PONG消息交换的过程。节点D未能响应任何PING消息,因此被A、B、C视为可能下线(PFAIL)。

flowchart TD
    subgraph step1 ["1. PING/PONG消息交换"]
        direction TB
        A[节点 A] -->|发送PING| B[节点 B]
        A -->|发送PING| C[节点 C]
        A -->|发送PING| D[节点 D]
        B -->|发送PING| D
        C -->|发送PING| D
        B -->|回复PONG| A
        C -->|回复PONG| A
        D -.->|无响应| A
        D -.->|无响应| B
        D -.->|无响应| C
    end

    classDef online fill:#bdf,stroke:#333,stroke-width:2px;
    classDef offline fill:#fbb,stroke:#333,stroke-width:2px;
    class A,B,C online;
    class D offline;

2. 状态信息交换

节点通过互相发送消息的方式交换集群中各个节点的状态信息,如在线状态、疑似下线状态(PFAIL),或已下线状态(FAIL)。

节点A、B、C之间交换关于节点D的状态信息,相互报告节点D疑似下线(PFAIL)的情况。

flowchart TB
    subgraph step2 ["2. 状态信息交换"]
        direction TB
        A[节点 A] -->|报告D PFAIL| B[节点 B]
        A -->|报告D PFAIL| C[节点 C]
        B -->|报告D PFAIL| C
        B -->|报告D PFAIL| A
        C -->|报告D PFAIL| A
        C -->|报告D PFAIL| B
    end

    classDef online fill:#bdf,stroke:#333,stroke-width:2px;
    classDef pfail fill:#fda,stroke:#333,stroke-width:2px;
    class A,B,C online;
    class D pfail;

3. 下线报告和标记

当一个主节点通过消息得知另一个主节点认为第三个主节点进入了疑似下线状态时,它会将这个下线报告添加到对应节点的fail_reports链表中。如果半数以上负责处理槽的主节点都将某个主节点报告为疑似下线,则该节点将被标记为已下线(FAIL),并通过集群广播FAIL消息。

当半数以上的节点(在这个场景中是所有其他节点)报告节点D为PFAIL后,节点D被集群内的其他节点标记为FAIL,并且这个FAIL状态被广播到整个集群。

flowchart TB
    subgraph step3 ["3. 下线报告和标记"]
        direction TB
        A[节点 A] -->|收集PFAIL报告| D[节点 D]
        B[节点 B] -->|收集PFAIL报告| D
        C[节点 C] -->|收集PFAIL报告| D
        D -->|多数PFAIL, 标记FAIL| D
        D -->|广播FAIL消息| A
        D -->|广播FAIL消息| B
        D -->|广播FAIL消息| C
    end

    classDef online fill:#bdf,stroke:#333,stroke-width:2px;
    classDef fail fill:#f96,stroke:#333,stroke-width:2px;
    class A,B,C online;
    class D fail;

总的来说,这里的故障主要是节点无响应或者不在线,认为节点下线,节点的下线有主观下线客观下线两种状态:

  • 主观下线(PFAIL):当一个节点在指定时间内没有收到另一个节点的心跳消息时,它会将该节点标记为疑似故障。
  • 客观下线(FAIL):当超过半数的主节点都认为某个节点疑似故障时,该节点会被集群标记为确认故障。这是通过节点间的通信达成一致的。

如何故障转移

在确定故障发生之后,接下来就需要讨论如何转移故障?谁去转移故障?

1. 从节点发现主节点下线

当一个从节点发现自己正在复制的主节点进入了已下线状态时,将开始对下线主节点进行故障转移。

2. 从节点晋升为主节点

复制下线主节点的所有从节点中,会有一个从节点被选中,并执行SLAVEOF no one命令,成为新的主节点。

3. 槽位重新指派

新的主节点会撤销所有对已下线主节点的槽指派,并将这些槽全部指派给自己。

4. 通知集群

新的主节点向集群广播一条PONG消息,让其他节点知道这个节点已经由从节点变成了主节点,并且接管了原本由已下线节点负责处理的槽。

5. 开始处理命令请求

新的主节点开始接收和自己负责处理的槽有关的命令请求,完成故障转移。

因此,在确认发现故障之后,原有的从节点会晋升为主节点,接管主节点的槽指派,并向集群广播自己已成为主节点。

接下来还要讨论个问题,应该选择哪个从节点晋升主节点?

选举新的主节点过程

选举主节点的过程基于Raft算法的领导选举方法。

1. 配置纪元更新

集群的配置纪元是一个自增计数器,开始一次故障转移操作时,其值会增加。

2. 投票过程

集群里每个负责处理槽的主节点都有一次投票的机会,第一个向主节点要求投票的从节点将获得主节点的投票。

3. 广播选举请求

从节点通过广播CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST消息,要求有投票权的主节点向其投票。

4. 统计投票

从节点根据收到的CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK消息数量来统计获得的支持票数。如果一个从节点收集到超过半数主节点的支持票,则当选为新的主节点。

5. 选举保证

每个具有投票权的主节点在每个配置纪元里只能投一次票,确保了新的主节点只会有一个。

6. 重试选举

如果没有从节点能收集到足够多的支持票,则集群进入一个新的配置纪元,并再次进行选举,直到选出新的主节点为止。

以下是一个选举的示例:

  • 从节点1、从节点2、和从节点3检测到主节点失败。
  • 开始选举过程,每个从节点都可以投票给自己或其他从节点。
  • 最终从节点2获得多数票,成为主节点。
flowchart TB
    subgraph election ["选举主节点的过程"]
        direction TB
        detectFail[检测到主节点失败]:::offline --> electionStart[开始选举]:::online
        electionStart --> vote1[从节点1投票给自己]:::online
        electionStart --> vote2[从节点2投票给自己]:::online
        electionStart --> vote3[从节点3投票给从节点2]:::online
        vote1 --> result1[从节点1: 1票]:::online
        vote2 --> result2[从节点2: 2票]:::online
        vote3 --> result3[从节点3: 0票]:::online
        result1 & result2 & result3 --> decision{检查多数票}:::online
        decision --> |从节点2获胜| promotion[从节点2晋升为主节点]:::online
    end

    classDef online fill:#bdf,stroke:#333,stroke-width:2px;
    classDef offline fill:#fbb,stroke:#333,stroke-width:2px;


这次讨论了Redis集群的故障发现与转移机制。

下一篇将讨论Redis集群方式的局限以及使用注意事项。