Redis 哨兵机制详解
哨兵机制的概念
Redis 有三种模式:分别是主从复制、哨兵模式、集群模式,后两者可以保证高可用。
- 哨兵(Sentinel)机制是什么
- 哨兵机制主要用于 Redis 主从架构下的故障检测与自动主从切换。
- 哨兵一个专门用于高可用的 Redis 组件(节点),不是用于存储数据的。
- 哨兵不参与数据同步和读写,而是专门负责监控、故障切换和通知客户端谁是主节点。
- 哨兵进程通常和 Redis 节点分开部署(也可以部署在同一台机器上,但进程独立)。
- 通常运行多个哨兵实例(即哨兵集群),实现冗余和仲裁。每个哨兵都会连接至 Master 节点和所有 Slave 节点,监控它们的状态信息。
哨兵(Sentinel)节点的作用是什么
- 监控(Monitor):持续检查主节点和从节点是否存活(通过 PING 等)。
- 通知(Notification):检测到故障后通知系统管理员或其他服务。
- 自动故障转移(Failover):如果主节点不可用,选举一个从节点提升为新主节点。
- 服务发现(Discovery):提供主节点的地址信息给客户端(可供自动重连);如果发生了故障转移,会通知客户端新的主节点地址。
哨兵(Sentinel)节点不是从节点(Slave)
- 哨兵节点不负责同步数据,也不参与数据读写。
- 它只是通过 Redis 协议连接 Redis 实例,执行命令如 INFO、PING 来感知状态。
- 哨兵节点不保存业务数据,最多保存一些监控状态(内存中)。
为什么需要哨兵(Sentinel)机制
- Redis 的主从复制机制主要用于实现数据备份和读请求的负载分担,但它本身并不具备自动容错和主节点自动切换的能力。因此,单纯依赖主从复制并不能保证系统的高可用性。具体表现如下:
- 需要人工介入:当主节点发生故障时,Redis 本身无法自动完成故障切换,需要运维人员手动将某个从节点提升为新的主节点,并重新配置其他从节点同步新的主节点。
- 单点故障风险:主节点作为写操作的唯一入口,一旦宕机,系统的写操作将完全中断,严重影响服务可用性。
- 主节点写入能力受限:Redis 是单线程模型,主节点的写入吞吐受限于单机性能,无法横向扩展。
- 单机节点存储容量有限:Redis 通常运行在内存中,主节点的物理内存限制了存储能力。
- Redis 哨兵机制应运而生,用于增强 Redis 在主从架构下的高可用性。它具备以下能力:
- 哨兵节点自动监控 Redis 实例状态(包括主节点和从节点)。
- 主节点宕机时,自动完成主从切换(Failover)。
- 通知支持 Sentinel 机制的客户端连接新的主节点。
- 协助管理 Redis 主从结构并确保一致性。
- 若需要进一步提高 Redis 的可用性与扩展性,还可以使用 Redis Cluster(集群),它支持数据分片(水平扩展),具备原生的多主多从架构、高可用、以及容错能力。
- Redis 的主从复制机制主要用于实现数据备份和读请求的负载分担,但它本身并不具备自动容错和主节点自动切换的能力。因此,单纯依赖主从复制并不能保证系统的高可用性。具体表现如下:
哨兵机制的原理
Redis 哨兵机制是通过在独立的哨兵节点上运行特定的哨兵进程来实现的。这些哨兵进程监控主从节点的状态,并在出现故障时自动完成故障转移,并通知应用方,实现高可用性。
(1) 哨兵选举:
- 在启动时,每个哨兵节点都会参与选举,其中一个哨兵节点会被选为领导者(Leader),负责协调其他哨兵节点执行故障转移。选举过程如下:
- 每个在线的哨兵节点都有资格成为领导者。当某个哨兵判断主节点不可用后,会向其他哨兵节点发送
is-master-down-by-addr命令,请求判断主节点状态并征求选票,希望被选为本轮的领导者。 - 其他哨兵节点在收到该命令后,会根据自身判断和是否已经投票的情况,决定是否同意对方成为领导者(每个哨兵在同一轮选举中只能投票一次)。
- 如果某个哨兵节点获得的选票数达到或超过
总哨兵节点数 / 2 + 1(即超过半数),则该哨兵节点将成为本轮选举的领导者;如果未能获得足够票数,则会进入下一轮选举,直到选出领导者为止。
- 每个在线的哨兵节点都有资格成为领导者。当某个哨兵判断主节点不可用后,会向其他哨兵节点发送
- 在启动时,每个哨兵节点都会参与选举,其中一个哨兵节点会被选为领导者(Leader),负责协调其他哨兵节点执行故障转移。选举过程如下:
(2) 哨兵监控主从节点:
- 哨兵节点通过发送命令周期性地检查主从节点的健康状态,包括主节点是否在线、从节点是否同步等。
- 如果哨兵节点发现主节点不可用,它会触发一次故障转移操作,而且是由哨兵领导者负责处理主节点的故障转移。
(3) 哨兵执行故障转移:
- 一旦主节点被判定为不可用,哨兵节点会执行故障转移操作。它会从当前的从节点中选出一个新的主节点,并将其他从节点切换到新的主节点。这样,缓存系统可以继续提供服务,而无需人工介入。
- 故障转移过程:
- 由哨兵节点定期监控主节点是否出现故障,哨兵节点会定期向主节点发送心跳 PING 来确认主节点是否存活。
- 如果主节点在 “一定时间范围” 内不响应 PONG 或者是回复了一个错误消息,那么这个哨兵节点会主观地(单方面地)认为这个主节点已经不可用了。
- 确认新主节点:
- 过滤掉不健康的从节点(如已下线、网络断连、长时间未响应哨兵 PING 命令的节点)。
- 在剩余的健康从节点中,优先选择优先级(Priority)最高的节点。
- 如果有多个从节点优先级相同,则选择复制偏移量(Replication Offset)最大的节点,即数据最接近原主节点的从节点。
- 若优先级和复制偏移量都相等,则选择节点 ID 字典序最小的节点作为新主节点。
(4) 客户端重定向:
- 当主节点出现故障时,哨兵节点会自动发起主从切换(故障转移),选举一个新的从节点作为新的主节点。
- 哨兵节点不会直接通知客户端新的主节点地址,而是提供一个服务发现机制。客户端需要通过支持哨兵机制的客户端,从哨兵节点动态获取当前的主节点地址。
- 这样一来,客户端可以在主节点切换后,通过哨兵节点重新获取主节点信息,从而无感知地完成主节点重连,保证业务连续性。
- 此外,哨兵节点还会持续监控所有主节点和所有从节点的运行状态,如果某个从节点出现故障,哨兵节点会将其标记为下线;一旦从节点恢复,哨兵节点会自动将其重新加入主从复制架构,并使其同步当前主节点的数据,以维持整个架构的完整性。
哨兵的部署架构
Redis Sentinel(哨兵)本身是一个分布式系统,通常以哨兵集群的形式部署,多个哨兵节点之间可以协同工作,保障系统的高可用性(如下图所示)。

- 哨兵(Sentinel)故障转移的核心概念
| 概念 | 解释 | 作用 |
|---|---|---|
quorum(法定票数) | Master 节点从主观下线(sdown)到客观下线(odown)所需的同意哨兵数(即最少 N 个哨兵同时判断 Master 为宕机) | 确保哨兵对 Master 宕机的判断具有一定共识,避免误判 |
majority(多数哨兵) | 执行故障转移操作时,要求同意执行故障转移的哨兵数量必须达到总数的一半以上,否则不会执行故障转移 | 为了避免「脑裂」现象,即多个哨兵在不同网络分区中同时尝试进行故障转移,导致系统不一致或混乱 |
哨兵(Sentinel)的部署架构是什么
- Redis Sentinel(哨兵)本身是一个分布式系统,通常以哨兵集群的形式部署,多个哨兵节点之间可以协同工作,保障系统的高可用性。
- 哨兵通常以集群的形式部署,这样是为了保证哨兵的高可用性
- 哨兵集群要求至少需要部署 3 个哨兵实例,否则可能无法保证故障转移的正常执行,同时也为了实现多数投票机制,并提高容错能力。
- 哨兵集群 + Redis 主从架构能够提供高可用性,但无法做到数据零丢失。故障转移过程中可能存在数据未完整同步到从节点的风险,因此仅适用于对可用性要求高、但允许少量数据丢失的场景。
- 由于哨兵 + Redis 主从是一种相对复杂的部署架构,建议在测试环境和生产环境中都进行充分的测试与故障演练,确保系统在各种异常情况下都能稳定运行。
- 在发生故障转移时,是否将一个主节点判定为宕机,必须经过多数哨兵节点(Quorum)的同意。这涉及到分布式选举机制,用于确保故障转移的判断和执行具备一致性和可靠性。
- 即使部分哨兵节点发生故障,哨兵集群仍然能够正常工作。这是因为哨兵本身作为高可用机制的一部分,必须具备容错能力,若其自身是单点的,那就违背了其设计初衷。
- 目前使用的是 Sentinel
2.x 版本。与 Sentinel1.x相比,Sentinel2.x重写了大量核心代码,主要目的是简化故障转移流程、提升算法健壮性和系统稳定性,使其更适用于生产环境中的高可用场景。
- 哨兵通常以集群的形式部署,这样是为了保证哨兵的高可用性
- Redis Sentinel(哨兵)本身是一个分布式系统,通常以哨兵集群的形式部署,多个哨兵节点之间可以协同工作,保障系统的高可用性。
为什么 Redis 哨兵集群只有 2 个节点会无法正常工作
- 以部署了 2 个哨兵实例的场景为例(配置:
quorum = 1):1
2
3
4+----+ +----+
| M1 |---------| R1 |
| S1 | | S2 |
+----+ +----+ - 当主节点 M1 宕机时,只要 S1 和 S2 中任意一个哨兵认为 M1 宕机,就可以发起主观下线(Subjective Down,简称 sdown)判断。接着,两个哨兵会通过选举机制,选举出其中一个哨兵来执行故障转移操作。
- 但是,哨兵系统执行真正的故障转移时,还需要满足
majority要求,也就是多数哨兵节点同意执行故障转移。例如:- 2 个哨兵时,要求
majority = 2 - 3 个哨兵时,要求
majority = 2 - 4 个哨兵时,要求
majority = 3 - 5 个哨兵时,要求
majority = 3
- 2 个哨兵时,要求
- 因此在 2 个哨兵节点的场景中,只有当两个哨兵都正常运行时,才满足
majority的要求,才允许执行故障转移操作。 - 如果运行主节点 M1 和哨兵 S1 的那台机器宕机了,意味着主节点 M1 和哨兵 S1 同时失效,只剩下从节点 R1 和哨兵 S2 仍在运行。此时虽然还有一个哨兵存在(S2),但由于无法满足
majority要求,故障转移将不会被执行,导致缓存系统始终处于不可用状态。
- 以部署了 2 个哨兵实例的场景为例(配置:
Redis 经典的 3 节点哨兵集群架构
- 以部署了 3 个哨兵实例的场景为例(配置:
quorum = 2):1
2
3
4
5
6
7
8
9+----+
| M1 |
| S1 |
+----+
|
+----+ | +----+
| R2 |----+----| R3 |
| S2 | | S3 |
+----+ +----+ - 如果主节点 M1 所在的机器宕机,意味着主节点 M1 和哨兵 S1 同时失效,剩下的两个哨兵 S2 和 S3 仍在运行。此时:
- 哨兵 S2 和 S3 可以一致地判断主节点 M1 宕机了(满足
quorum = 2),从而形成客观下线(odown)判断; - 接着,S2 和 S3 两个哨兵会通过选举机制,选举出其中一个哨兵负责执行故障转移操作;
- 因为 3 个哨兵的
majority要求为 2,而当前恰好有 2 个哨兵仍然存活,所以满足故障转移所需的条件。
- 哨兵 S2 和 S3 可以一致地判断主节点 M1 宕机了(满足
- 因此,在这种经典的 3 节点哨兵集群架构下,即使一台机器宕机,只要剩余的 2 个哨兵还在正常运行,依然可以完成故障转移,从而保证整个哨兵集群的高可用性。
- 以部署了 3 个哨兵实例的场景为例(配置:
特别注意
- 无论 Redis 是一主一从、一主多从的复制架构,都可以使用 Redis 的哨兵机制,官方更推荐使用一主多从的复制架构,这样可用性更高。
哨兵机制的使用
在 SpringBoot 项目中,使用 Lettuce 客户端连接 Redis 的一主多从 + 哨兵模式时,只需要在 application.yml 正确配置哨兵信息,SpringBoot 就会自动识别并创建 Lettuce 连接。
- SpringBoot 配置文件示例(
application.yml)
1 | spring: |
- 若希望使用从节点读取数据(主节点默认可以读写,但从节点只读不可写),可以使用以下 SpringBoot 配置信息
1 | spring: |
| Lettuce 的配置值 | 含义 |
|---|---|
MASTER | 所有请求都从主节点读取(默认) |
MASTER_PREFERRED | 优先主节点,主节点不可用时才从从节点读取 |
REPLICA(或 SLAVE) | 所有请求都从从节点读取 |
REPLICA_PREFERRED | 优先从从节点读取,从节点不可用时回退到主节点 |
NEAREST | 从网络延迟最小的节点读取(需要集群拓扑支持) |
- Redis 哨兵配置文件示例(
redis-sentinel.conf)
1 | # 监控名为 mymaster 的主节点,IP 是 192.168.1.100,端口 6379, |
- 引入 Lettuce 依赖,使用 Lettuce 的默认连接工厂
1 | <!-- SpringBoot 默认使用 Lettuce 作为 Redis 客户端 --> |
- Redis 连接验证代码
1 |
|
- SpringBoot 配置(
application.yml)的注意事项- 主从节点的 IP 和端口不需要配置,哨兵模式下 Lettuce 客户端能自动发现主从拓扑结构。
mymaster一定要和哨兵配置文件(redis-sentinel.conf)里设置的一致。- Lettuce 默认支持哨兵模式,一般不需要额外配置;但是,如果哨兵模式使用 SSL,则需要显式配置连接工厂。
- SpringBoot 的默认连接池支持读写分离,只需要配置 Lettuce 的
read-from属性即可生效。 - 在 Redis 的主从复制或者集群架构中,主节点可以读写,但从节点默认是只读的,即可以响应读请求(如
GET、MGET等),但不能写入数据。
特别注意
无论 Redis 是一主一从、一主多从还是集群架构,Redis 的主节点都可以读写,从节点默认是可以读(只读,不能写入);但如果想实现真正的读写分离或者读负载均衡,还需要在客户端进行配置或开发支持(比如 Jedis、Lettuce 都支持手动配置是否访问从节点),因为 Redis 本身不会自动将读请求发送给从节点。由于 Redis 的主从同步可能存在延迟,如果业务对读取一致性要求较高(如读取后马上更新),那么就不要使用从节点读取数据。
哨兵机制的问题
如何避免脑裂现象
Redis 哨兵集群的脑裂现象是指什么
- 指在出现网络分区或者部分哨兵节点失联的情况下,多个哨兵节点在没有达到
majority(多数哨兵)共识的前提下,分别认为 Master 节点宕机并发起故障转移,导致出现两个或多个 Master 节点,从而造成数据不一致或系统混乱。 - 值得一提的是,除了主从同步延迟外,脑裂现象也会导致 Redis 集群丢失部分缓存数据。
- 指在出现网络分区或者部分哨兵节点失联的情况下,多个哨兵节点在没有达到
Redis 哨兵集群如何避免脑裂现象
- 设置合适的
quorum(法定票数),即某个哨兵要认为主节点下线,必须要有至少quorum个哨兵达成共识。 - 真正的故障转移必须经过
majority个哨兵(多数哨兵)投票通过,避免少数哨兵单方面误判。 - 因此,只要哨兵总数为奇数(如 3 个或 5 个),且大多数哨兵能互通,就不会发生脑裂现象。
- 举个例子,假设集群中有 3 个哨兵,并配置
quorum = 2,那么:- 只有至少 2 个哨兵都判断主节点不可用,才会触发故障转移;
- 此时,还需要过半(即至少 2 个)的哨兵同意发起故障转移,才能选出新的主节点;
- 如果由于网络问题分区成
1 + 2的两组哨兵,单独的哨兵将无法满足quorum和majority条件,这样就不会误判,从而避免脑裂。
- 举个例子,假设集群中有 3 个哨兵,并配置
- 设置合适的
| 概念 | 解释 | 作用 |
|---|---|---|
quorum(法定票数) | Master 节点从主观下线(SDOWN)到客观下线(ODOWN)所需的同意哨兵数(即最少 N 个哨兵同时判断 Master 为宕机) | 确保哨兵对 Master 宕机的判断具有一定共识,避免误判 |
majority(多数哨兵) | 执行故障转移操作时,要求同意执行故障转移的哨兵数量必须达到总数的一半以上,否则不会执行故障转移 | 为了避免「脑裂」现象,即多个哨兵在不同网络分区中同时尝试进行故障转移,导致系统不一致或混乱 |
网络分区是指什么
网络分区(Network Partition)是指由于网络故障,集群中的一部分节点之间无法正常通信,被分隔成了两个或多个 "孤岛",每个孤岛只能看到自身可达的节点,看不到其他节点。在 Redis 的哨兵集群架构中,网络分区通常指的是:部分哨兵与主节点失去连接、部分哨兵之间失去通信、主从节点之间断联。这些都可能导致:误判主节点下线、多个哨兵同时发起故障转移、出现多个主节点(脑裂)。
如何避免数据丢失
Redis 的哨兵机制主要用于主从架构下的故障检测与自动主备切换,但在某些特殊场景下,哨兵机制可能会导致数据丢失,主要包括以下两种情况:
(1) 异步复制导致的数据丢失
- Redis 主从节点之间的数据同步是异步复制,这意味着主节点写入的数据不会立即同步到从节点。
- 当主节点(Master)宕机时,可能仍有部分数据尚未同步写入到从节点(Slave)。
- 此时,如果哨兵进行故障转移,从节点被提升为新的主节点,那么这些未同步的数据将永久丢失。
- 这种情况的本质是主从延迟造成的数据不一致,属于设计上的权衡(异步复制换取更高性能和低延迟)。
- Redis 主从节点之间的数据同步是异步复制,这意味着主节点写入的数据不会立即同步到从节点。
(2) 脑裂(Split-Brain)导致的数据丢失
- 脑裂指的是:主节点与其他哨兵和从节点之间发生网络分区,导致其在局部网络中 “孤岛运行”,而在整体视角下却被认为已宕机。
- 在发生网络分区期间,哨兵可能判定主节点不可用(ODOWN - 客观不可用),并发起故障转移,将某个从节点提升为新的主节点。
- 但由于旧主节点实际上仍在运行,客户端可能仍将数据写入旧主节点,形成了两个 “主节点”(双主)。
- 当网络恢复时,旧主节点会被哨兵强制转为从节点,并从新主节点复制数据,这会导致旧主节点上的数据被清空。
- 因此,在发生网络分区的这段时间内,写入旧主节点的数据会丢失,因为它从未被同步到新主节点上,并且在旧主节点恢复后被覆盖掉。
- 脑裂指的是:主节点与其他哨兵和从节点之间发生网络分区,导致其在局部网络中 “孤岛运行”,而在整体视角下却被认为已宕机。
Redis 提供以下两个配置项用于控制写请求行为,从而减少异步复制和脑裂导致的数据丢失。
min-slaves-to-write 1:要求至少有 1 个从节点处于正常连接状态,主节点才允许写入数据。min-slaves-max-lag 10:要求从节点的复制延迟(ACK 返回的时间)不能超过 10 秒,主节点才允许写入数据。
当主节点检测到可以正常连接的从节点数量不足,或者所有从节点的复制延迟都超过 10 秒,那么主节点将拒绝客户端的写请求。
(1) 减少异步复制导致的数据丢失
- Redis 的主从复制是异步的。如果主节点宕机,而有些数据尚未同步到从节点,那么这些数据将永久丢失。
- 通过设置
min-slaves-max-lag,主节点可以感知从节点复制数据的延迟。如果从节点响应太慢(比如都超过了 10 秒),主节点会拒绝客户端的写入请求,防止继续写入大量数据而无法同步,从而将主节点宕机时可能丢失的数据限制在一个可控的范围(如 10 秒)内。
(2) 减少脑裂导致的数据丢失
- 在出现脑裂的情况下,客户端可能仍向旧主节点写入数据,形成两个主节点(双主),导致数据不一致和丢失。
- 通过
min-slaves-to-write和min-slaves-max-lag这两个配置项,可以防止脑裂主节点继续接受写请求:- 如果旧主节点失去了所有从节点的连接;
- 且在
min-slaves-max-lag时间(如 10 秒)内未收到任何从节点的 ACK 消息; - 那么主节点将自动停止接受写请求。
- 这样,即使发生脑裂,旧主节点也会在 10 秒内拒绝写入数据,最多只会丢失 10 秒的数据,大大降低了数据丢失的风险。
当 Redis 主节点拒绝写请求时,客户端可以采取如下策略进行容灾处理:
- (1) 客户限流处理
- 对接口请求进行限流处理,减慢请求涌入的速度,防止请求堆积或爆发式增长。
- (2) 异步重试机制
- 将待写入的数据缓存在本地磁盘或者 Kafka 消息队列中。
- 客户端定时从本地磁盘或者 Kafka 队列中获取数据(例如每隔 10 分钟),然后尝试将数据重新写回 Redis 主节点。
