Redis 单节点性能很强,但单节点并不等于没有瓶颈。本文从主从架构切入,按照“为什么需要高可用 → 主从复制如何同步数据 → Sentinel 如何完成故障转移 → 哨兵集群如何保证自身可靠”的思路,系统讲清楚 Redis 高可用中最核心的主从复制与哨兵机制。持久化同样属于 Redis 高可用的重要部分,后续会单独展开。


一、Redis 为什么需要高可用

Redis 是一个高性能的开源键值对数据库,最典型的特点是数据主要存储在内存中,因此读写速度非常快。

也正因为 Redis 性能足够高,它经常被用在这些场景中:

  • 缓存热点数据
  • 保存登录态和 Session
  • 实现计数器、排行榜
  • 实现分布式锁
  • 承担高频读写的中间层存储

但是,高性能不等于没有上限。

如果整个系统只有一个 Redis 节点,所有读写请求都打到这个节点上,那么它至少会遇到两个核心问题:

  • 性能瓶颈:请求量持续上涨后,单个 Redis 节点的 CPU、内存、网络带宽都会成为上限。
  • 单点故障:一旦这个 Redis 节点宕机,所有依赖 Redis 的业务都会受到影响。

所以 Redis 高可用要解决的本质问题是:

在 Redis 节点出现性能瓶颈或节点故障时,系统仍然能够尽可能稳定地对外提供服务。

围绕这个目标,本文先讨论两个机制:

  • 主从复制:通过一个主节点搭配多个从节点,提高读能力,并让从节点保存主节点的数据副本。
  • Sentinel 哨兵机制:当主节点故障时,自动发现故障、选择新主节点并完成故障转移。

可以先用一张图理解 Redis 主从和 Sentinel 的整体关系:

flowchart LR
    Client[客户端]

    subgraph RedisNodes[Redis 主从节点]
        Master[Master 主节点]
        Replica1[Replica 从节点 1]
        Replica2[Replica 从节点 2]
    end

    subgraph SentinelNodes[Sentinel 集群]
        S1[Sentinel 1]
        S2[Sentinel 2]
        S3[Sentinel 3]
    end

    Client -->|写请求| Master
    Client -->|读请求| Replica1
    Client -->|读请求| Replica2

    Master -->|复制数据| Replica1
    Master -->|复制数据| Replica2

    S1 -.监控.-> Master
    S1 -.监控.-> Replica1
    S1 -.监控.-> Replica2
    S2 -.监控.-> Master
    S3 -.监控.-> Master

二、主从架构是什么:先解决读性能瓶颈

2.1 主从架构的基本思想

Redis 主从架构的基本思想很简单:

主节点负责写请求,从节点负责读请求,主节点再把数据同步给从节点。

也就是:

  • Master 主节点:处理写请求,并把写入产生的数据变化同步给从节点。
  • Replica 从节点:主要处理读请求,并持续复制主节点的数据。

在很多业务中,Redis 通常是读多写少。如果所有读请求都由一个 Redis 节点处理,那么读请求量一上来,单节点就可能成为瓶颈。

引入从节点后,读请求可以分散到多个从节点上。例如一个主节点搭配三个从节点,理论上读能力可以明显提升。虽然实际效果会受到网络、复制开销、从节点性能等因素影响,不一定能完全达到线性提升,但读压力确实可以被有效分摊。

2.2 主从架构解决了什么

主从架构主要解决的是:单节点读性能有限的问题

它的价值体现在三个方面:

  • 读写分离:写请求集中到主节点,读请求分散到从节点。
  • 读能力扩展:增加从节点后,可以承载更多读请求。
  • 数据副本冗余:从节点保存主节点的数据副本,为后续故障转移打基础。

但是,主从架构并不是终点。它只是把单节点 Redis 扩展成多个 Redis 节点,随之也引入了新的问题。


三、主从架构带来了什么问题

主从架构解决了读性能瓶颈,但它至少带来了两个新的问题:数据同步问题故障转移问题

3.1 问题一:主从数据不一致怎么办

主从架构中,从节点一般不处理写请求,写请求只进入主节点。

这意味着主节点必须把写命令同步给从节点,否则从节点读到的数据就可能是旧数据。

如果主从长期不一致,可能引发:

  • 读取到旧数据。
  • 读取到脏数据。
  • 主节点故障后,从节点数据不完整。
  • 故障转移后出现数据丢失风险。

所以第一个问题是:

主节点的数据变化,如何可靠地同步给从节点?

这个问题由 主从复制机制解决。

3.2 问题二:主节点挂了怎么办

如果从节点宕机,系统的读能力会下降,但通常不会直接导致整个 Redis 服务不可用。

真正严重的是主节点宕机。

因为主节点负责写请求,如果主节点挂掉,就会出现:

  • 写请求无法继续处理。
  • 从节点不会自动变成主节点。
  • 客户端不知道新的主节点是谁。
  • 整个 Redis 写服务可能进入不可用状态。

所以第二个问题是:

主节点故障之后,Redis 如何自动发现故障,并从从节点里选出一个新的主节点?

这个问题由 Sentinel 哨兵机制解决。

接下来先讲主从复制,再讲 Sentinel。因为只有先理解主从节点之间的数据如何同步,后面再看 Sentinel 选择新主节点时,才能理解为什么复制偏移量、从节点健康状态这些条件很重要。


四、怎么解决数据同步:Redis 主从复制

Redis 主从复制解决的是“主节点和从节点数据如何保持同步”的问题。

它主要分为三种情况:

  • 全量同步:从节点第一次连接主节点,或者无法进行增量同步时使用。
  • 命令传播:主从正常连接后,主节点持续把写命令发送给从节点。
  • 增量同步:主从断线重连后,只同步断线期间缺失的数据。

4.1 第一次连接:全量同步

当从节点第一次连接主节点时,会发送:

1
PSYNC <master_replid> <offset>

第一次同步时,从节点并不知道主节点的复制 ID 和复制偏移量,所以一般发送:

1
PSYNC ? -1

含义是:

  • ?:不知道主节点的复制 ID。
  • -1:没有复制进度。
  • 请求主节点进行全量同步。

全量同步不是一条线从上到下,而是 Master 和 Replica 之间的一次交互。左边是主节点,右边是从节点,流程如下:

sequenceDiagram
    participant Master as Master 主节点
    participant Replica as Replica 从节点

    Replica->>Master: PSYNC ? -1,请求全量同步
    Master->>Master: BGSAVE 生成 RDB 快照
    Master->>Master: BGSAVE 期间的写命令写入 replication buffer
    Master-->>Replica: FULLRESYNC replid offset
    Master-->>Replica: 发送 RDB 文件
    Replica->>Replica: 清空旧数据并加载 RDB
    Replica-->>Master: 加载完成,继续接收增量命令
    Master-->>Replica: 发送 replication buffer 中积累的写命令
    Replica->>Replica: 执行写命令,追平 Master

这个流程可以拆成几步理解:

  • 从节点发送 PSYNC ? -1,表示自己没有复制进度,请求全量同步。
  • 主节点执行 BGSAVE,通过子进程生成 RDB 快照。
  • 主节点在生成和传输 RDB 的同时,继续接收新的写命令。
  • 这些新写命令会先写入 replication buffer,防止同步期间的数据变化丢失。
  • 从节点收到 RDB 后,会清空旧数据并加载 RDB。
  • RDB 加载完成后,主节点再把 replication buffer 中积累的写命令发送给从节点。
  • 从节点执行这些写命令后,就完成了全量同步。

这里有两个关键字段:

  • runID / replid:Redis 实例启动时生成的唯一标识,用于标识主节点身份。
  • offset:复制偏移量,用来记录主从复制进度。

4.2 正常连接后:命令传播

全量同步完成后,主节点和从节点之间会建立并保持一个 TCP 长连接。

后续主节点每执行一个写命令,就会把这个写命令通过长连接发送给从节点。从节点收到后,在本地执行相同的写命令,从而保持数据同步。

这个过程同样适合画成左右交互:

sequenceDiagram
    participant Client as 客户端
    participant Master as Master 主节点
    participant Replica as Replica 从节点

    Client->>Master: 写命令 SET key value
    Master->>Master: 执行写命令
    Master-->>Replica: 通过长连接传播写命令
    Replica->>Replica: 执行同样的写命令
    Replica-->>Master: 上报复制偏移量

为什么要保持长连接?

因为 Redis 主从复制基于 TCP。如果每次同步都重新建立连接,就会不断产生连接建立和释放的成本。保持长连接可以减少这些开销,让写命令传播更加高效。

在网络正常、主从压力不大的情况下,主从同步延迟通常很低。但严格来说,Redis 主从复制默认是异步复制,所以它不能保证绝对强一致。

4.3 断线重连后:增量同步

如果主从之间网络断开,或者从节点短暂宕机,那么断开期间主节点仍然可能继续处理写命令。

这时主从数据就会出现差异。

从节点重新连接主节点后,会再次发送:

1
PSYNC <master_replid> <offset>

这一次它会带上之前保存的主节点复制 ID 和自己的复制偏移量。

如果主节点判断从节点缺失的数据仍然保存在 repl_backlog_buffer 中,就可以执行增量同步。

增量同步的交互流程如下:

sequenceDiagram
    participant Master as Master 主节点
    participant Replica as Replica 从节点

    Replica->>Master: 断线后重连,发送 PSYNC replid offset
    Master->>Master: 校验 replid 和 offset
    Master->>Master: 从 repl_backlog_buffer 找到缺失命令
    Master-->>Replica: CONTINUE
    Master-->>Replica: 发送 offset 之后缺失的写命令
    Replica->>Replica: 执行缺失命令,追平 Master
    Replica-->>Master: 上报新的复制偏移量

4.4 什么时候不能增量同步

同步过程

增量同步依赖 repl_backlog_buffer

repl_backlog_buffer 是主节点维护的一个环形缓冲区,用来保存最近一段时间传播过的写命令。因为它是环形结构,所以空间是有限的:如果写入的数据超过缓冲区大小,旧数据就会被新数据覆盖。

如果从节点断线时间太久,或者主节点在断线期间写入量太大,那么从节点缺失的那部分命令可能已经被覆盖。此时主节点就无法根据 offset 找回缺失数据,只能重新触发全量同步。

这种情况下的流程如下:

sequenceDiagram
    participant Master as Master 主节点
    participant Replica as Replica 从节点

    Replica->>Master: 断线后重连,发送 PSYNC replid offset
    Master->>Master: 校验 replid 和 offset
    Master->>Master: 发现缺失数据已被 repl_backlog_buffer 覆盖
    Master-->>Replica: FULLRESYNC replid offset
    Master->>Master: BGSAVE 生成 RDB 快照
    Master-->>Replica: 发送 RDB 文件
    Replica->>Replica: 清空旧数据并加载 RDB
    Master-->>Replica: 发送同步期间积累的写命令
    Replica->>Replica: 执行写命令,完成全量同步

4.5 replication buffer 和 repl_backlog_buffer 的区别

主从复制里有两个名字很像的缓冲区:

  • replication buffer
  • repl_backlog_buffer

它们的作用不同,不能混为一谈。

replication buffer

replication buffer 是主节点为每个从节点维护的复制缓冲区。

它主要用于:

  • 主从正常复制时,暂存要发送给某个从节点的数据。
  • 全量同步期间,保存 RDB 生成和传输过程中产生的新写命令。
  • 某个从节点处理较慢时,临时堆积还没发送或还没被处理的数据。

可以理解为:

replication buffer 更偏向于“当前连接上的数据发送缓冲”。

repl_backlog_buffer

repl_backlog_buffer 是主节点维护的一个全局环形缓冲区。

它主要用于:

  • 主从断线后,保存最近一段时间的写命令。
  • 从节点重连后,根据 offset 判断能否进行增量同步。

可以理解为:

repl_backlog_buffer 更偏向于“断线重连后的补偿数据”。

两者区别如下:

对比项 replication buffer repl_backlog_buffer
作用对象 每个从节点一个 主节点全局一个
数据结构 普通缓冲区 环形缓冲区
主要作用 保存待发送给从节点的数据 保存最近的写命令,用于增量同步
典型场景 全量同步期间、从节点处理变慢 主从断线重连
是否会覆盖旧数据 通常不是环形覆盖逻辑 会环形覆盖旧数据

4.6 怎么优化:尽量让断线重连走增量同步

全量同步开销很大,因为它涉及 BGSAVE、RDB 文件传输、从节点清空旧数据并重新加载 RDB。相比之下,增量同步只需要补齐断线期间缺失的写命令,成本低很多。

所以生产环境中,一个重要优化目标就是:尽量让主从断线重连后走增量同步,而不是全量同步

要做到这一点,核心是让 repl_backlog_buffer 能覆盖从节点断线期间产生的写入数据。

一个常见估算公式是:

1
repl_backlog_buffer 大小 = 从节点平均重连时间 × 主节点每秒产生的写命令数据量

也就是:

1
second * write_size_per_second

例如:

  • 从节点断线后平均 30 秒恢复。
  • 主节点每秒产生 2MB 写命令数据。
  • 那么 backlog 至少需要 60MB。

实际配置时,通常还要留出冗余,避免网络抖动或写入峰值导致 backlog 不够用。

除了调大 repl_backlog_buffer,还可以从下面几个方向优化:

  • 降低主从断线时间:保证 Redis 节点之间网络稳定,减少长时间断连。
  • 监控复制延迟:关注主从 offset 差距,及时发现从节点落后过多的问题。
  • 避免从节点长期阻塞:从节点阻塞越久,越容易错过 backlog 中的数据。
  • 根据写入峰值预留空间:不要只按平均写入量估算,最好考虑业务高峰期写入速度。

五、怎么解决主节点故障:Sentinel 哨兵机制

主从复制解决了数据同步问题,但它没有解决主节点故障后的自动切换问题。

如果 Master 宕机,Replica 虽然保存了数据副本,但它不会天然自动变成新的 Master。此时就需要 Sentinel 来完成故障发现、投票确认、Leader 选举、新主选择和故障转移。

这一节先讲 Sentinel 的故障转移机制。先把“Sentinel 到底怎么完成一次主从切换”讲清楚,再看为什么 Sentinel 自己也不能是单点,为什么还需要组成哨兵集群。

5.1 Sentinel 是什么

Redis Sentinel 是 Redis 官方提供的高可用方案。

它的核心作用是:

监控 Redis 主从节点状态,并在主节点故障时自动完成故障转移。

Sentinel 本身是一个独立进程。它不会直接存储业务数据,也不是 Redis 读写请求的代理层,而是专门负责监控、判断和协调故障转移。

Sentinel 主要有四个职责:

  • 监控:持续检测 Master 和 Replica 是否存活。
  • 通知:发现节点异常后,可以通知管理员或其他系统。
  • 故障转移:主节点故障后,自动选择一个从节点升级为新主节点。
  • 配置提供者:客户端可以通过 Sentinel 获取当前主节点地址。

这里要注意一点:

Sentinel 通常不直接代理 Redis 的读写命令。客户端一般是先向 Sentinel 查询当前 Master 地址,然后再直接连接 Redis 节点执行命令。

所以 Sentinel 更像是 Redis 高可用架构里的“监控者”和“协调者”,而不是请求转发代理。

5.2 故障转移从哪里开始:主观下线

一次故障转移的起点,是某个 Sentinel 发现 Master 长时间没有响应。

每个 Sentinel 会定期向 Redis 节点发送心跳检测,例如 PING。如果 Master 在指定时间内没有响应,当前 Sentinel 就会先把它标记为主观下线

相关配置类似:

1
sentinel down-after-milliseconds <master-name> <milliseconds>

比如配置为 30000,就表示当前 Sentinel 在 30 秒内没有收到 Master 有效响应时,会认为 Master 主观下线。

但这里要注意:主观下线只是“当前这个 Sentinel 自己的判断”。在分布式环境中,网络抖动、链路异常、机器负载过高都可能导致某个 Sentinel 误判,所以单个 Sentinel 不能直接发起故障转移。

5.3 从主观下线到客观下线:让其他 Sentinel 一起确认

当一个 Sentinel 判断 Master 主观下线后,它不会立刻切换主节点,而是会询问其他 Sentinel:你们是否也认为这个 Master 下线了?

它会发送类似命令:

1
SENTINEL is-master-down-by-addr

其他 Sentinel 会根据自己和 Master 的连接情况进行回复。

如果认为 Master 下线的 Sentinel 数量达到 quorum 阈值,那么 Master 就会被判定为客观下线

这个阶段可以理解为:

  • 主观下线:一个 Sentinel 说“我觉得 Master 挂了”。
  • 客观下线:足够多 Sentinel 说“我们也觉得 Master 挂了”。
    客观下线
    流程如下:
    flowchart LR
      A[Sentinel 定期 PING Master] --> B{Master 超时未响应}
      B -->|否| C[继续正常监控]
      B -->|是| D[当前 Sentinel 标记主观下线]
      D --> E[向其他 Sentinel 询问]
      E --> F{同意下线数量达到 quorum}
      F -->|否| G[暂不故障转移]
      F -->|是| H[Master 被判定为客观下线]

到这里为止,Sentinel 只是确认了“Master 大概率真的不可用了”。但确认故障并不等于已经完成故障转移,后面还需要选出一个 Sentinel 来负责执行切换。

5.4 客观下线之后:选出 Leader Sentinel

Master 被判定为客观下线后,并不是所有 Sentinel 都同时执行故障转移。

如果多个 Sentinel 同时操作,可能会产生混乱,比如多个从节点同时被提升为 Master。因此 Sentinel 集群需要先选出一个 Leader Sentinel,由它统一执行后续故障转移流程。

Leader Sentinel 的选举大致是这样的:

  • 最先发现 Master 客观下线的 Sentinel 发起选举请求。
  • 它向其他 Sentinel 请求投票。
  • 每个 Sentinel 在一个选举周期内通常只能投一票。
  • 获得多数票的 Sentinel 成为 Leader Sentinel。

这一步很关键。因为故障转移不是“大家一起改配置”,而是必须由一个被多数 Sentinel 认可的 Leader 来执行。

5.5 Leader 如何选择新的主节点

Leader Sentinel 选出来之后,下一步就是从多个 Replica 中选择一个最合适的节点升级为新的 Master。

这一步不能随便选,因为不同从节点的数据新旧程度、健康状态、配置优先级都可能不同。如果选了一个数据太旧或者状态不健康的从节点,就可能扩大故障影响。

大致选择规则如下:

优先级 筛选条件 说明
1 排除已断线或响应超时的从节点 确保候选节点在线且健康
2 排除长时间未与主节点同步的从节点 避免选择数据过旧的节点
3 replica-priority 排序 值越小优先级越高,默认值通常为 100
4 按复制偏移量排序 偏移量越大,说明数据越新
5 按 Run ID 字典序排序 最后的兜底规则,保证结果确定

其中 replica-priority 可以由管理员配置。

如果某个从节点机器性能更好、内存更大、网络更稳定,就可以把它的 replica-priority 设置得更小,让它在故障转移时更优先成为新的主节点。

需要注意:

replica-priority 数值越小,优先级越高。如果设置为 0,表示这个从节点不会被选为主节点。

5.6 执行故障转移:升级新主、重配从节点、通知客户端

当 Leader Sentinel 选出目标 Replica 后,真正的故障转移才开始执行。

第一步,Leader Sentinel 会让目标 Replica 取消复制旧 Master:

1
REPLICAOF NO ONE

这个命令执行后,目标 Replica 就会升级为新的 Master。

第二步,Leader Sentinel 会让其他 Replica 改为复制新的 Master:

1
REPLICAOF <new_master_ip> <new_master_port>

这样 Redis 主从关系就从“旧 Master → 多个 Replica”切换成了“新 Master → 其他 Replica”。

第三步,Sentinel 会发布 +switch-master 事件。客户端如果使用支持 Sentinel 的客户端库,就可以感知 Master 地址变化,并更新后续连接目标。

第四步,如果旧 Master 后续恢复上线,Sentinel 不会让它重新成为 Master,而是会把它降级为新 Master 的 Replica。这样可以避免旧 Master 恢复后产生双主问题。

完整故障转移流程可以串起来看:

flowchart LR
    A[Master 无响应] --> B[某个 Sentinel 判定主观下线]
    B --> C[询问其他 Sentinel]
    C --> D{达到 quorum}
    D -->|否| E[继续监控,不切换]
    D -->|是| F[Master 客观下线]
    F --> G[Sentinel 集群选举 Leader]
    G --> H[Leader 筛选最合适的 Replica]
    H --> I[目标 Replica 执行 REPLICAOF NO ONE]
    I --> J[目标 Replica 升级为新 Master]
    J --> K[其他 Replica 执行 REPLICAOF 新 Master]
    K --> L[Sentinel 发布 switch-master 事件]
    L --> M[客户端更新 Master 地址]
    M --> N[旧 Master 恢复后降级为 Replica]

这一整条链路才是 Sentinel 故障转移的完整过程:先发现异常,再让多个 Sentinel 共同确认,再选出一个执行者,接着选择最新、最健康、优先级最高的从节点,最后完成主从关系重构和客户端通知。


六、为什么 Sentinel 也要组成集群

理解了 Sentinel 的故障转移流程之后,就会发现一个新的问题:既然 Sentinel 要负责判断 Master 是否下线,还要负责选举 Leader、选择新主节点、执行故障转移,那么 Sentinel 自己如果只有一个,也会成为新的单点。

所以完整的 Sentinel 机制不能只讲“怎么切换 Master”,还必须讲“Sentinel 集群是怎么组成的,以及 Sentinel 节点挂了怎么办”。

6.1 为什么不能只部署一个 Sentinel

如果只部署一个 Sentinel,它确实可以监控 Master,也可以发现 Master 不响应。

但问题是:这个 Sentinel 自己可能会挂,也可能因为网络抖动误判 Master 下线。

如果所有判断都依赖一个 Sentinel,那么它既是监控者,又是唯一决策者,一旦它出问题,高可用机制本身就不可靠了。

所以 Sentinel 的设计思路不是让某一个 Sentinel 永远不挂,而是部署多个 Sentinel,通过多个节点之间的确认和投票,降低误判概率,并保证少数 Sentinel 宕机时,整体机制仍然可用。

6.2 哨兵集群是如何组成的

一个 Sentinel 集群不是通过某个中心节点统一管理出来的,而是多个 Sentinel 进程在启动后,通过监控同一个 Master,逐步发现 Redis 主从节点和其他 Sentinel 节点,最终形成一个互相通信、互相确认的监控集群。

通常每个 Sentinel 启动时,配置文件里至少会写入它要监控的 Master 信息,例如:

1
sentinel monitor <master-name> <ip> <port> <quorum>

这行配置表达的含义是:当前 Sentinel 要监控哪个 Master,以及判断这个 Master 客观下线时至少需要多少个 Sentinel 同意。

Sentinel 集群的组成过程大致可以分为三步。

第一步,Sentinel 根据配置连接 Master

Sentinel 启动后,会根据 sentinel monitor 中配置的 IP 和端口连接 Master,并周期性向 Master 发送 PINGINFO 等命令。通过 INFO replication,Sentinel 可以拿到 Master 当前有哪些 Replica,于是它不仅能监控 Master,也能自动发现并监控 Master 下面的从节点。

第二步,Sentinel 之间互相发现

Sentinel 会通过 Redis 的 Pub/Sub 机制,在被监控 Master 的 __sentinel__:hello 频道上发布自己的信息,例如自己的 IP、端口、运行 ID、配置版本等。其他 Sentinel 也会订阅这个频道。

这样一来,只要多个 Sentinel 监控的是同一个 Master,它们就可以通过这个频道互相发现对方,逐渐形成 Sentinel 集群。

第三步,Sentinel 之间建立连接并持续通信

当一个 Sentinel 发现其他 Sentinel 后,会和对方建立连接,并持续进行心跳检测和状态交换。后续判断主观下线、客观下线、Leader 选举、故障转移授权,都依赖这些 Sentinel 之间的通信。

这个过程可以这样理解:

flowchart LR
    S1[Sentinel 1 启动] --> M[连接配置中的 Master]
    S2[Sentinel 2 启动] --> M
    S3[Sentinel 3 启动] --> M

    M --> R1[通过 INFO 发现 Replica 1]
    M --> R2[通过 INFO 发现 Replica 2]

    S1 --> C[向 __sentinel__:hello 发布自身信息]
    S2 --> C
    S3 --> C

    C --> D[Sentinel 之间互相发现]
    D --> E[建立连接并交换状态]
    E --> F[形成 Sentinel 集群]

所以 Sentinel 集群的形成不是一次性静态完成的,而是一个动态发现过程:先根据配置找到 Master,再通过 Master 发现 Replica,最后通过 __sentinel__:hello 频道发现其他 Sentinel。

6.3 Sentinel 节点挂了怎么办

Sentinel 节点之间也会互相通信和检测。如果某个 Sentinel 节点宕机,其他 Sentinel 会发现它不可达,但不会因此影响 Redis 主从节点继续运行。

关键在于:剩余 Sentinel 数量是否还能够满足客观下线判断和 Leader 选举所需的多数派。

比如部署 3 个 Sentinel:

  • 挂掉 1 个 Sentinel,剩下 2 个 Sentinel 仍然可以形成多数派,通常还能完成 Master 客观下线判断和 Leader 选举。
  • 挂掉 2 个 Sentinel,只剩 1 个 Sentinel,就很难再完成可靠的多数派确认。即使它自己认为 Master 挂了,也可能无法安全地发起故障转移。

这也是 Sentinel 通常建议至少部署 3 个,并且尽量部署奇数个的原因。

奇数个 Sentinel 的好处是更容易形成多数派,例如:

  • 3 个 Sentinel,至少 2 个同意才能形成多数。
  • 5 个 Sentinel,至少 3 个同意才能形成多数。

如果 Sentinel 数量是偶数,就更容易出现票数对半的情况,导致 Leader 选举无法顺利完成。

可以把 Sentinel 自身故障处理理解成下面这个过程:

flowchart LR
    A[Sentinel 集群运行中] --> B[某个 Sentinel 节点宕机]
    B --> C[其他 Sentinel 发现它不可达]
    C --> D{剩余 Sentinel 是否还能形成多数派}
    D -->|能| E[继续监控 Redis 并具备故障转移能力]
    D -->|不能| F[无法可靠完成 Leader 选举]
    F --> G[需要恢复 Sentinel 节点或补充新节点]

因此,Sentinel 自身高可用的关键点有三个:

  • 多节点部署:不要只部署一个 Sentinel,至少部署 3 个。
  • 奇数个部署:通常部署 3 个、5 个,方便形成多数派。
  • 分散部署:Sentinel 最好部署在不同机器或不同故障域中,避免一台机器宕机导致多个 Sentinel 同时不可用。

七、总结

Redis 从单节点走向主从架构,首先解决的是读性能瓶颈:主节点负责写,从节点负责读,读请求可以被多个从节点分摊。

但主从架构带来了两个新问题。

第一个问题是主从数据如何保持同步。Redis 通过主从复制解决这个问题:第一次连接时走全量同步,正常连接后通过长连接进行命令传播,断线重连后优先尝试增量同步。如果缺失数据已经被 repl_backlog_buffer 覆盖,则只能重新走全量同步。

第二个问题是主节点故障后如何自动恢复。Redis 通过 Sentinel 解决这个问题:Sentinel 会先完成主观下线判断,再让多个 Sentinel 共同确认客观下线,然后选举 Leader Sentinel,由 Leader 选择合适的 Replica 升级为新 Master,并让其他 Replica 改为复制新 Master。

理解 Sentinel 的故障转移流程后,就能明白为什么 Sentinel 自己也要组成集群。因为 Sentinel 不只是一个监控进程,它还参与故障确认、投票和 Leader 选举。如果只有一个 Sentinel,Sentinel 自身就会成为单点;只有多个 Sentinel 通过互相发现、互相通信和多数派机制协作,才能保证故障转移过程更加可靠。

需要注意的是,本文主要讨论的是主从复制和 Sentinel。Redis 高可用体系中,持久化同样很重要,因为它关系到 Redis 重启后的数据恢复能力,这部分可以单独展开。

最后可以用一句话概括本文内容:主从复制解决数据副本和读扩展问题,Sentinel 解决主节点故障后的自动切换问题,而 Sentinel 集群则保证故障转移决策本身不依赖单点。