Redis 的高可用之Redis 哨兵模式

177 阅读10分钟

一、Redis 哨兵模式的来源

主从模式中,一旦主节点由于故障不能提供服务,需要人工将从节点晋升为主节点,同时还要通知应用方更新主节点地址。

这种手动切换的方式对于大规模部署和高可用性要求较高的系统来说,可能会导致较长的服务中断时间和手动操作的复杂性。显然,多数业务场景都不能接受这种故障处理方式。

为了解决这个问题,Redis 从 2.8 开始正式提供了 Redis 哨兵机制来解决这个问题。

flowchart LR
    A[主从模式]
    B[故障]
    C[人工处理故障]
    D[人工更新主节点地址]
    A ---> B
    B ---> C
    C ---> D

二、 Redis 哨兵模式的作用

Redis 哨兵(Sentinel)是Redis的一种高可用解决方案。它是一种分布式系统,用于监控Redis主服务器和从服务器的运行状态,它可以在主服务器出现故障时自动将从服务器升级为主服务器,以保证系统的连续可用性。哨兵其实是一个运行在特殊模式下 Redis 进程。它有三个作用,分别是:监控自动选主切换(简称选主)通知

graph LR
A(哨兵模式的作用) ---> B(监控)
A(哨兵模式的作用) ---> C(选主)
A(哨兵模式的作用) ---> D(通知)

style B fill:#FFC0CB,stroke:#FFC0CB,stroke-width:2px
style C fill:#FFA07A,stroke:#FFA07A,stroke-width:2px
style D fill:#FFFFE0,stroke:#FFFFE0,stroke-width:2px

哨兵进程在运行期间,监视所有 Redis 主节点和从节点。

它通过周期性给主从库发送 PING命令,检测主从库是否挂了。

如果从库没有在规定时间内响应哨兵 PING命令,哨兵就会把它标记为下线状态;

如果主库没有在规定时间内响应哨兵 PING命令,哨兵则会判定主库下线,然后开始切换到选主任务。

所谓选主,其实就是从多个从库中,按照一定规则,选出一个当做主库。 至于通知呢,就是选出主库后,哨兵把新主库连接信息发给其他从库,让它们和新主库建立主从关系。

同时,哨兵也会把新主库连接信息通知给客户端,让它们把请求操作发到新主库上。

三、 Redis 哨兵模式到底是什么呢?

3.1 Redis 概念

因为Redis 哨兵也是一个 Redis 进程(注意和线程的区别,进程说明他是一个单独的运行程序),如果它自己挂了呢,那是不是就起不了监控作用啦。我们一起来看下 Redis 哨兵模式。

哨兵模式,就是由一个或多个哨兵实例组成哨兵系统,它可以监视所有Redis 主节点和从节点,并在被监视主节点进入下线状态时,自动将下线主服务器属下某个从节点升级为新主节点。,一个哨兵进程对 Redis 节点进行监控,就可能会出现问题(单点问题)。因此,++一般使用多个哨兵来进行 监控 Redis 节点,并且各个哨兵之间还会进行监控++。

其实哨兵之间是通过发布订阅机制组成集群,同时,哨兵又通过 INFO命令,获得了从库连接信息,也能和从库立连接,从而进行监控。

sequenceDiagram
    participant A as 哨兵节点A
    participant B as 哨兵节点B
    participant C as 哨兵节点C
    participant D AS 主节点
    participant E AS 从节点1
    participant F AS 从节点2
    participant G AS 从节点3
    
    loop 每秒一次
        A->>B: ping
        B->>A: ping
    end
    
    loop 每秒一次
        B->>C: ping
        C->>B: ping
    end
    
    A-->>D: 监视
    A-->>E: 监视
    B-->>F: 监视
    C-->>G: 监视
    
    D->>E: 复制
    D->>F: 复制
    D->>G: 复制

3.2 Redis 哨兵工作模式

  1. 每个哨兵以每秒钟一次频率向它所知主库、从库以及其他哨兵实例发送一个PING命令。

  2. 如果一个实例节点距离最后一次有效回复 PING命令时间超过 down-after-milliseconds选项所指定值, 则这个实例会被哨兵标记为主观下线。

  3. 如果主库被标记为主观下线,则正在监视这个主库所有哨兵要以每秒一次频率,确认主库确进入了主观下线状态。

  4. 当有足够数量哨兵(大于等于配置文件指定值)在指定时间范围内确认主库的确进入了主观下线状态, 则主库会被标记为客观下线。

  5. 当主库被哨兵标记为客观下线时,就会进入选主模式。

  6. 若没有足够数量哨兵同意主库已经进入主观下线, 主库主观下线状态就会被移除;若主库重新向哨兵 PING命令返回有效回复,主库主观下线状态就会被移除。

3.3 Redis 哨兵模式如何选主?

Redis的哨兵模式在主节点发生故障后,会从剩余的从节点中选举一个成为新的主节点。这个过程称为故障转移(failover)。哨兵模式选择主节点基本分为五个步骤:

  1. 主观下线: 当一个哨兵(Sentinel A)无法通过PING命令达到主节点时,该哨兵会将主节点标记为主观下线(sdown)。这只代表Sentinel A无法达到主节点,并不代表其他哨兵也无法达到。

  2. 客观下线: Sentinel A会询问其他哨兵节点,如果大多数哨兵也认为主节点不可达,那么主节点会被标记为客观下线(odown)。

  3. 开始选主: 当主节点被标记为客观下线后,哨兵们会开始选主过程。首先,所有哨兵会选出一个领导者,只有领导者可以执行选主操作。

  4. 选择从节点: 领导者会从所有的从节点中选择一个升级为新的主节点,选择的标准包括:

    • 优先选择那些复制偏移量最大的节点,因为这意味着这些节点含有更多的数据更新。

    • 如果有多个节点的复制偏移量相同,那么优先选择那些运行ID较小的节点,因为运行ID较小意味着这个节点运行的时间更长,更稳定。

    • 如果以上两个条件都无法决定,那么会根据节点的优先级来选择,优先级数值越小优先级越高。

  5. 升级主节点: 选定新的主节点后,领导者会向所有从节点发送命令,让他们将新的主节点设为他们的主节点。这样,故障转移过程就完成了。

下面是流程图

graph LR;
    A[哨兵A] -->|无法PING到主节点| M[主节点] & A[哨兵A];
    A[哨兵A] -->|标记主观下线| M[主节点] & sd[主观下线标记];
    A[哨兵A] -->|询问其他哨兵| B[哨兵B];
    A[哨兵A] -->|询问其他哨兵| C[哨兵C];
    B[哨兵B] -->|可以PING到主节点| M[主节点] & sd[主观下线标记];
    C[哨兵C] -->|可以PING到主节点| M[主节点] & sd[主观下线标记];
    B[哨兵B] -->|认为主节点不可达| M[主节点] & sd[主观下线标记];
    C[哨兵C] -->|认为主节点不可达| M[主节点] & sd[主观下线标记];
    
graph TD;
    B[哨兵B] -->|发送选主请求| L[选主领导者];
    C[哨兵C] -->|发送选主请求| L[选主领导者];
    L[选主领导者] -->|选择从节点| S1[从节点1];
    L[选主领导者] -->|选择从节点| S2[从节点2];
    L[选主领导者] -->|选择从节点| S3[从节点3];
    S1[从节点1] -->|同意成为新主节点| N[新主节点];
    S2[从节点2] -->|同意成为新主节点| N[新主节点];
    S3[从节点3] -->|同意成为新主节点| N[新主节点];
    L[选主领导者] -->|升级新主节点| N[新主节点];
    N[新主节点] -->|通知从节点切换主节点| S1[从节点1];
    N[新主节点] -->|通知从节点切换主节点| S2[从节点2];
    N[新主节点] -->|通知从节点切换主节点| S3[从节点3];

注意:哨兵选主包括两大过程,分别是:过滤打分

其实就是在多个从库中,先按照一定筛选条件,把不符合条件从库过滤掉。

然后再按照一定规则,给剩下从库逐个打分,将得分最高从库选为新主库

  • 选主时,会判断从库状态,如果已经下线,就直接过滤。

  • 如果从库网络不好,老是超时,也会被过滤掉。看这个参数 down-after-milliseconds,它表示我们认定主从库断连最大连接超时时间。

  • 过滤掉了不适合做主库从库后,就可以给剩下从库打分,按这三个规则打分:从库优先级、从库复制进度以及从库 ID 号。

  • 从库优先级最高话,打分就越高,优先级可以通过 slave-priority配置。如果优先级一样,就选与旧主库复制进度最快从库。如果优先级和从库进度都一样,从库ID 号小打分高。

3.4 由哪个哨兵执行主从切换呢?

选主领导者的确定是动态的,并且在哨兵节点之间进行竞争

一个哨兵标记主库为主观下线后,它会征求其他哨兵意见,确认主库是否确进入了主观下线状态。它向其他实例哨兵发送 is-master-down-by-addr命令。

其他哨兵会根据自己和主库连接情况,回应 Y或 N(Y 表示赞成,N 表示反对票)。如果这个哨兵获取得足够多赞成票数(quorum配置),主库会被标记为客观下线。

标记主库客观下线这个哨兵,紧接着向其他哨兵发送命令,再发起投票,希望它可以来执行主从切换。

这个投票过程称为 Leader 选举。因为最终执行主从切换哨兵称为 Leader,投票过程就是确定 Leader。一个哨兵想成为Leader 需要满足两个条件:

  • 需要拿到 num(sentinels)/2+1赞成票。

  • 并且拿到票数需要大于等于哨兵配置文件中 quorum值。

举个例子,假设有 3 个哨兵。配置 quorum 值为 2。即一个一个哨兵想成为Leader 至少需要拿到 2 张票。如下图:

sequenceDiagram
    participant A as 哨兵A1
    participant B as 哨兵A2
    participant C as 哨兵A3
    
    opt T1
        A->>A: 给自己投一票
        A->>B: 我想成为Leader
        A->>C: 我想成为Leader
    end
    
    opt T2
        C->>C: 给自己投一票
        C->>B: 我想成为Leader
        C->>A: 我想成为Leader
    end
    
    opt T3
        A->>C: 回复N,因为1已经投给自己了
    end
    
    opt T4
        B->>C: 回复Y,因为前面还没投过票
    end
    
    opt T5
        B->>A: 回复N,因为t4已经投给A3了
    end
    
    opt T6
        A->>A: 总票数:1Y1N
        C->>C: 总票数:2Y,可以成为Leader
    end

在上图中

  • 在t1时刻,哨兵A1判断主库为客观下线,它想成为主从切换Leader,于是先给自己投一张赞成票,然后分别向哨兵A2和A3发起投票命令,表示想成为Leader。

  • 在t2时刻,A3判断主库为客观下线,它也想成为Leader,所以也先给自己投一张赞成票,再分别向A1和A2发起投票命令,表示也要成为Leader。

  • 在t3时刻,哨兵A1收到了A3Leader投票请求。因为A1已经把票Y投给自己了,所以它不能再给其他哨兵投赞成票了,所以A1投票N给A3。

  • 在t4时刻,哨兵A2收到A3Leader投票请求,因为哨兵A2之前没有投过票,它会给第一个向它发送投票请求哨兵回复赞成票Y。

  • 在t5时刻,哨兵A2收到A1Leader投票请求,因为哨兵A2之前已经投过赞成票给A3了,所以它只能给A1投反对票N。

  • 最后t6时刻,哨兵A1只收到自己一票Y赞成票,而哨兵A3得到两张赞成票(A2和A3投)。

最终,哨兵A3成为了Leader,A3将来完成选主。

注意:假设网络故障等原因,哨兵A3也没有收到两张票,那么这轮投票就不会产生Leader。哨兵集群会等待一段时间(一般是哨兵故障转移超时时间2倍),再进行重新选举。

3.5 Redis 哨兵如何完成故障转义的?

假设哨兵模式架构如下,有三个哨兵组成集群,一个主库M1,两个从库S1和S2;

这时他们的正常情况如下

sequenceDiagram
    participant A as 哨兵集群
    participant B as 主库M1
    participant C as 从库S1
    participant D as 从库S2
    
    loop 监控
        A->>B: 监控
        A->>C: 监控
        A->>D: 监控
    end
    
    loop 复制
        B->>C: 复制
        B->>D: 复制
    end

当哨兵检测到Redis主库M1出现故障,那么哨兵需要对集群进行故障转移。假设选出了哨兵3作为Leader。

故障转移流程如下

sequenceDiagram
    participant A as 哨兵集群
    participant B as 主库M1
    participant C as 从库S1
    participant D as 从库S2
    participant E as 客户端应用
    
    
    A->>C: 解除从节点身份,升级为主节点
    A->>D: 更改为新主库的从库
    A->>B: 原主节点恢复也会变成新主库的从库
    A->>E: 通知应用程序新主节点地址

故障转移完成之后他们的流程如下

sequenceDiagram
    participant A as 哨兵集群
    participant B as 新从库S3(老的主库M1)
    participant C as 新的主库M2(原从库S1)
    participant D as 从库S2
    
    loop 监控
        A->>B: 监控
        A->>C: 监控
        A->>D: 监控
    end
    
    loop 复制
        C->>B: 复制
        C->>D: 复制
    end

四、总结一下哨兵模式的优点

哨兵模式的出现,给Redis增加了以下5大优点:

graph LR
A(哨兵模式的优点) ---> B(更高的可用性)
A(哨兵模式的优点) ---> C(自动化故障转移)
A(哨兵模式的优点) ---> D(简化的配置和管理)
A(哨兵模式的优点) ---> E(动态扩展能力)
A(哨兵模式的优点) ---> F(高度可靠的监控系统)

style B fill:#FFC0CB,stroke:#FFC0CB,stroke-width:2px
style C fill:#FFA07A,stroke:#FFA07A,stroke-width:2px
style D fill:#FFFFE0,stroke:#FFFFE0,stroke-width:2px
style E fill:#98FB98,stroke:#98FB98,stroke-width:2px
style F fill:#ADD8E6,stroke:#ADD8E6,stroke-width:2px
  • 自动故障检测和故障转移:哨兵模式可以自动检测主节点的故障,并在发生故障时自动进行故障转移。当主节点不可用时,哨兵节点能够协调选举新的主节点,并通知其他节点进行切换,从而实现高可用性和数据的连续性。

  • 高可用性:哨兵模式通过自动故障转移机制,提供了更高的系统可用性。当主节点发生故障时,系统能够快速恢复,并选举出新的主节点,减少业务中断时间。

  • 简化配置和管理:哨兵模式相对于主从模式,配置和管理更加简单。在主从模式中,需要手动配置主节点和从节点的关系,而哨兵模式下,哨兵节点会自动监控和管理主从节点的切换。这降低了部署和维护的复杂性。

  • 动态扩展:哨兵模式支持动态添加或删除节点,包括主节点和从节点。当需要扩展Redis集群的容量或通过增加节点提高系统性能时,可以方便地添加新的主节点或从节点,并通过哨兵进行自动的故障检测和配置更新。

  • 高度可靠的监控系统:哨兵模式提供了对Redis集群的实时监控和管理。哨兵节点会定期检查主从节点的健康状态,并在发现异常时采取相应的措施。这使得系统管理员能够及时了解Redis集群的状态,并采取必要的措施来确保高可用性和稳定性。