Sentinel 高可用
系统梳理 Redis Sentinel 的监控、通知、服务发现、SDOWN、ODOWN、quorum、多数派、Leader 选举、故障转移、网络分区、RTO、RPO 与 Go 客户端接入。
第 16 章:Sentinel 高可用
1. 本章定位
Redis 主从复制解决了数据副本问题,但仅有主从复制并不会自动完成故障检测、主节点选举、角色切换和客户端重定向。Redis Sentinel 正是用于补齐这些能力的高可用组件。
Sentinel 面向非 Redis Cluster 的单主多副本部署,提供四类核心能力:
- 持续监控主节点、副本和其他 Sentinel。
- 在节点异常时产生通知。
- 主节点故障后自动选择副本并完成故障转移。
- 为客户端提供当前主节点地址,实现服务发现。
Sentinel 不负责数据分片,也不提供强一致复制、零数据丢失或备份能力。它解决的是“一个 Redis 主节点发生故障后,系统如何自动恢复可写服务”,而不是“如何把超大数据集拆到多个节点”。 (Redis)
2. 学习目标
完成本章后,应能够:
- 解释 Sentinel 的监控、通知、故障转移和服务发现能力。
- 准确区分主观下线 SDOWN 与客观下线 ODOWN。
- 解释
quorum与 Sentinel 多数派之间的区别。 - 描述 Sentinel Leader 选举及完整故障转移流程。
- 说明 Sentinel 如何选择待提升副本。
- 分析网络分区、脑裂、RTO 和 RPO。
- 使用
redis.NewFailoverClient编写支持 Sentinel 的 Go 客户端。 - 设计一个至少包含三个 Sentinel、跨独立故障域的生产拓扑。
2.1 本章边界与跳转
本章是 Sentinel 高可用的主章节。主从复制的数据同步和一致性边界先看第 15 章;Cluster 的分片、槽位、Gossip 和分片级高可用见[第 17 章](/blog/tech/Redis/17.Redis Cluster 分片与高可用/);Go 客户端如何配置 FailoverClient 的基础用法见[第 2 章](/blog/tech/Redis/02.使用 Go 操作 Redis/);故障转移期间连接风暴、重试和观测指标见第 20 章。
3. 核心概念
3.1 Sentinel 部署模型
一个典型拓扑如下:
+-------------------+
| Go Clients |
| Sentinel-aware |
+---------+---------+
|
查询当前主节点 / 故障后重新发现
|
+-------------------+-------------------+
| | |
+------v------+ +-----v-------+ +-----v-------+
| Sentinel S1 | | Sentinel S2 | | Sentinel S3 |
+------+------+ +------+------+ +------+------+
| | |
+--------------------+-------------------+
|
监控、投票、配置传播
|
+----------------+----------------+
| |
+------v------+ +------v------+
| Redis Master | ----复制-------> | Redis Replica|
+-------------+ +-------------+
生产环境通常至少部署三个 Sentinel,并尽量放在能够独立故障的物理机、虚拟机或可用区中。把三个 Sentinel 都放在同一台机器上,虽然进程数量是三个,但机器故障时它们会一起失效,不能形成真正的高可用。
Sentinel 的配置文件必须可写,因为 Sentinel 会把发现的副本、其他 Sentinel、配置纪元和故障转移后的新主节点写回配置文件。没有配置文件或配置文件不可写时,Sentinel 会拒绝启动。默认监听端口为 26379。 (Redis)
3.2 Sentinel 的四项核心职责
| 职责 | 作用 |
|---|---|
| 监控 | 周期性检查主节点、副本和其他 Sentinel 是否可达、角色是否正确 |
| 通知 | 通过日志、Pub/Sub 或通知脚本报告节点异常和故障转移事件 |
| 自动故障转移 | 选择合适副本,将其提升为主节点,再让其他副本复制新主节点 |
| 服务发现 | 客户端通过主节点逻辑名称查询当前主节点地址 |
需要注意,Sentinel 通常不位于业务请求的数据通路中。应用不会把每条 GET、SET 先发给 Sentinel;应用只在初始化或连接异常后向 Sentinel查询当前主节点,然后直接连接 Redis。
3.3 SDOWN:主观下线
SDOWN,Subjectively Down,主观下线,表示某一个 Sentinel 根据自己的观察,认为某个实例不可用。
Sentinel 会持续向实例发送 PING。如果在 down-after-milliseconds 指定的整个时间窗口内都没有收到可接受的回复,该 Sentinel 就会把实例标记为 SDOWN。
例如:
sentinel down-after-milliseconds mymaster 5000
表示一个 Sentinel 连续约 5 秒没有收到有效回复后,才会认为实例主观下线。
SDOWN 有以下特点:
- 只代表单个 Sentinel 的本地判断。
- 主节点、副本和其他 Sentinel 都可能被标记为 SDOWN。
- 单个 Sentinel 的 SDOWN 不足以触发自动故障转移。
- 被标记为 SDOWN 的副本不会成为待提升候选节点。
Redis 官方文档列出的有效 PING 回复包括 PONG、LOADING 和 MASTERDOWN;持续收不到有效回复才会形成 SDOWN。 (Redis)
3.4 ODOWN:客观下线
ODOWN,Objectively Down,客观下线,表示足够数量的 Sentinel 都认为某个主节点不可用。
当一个 Sentinel认为主节点 SDOWN 后,它会询问其他 Sentinel:
SENTINEL IS-MASTER-DOWN-BY-ADDR ...
如果至少有 quorum 个 Sentinel 都报告主节点不可达,本地 Sentinel 才会把主节点提升为 ODOWN。
ODOWN 的重要边界是:
- ODOWN 只应用于主节点。
- 副本和其他 Sentinel 只有 SDOWN,没有 ODOWN。
- 进入 ODOWN 才具备启动故障转移流程的前提。
- ODOWN 本身仍不等于故障转移一定能够执行,因为还需要选出获得足够授权的 Sentinel Leader。 (Redis)
3.5 quorum 与多数派不是同一个概念
这是 Sentinel 面试中最容易答错的地方。
quorum
quorum 用于判断主节点能否进入 ODOWN。
例如:
sentinel monitor mymaster 10.0.0.10 6379 2
最后一个参数 2 表示至少两个 Sentinel 认为主节点不可达,才可将其标记为 ODOWN。
多数派
真正执行故障转移时,某个 Sentinel 还必须获得 Sentinel 集群多数成员的授权。
若 Sentinel 总数为 N,多数派为:
floor(N / 2) + 1
实际启动故障转移所需的授权数,可以理解为:
max(quorum, floor(N / 2) + 1)
示例:
| Sentinel 数量 | quorum | 形成 ODOWN 所需报告数 | 执行故障转移所需授权数 |
|---|---|---|---|
| 3 | 2 | 2 | 2 |
| 5 | 2 | 2 | 3 |
| 5 | 3 | 3 | 3 |
| 5 | 4 | 4 | 4 |
| 5 | 5 | 5 | 5 |
假设有五个 Sentinel,quorum=2。两个 Sentinel 认为主节点宕机后可以形成 ODOWN,但至少还需要三个 Sentinel 可互相通信,某个候选 Sentinel 才能获得多数派授权并执行故障转移。
因此,quorum=2 并不表示“两个 Sentinel 就足够完成切换”。 (Redis)
3.6 Sentinel Leader 选举
Sentinel 选出的 Leader 不是永久 Leader,也不是新的 Redis 主节点。
它只是:
针对某一个主节点、某一次故障转移过程的协调者。
当主节点进入 ODOWN 后,多个 Sentinel 都可能尝试发起故障转移。它们通过投票决定由哪一个 Sentinel 执行本次操作。
获得授权的 Sentinel 会取得一个新的配置纪元,configuration epoch。配置纪元相当于故障转移配置的版本号:
- 每次故障转移使用新的纪元。
- 纪元较大的配置覆盖纪元较小的配置。
- Sentinel 在网络分区恢复后根据纪元收敛到较新的主节点配置。
- 一个 Sentinel 为其他候选者投票后,不会立即反复为同一个主节点发起竞争。
这不是完整的 Raft 日志复制,也不应把 Sentinel 描述成通用共识系统。它使用多数派授权和配置纪元来约束故障转移顺序、传播最新拓扑。 (Redis)
3.7 完整故障转移过程
一次正常的自动故障转移大致包括以下步骤。
第一步:主节点进入 SDOWN
每个无法正常联系主节点的 Sentinel 独立形成 SDOWN 判断。
第二步:主节点进入 ODOWN
Sentinel 互相交换主节点下线判断,满足 quorum 后形成 ODOWN。
第三步:选出故障转移 Leader
候选 Sentinel 发起投票,获得足够授权的 Sentinel 成为本次故障转移协调者。
第四步:选择待提升副本
Sentinel 先排除:
- 当前不可达或处于 SDOWN 的副本。
- 与旧主断开时间过长的副本。
replica-priority为0的副本。- 状态或复制信息不足以支持提升的副本。
对剩余副本按以下顺序比较:
replica-priority更小的优先。- 优先级相同时,复制偏移量更大的优先。
- 偏移量也相同时,run ID 字典序更小的优先,用于确定性地打破平局。
replica-priority=0 表示该副本永远不应被 Sentinel 提升。 (Redis)
第五步:提升副本
Leader 向候选副本发送等价于:
REPLICAOF NO ONE
的命令,使其停止复制旧主并切换为主节点。
Sentinel 不能只根据命令发送成功就认定提升完成,还要通过 INFO 等信息确认该节点已报告自己为主节点。
第六步:传播新配置
提升成功后,Leader 发布新的主节点地址和配置纪元。其他 Sentinel 接收更高纪元后更新自己的状态。
第七步:重新配置其他副本
其他副本被依次改为复制新主节点。
parallel-syncs 决定可以同时重新配置多少个副本。例如:
sentinel parallel-syncs mymaster 1
表示一次只安排一个副本切换到新主节点。数值较小会延长整个副本恢复过程,但可以避免所有读副本同时进入重新同步状态。 (Redis)
第八步:旧主恢复后降级
旧主节点重新上线后,Sentinel 会把它重新配置为新主节点的副本。网络分区期间旧主上的分叉数据可能因此被覆盖。
官方源码中的故障转移状态机包括等待开始、选择副本、发送取消复制命令、等待提升、重新配置其他副本和更新最终配置等阶段。 (GitHub)
3.8 客户端如何发现新主节点
支持 Sentinel 的客户端不应把主节点地址当作永久地址,而是持有:
Sentinel 地址列表 + 主节点逻辑名称
客户端发现流程通常是:
-
依次或并发尝试连接已知 Sentinel。
-
执行:
SENTINEL GET-MASTER-ADDR-BY-NAME mymaster -
连接返回的 Redis 地址。
-
使用
ROLE等方式确认目标确实是主节点。 -
连接中断或收到角色异常后,重新向 Sentinel 查询。
-
主节点发生变化时,清理连接池中指向旧主的连接。
Sentinel Pub/Sub 事件可以加速客户端感知,但不能作为唯一正确性来源,因为客户端可能断线并错过通知。重新查询和验证角色才是可靠流程。 (Redis)
3.9 网络分区与脑裂
考虑以下网络分区:
分区 A 分区 B
Client A Client B
| |
Old Master New Master
Sentinel S1 // 网络中断 // Sentinel S2
Sentinel S3
分区 B 拥有 Sentinel 多数派,因此可以把一个副本提升为新主节点。
但分区 A 中的旧主节点可能仍然存活,并继续接受 Client A 的写入。此时系统层面出现两个可写节点:
- 新主节点是 Sentinel 多数派认可的主节点。
- 旧主节点不知道自己已经失去主节点资格。
- 网络恢复后,旧主会被降级并复制新主。
- 旧主在分区期间独有的写入将被丢弃。
因此:
Sentinel 能阻止少数派 Sentinel 发起另一次经过授权的故障转移,但不能主动“关闭”一个网络隔离且仍在运行的旧主节点。
可以配置:
min-replicas-to-write 1
min-replicas-max-lag 10
使主节点在没有至少一个延迟不超过约 10 秒的副本时拒绝写入。这样能够缩小旧主持续接收写入的时间窗口,但代价是网络抖动或副本故障时主动牺牲写可用性。
该配置不能把异步复制变成同步复制,也不能保证零数据丢失。 (Redis)
3.10 RTO 与 RPO
RTO:恢复时间目标
RTO 表示从主节点故障开始,到应用恢复成功读写新主节点所经历的时间。
Sentinel 场景中的 RTO 大致包括:
故障检测时间
+ ODOWN 协商时间
+ Leader 选举时间
+ 副本提升时间
+ 客户端发现和重连时间
因此,RTO 不等于 down-after-milliseconds。
RPO:恢复点目标
RPO 表示故障后最多可能丢失多少数据。
Redis 主从复制默认是异步的,旧主已经向客户端返回成功、但尚未复制到最终被提升副本的写入,可能在切换中丢失。
RPO 受以下因素影响:
- 主从复制延迟。
- 被提升副本的复制偏移量。
- 网络分区持续时间。
min-replicas-to-write配置。- 客户端是否需要执行额外复制确认。
- 业务是否允许在旧主分区中继续写入。
3.11 一次主节点宕机的时间线分析
假设:
3 个 Sentinel
quorum = 2
down-after-milliseconds = 5000
parallel-syncs = 1
下面时间仅用于理解,不是性能承诺:
| 相对时间 | 可能发生的事件 |
|---|---|
T+0s | 主节点进程崩溃或网络完全中断 |
T+0~5s | Sentinel 持续探测;客户端可能出现超时、EOF 或连接拒绝 |
T+约5s | 各 Sentinel 陆续将旧主标记为 SDOWN |
T+约5~6s | 至少两个 Sentinel 同意下线,形成 ODOWN |
T+约6s | Sentinel 进行本次故障转移的 Leader 授权 |
T+约6~8s | 选择副本,发送 REPLICAOF NO ONE,等待其报告主角色 |
T+约7~9s | Sentinel 开始发布新主地址;客户端重新发现并建立连接 |
T+后续 | 其他副本按 parallel-syncs 逐步改为复制新主 |
| 旧主恢复后 | Sentinel 将旧主改为新主的副本 |
实际时间会受到网络延迟、Sentinel 调度、候选副本状态、DNS、客户端超时、连接池和重试策略影响。
4. 命令与 Go 使用方法
4.1 最小 Sentinel 配置
三个 Sentinel 分别使用类似配置,并监听不同机器上的 26379:
port 26379
sentinel monitor mymaster 10.0.0.10 6379 2
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 60000
sentinel parallel-syncs mymaster 1
参数含义:
| 参数 | 含义 |
|---|---|
mymaster | 主节点组的逻辑名称,客户端使用该名称发现主节点 |
2 | 判断 ODOWN 所需的 quorum |
down-after-milliseconds | 单个 Sentinel 形成 SDOWN 前的连续不可达时间 |
failover-timeout | 控制故障转移重试、状态超时和副本重新配置等待等流程 |
parallel-syncs | 同时切换到新主节点的副本数量 |
failover-timeout 不是简单的“切换必须在该时间内完成”。它还影响失败后再次尝试故障转移的间隔,以及等待副本重新配置的时间。 (Redis)
4.2 常用 redis-cli 命令
# 查看 Sentinel 自身状态
redis-cli -h 10.0.0.21 -p 26379 INFO sentinel
# 查看指定主节点组
redis-cli -h 10.0.0.21 -p 26379 \
SENTINEL MASTER mymaster
# 查看该主节点组的副本
redis-cli -h 10.0.0.21 -p 26379 \
SENTINEL REPLICAS mymaster
# 查看监控同一主节点组的其他 Sentinel
redis-cli -h 10.0.0.21 -p 26379 \
SENTINEL SENTINELS mymaster
# 查询当前主节点
redis-cli -h 10.0.0.21 -p 26379 \
SENTINEL GET-MASTER-ADDR-BY-NAME mymaster
# 检查当前部署是否同时满足 quorum 和多数派要求
redis-cli -h 10.0.0.21 -p 26379 \
SENTINEL CKQUORUM mymaster
# 人工触发故障转移,生产环境必须谨慎使用
redis-cli -h 10.0.0.21 -p 26379 \
SENTINEL FAILOVER mymaster
SENTINEL FAILOVER 可以在旧主仍可达时强制切换,而且不会先要求其他 Sentinel 对主节点故障达成一致,因此应作为受控运维操作,而不是常规健康检查命令。 (Redis)
4.3 使用 go-redis/v9 连接 Sentinel
package main
import (
"context"
"errors"
"log"
"time"
"github.com/redis/go-redis/v9"
)
func main() {
rdb := redis.NewFailoverClient(&redis.FailoverOptions{
// Sentinel 中配置的逻辑主节点名称。
MasterName: "mymaster",
// 至少配置多个 Sentinel 种子地址。
SentinelAddrs: []string{
"10.0.0.21:26379",
"10.0.0.22:26379",
"10.0.0.23:26379",
},
// Sentinel 自身的 ACL 凭据。
SentinelUsername: "sentinel-app",
SentinelPassword: "sentinel-secret",
// Redis 数据节点的 ACL 凭据。
Username: "redis-app",
Password: "redis-secret",
DB: 0,
DialTimeout: 500 * time.Millisecond,
ReadTimeout: time.Second,
WriteTimeout: time.Second,
PoolTimeout: time.Second,
// 自动重试必须保守设置。
// 对 INCR、出队、非幂等 Lua 等操作不能依赖无脑重试。
MaxRetries: 1,
MinRetryBackoff: 50 * time.Millisecond,
MaxRetryBackoff: 200 * time.Millisecond,
ContextTimeoutEnabled: true,
})
defer func() {
if err := rdb.Close(); err != nil {
log.Printf("close redis client: %v", err)
}
}()
startupCtx, cancel := context.WithTimeout(
context.Background(),
2*time.Second,
)
defer cancel()
if err := rdb.Ping(startupCtx).Err(); err != nil {
log.Fatalf("redis is unavailable: %v", err)
}
ctx := context.Background()
if err := setCache(ctx, rdb, "user:42:name", "Alice"); err != nil {
log.Printf("set cache failed: %v", err)
}
value, found, err := getCache(ctx, rdb, "user:42:name")
if err != nil {
log.Printf("get cache failed: %v", err)
return
}
if !found {
log.Printf("cache miss")
return
}
log.Printf("cache value: %s", value)
}
func setCache(
parent context.Context,
rdb *redis.Client,
key string,
value string,
) error {
ctx, cancel := context.WithTimeout(parent, 800*time.Millisecond)
defer cancel()
if err := rdb.Set(ctx, key, value, 5*time.Minute).Err(); err != nil {
return err
}
return nil
}
func getCache(
parent context.Context,
rdb *redis.Client,
key string,
) (value string, found bool, err error) {
ctx, cancel := context.WithTimeout(parent, 800*time.Millisecond)
defer cancel()
value, err = rdb.Get(ctx, key).Result()
switch {
case err == nil:
return value, true, nil
case errors.Is(err, redis.Nil):
return "", false, nil
default:
return "", false, err
}
}
4.4 代码说明
NewFailoverClient 会使用 MasterName 和 Sentinel 地址发现当前主节点。主节点发生切换、已有连接失效后,客户端会重新查询 Sentinel,而不是继续永久连接旧地址。
需要区分两组认证信息:
SentinelUsername、SentinelPassword用于访问 Sentinel。Username、Password用于访问 Redis 数据节点。
redis.Client 可以安全地被多个 goroutine 共享,不应为每个请求创建新客户端。故障转移期间仍可能出现短暂的超时、EOF、连接关闭或角色错误,业务层必须限制重试次数,并只安全重试满足幂等条件的操作。 (GitHub)
redis.NewSentinelClient 是直接访问某一个 Sentinel 管理接口的客户端;普通业务读写通常应使用 redis.NewFailoverClient。
5. 典型业务场景
| 场景 | 适用性 | 数据量要求 | 一致性要求 | 主要风险 | 可替代方案 |
|---|---|---|---|---|---|
| 单分片业务缓存 | 很适合 | 单个主节点可承载 | 允许短暂丢失或重建 | 切换期间缓存访问失败 | Redis Cluster、托管 Redis |
| Session、验证码、临时状态 | 适合 | 中小规模 | 通常允许有限 RPO | 切换后少量会话或状态丢失 | 数据库、JWT、Cluster |
| 排行榜、计数和实时状态 | 条件适合 | 单节点内存和吞吐可承载 | 需接受异步复制 | 已确认计数可能回退 | Cluster、数据库事件日志 |
| 重要业务状态存储 | 谨慎 | 单分片即可 | 不能接受丢失时不适合 | 分区写丢失、旧主脑裂 | 强一致数据库、共识型 KV |
| 超大数据集或水平扩展 | 不适合 | 超出单节点容量 | 与 Sentinel 无关 | Sentinel 不分片 | Redis Cluster |
| 跨地域双活写入 | 不适合 | 任意 | 通常要求冲突处理 | 高延迟、双主冲突 | Active-Active 产品或可合并数据模型 |
6. 底层实现
6.1 核心数据结构
Sentinel 内部维护以主节点逻辑名称为索引的字典。每个主节点对象又关联:
- 当前主节点地址和 run ID。
- 已发现副本字典。
- 已发现 Sentinel 字典。
- SDOWN、ODOWN 时间。
quorum、parallel-syncs、failover-timeout。- 当前配置纪元和故障转移纪元。
- 待提升副本及故障转移状态。
- 与各实例的异步命令连接和 Pub/Sub 连接。
使用字典的原因是,副本和 Sentinel 都可能动态加入、删除或改变地址,需要高效地按名称、地址或 run ID 查找实例。源码也会在多个主节点组之间共享到同一个 Sentinel 的底层连接,避免监控很多主节点时产生不必要的连接爆炸。 (GitHub)
6.2 自动发现机制
Sentinel 不要求在配置中逐个列出所有副本和其他 Sentinel。
副本主要通过主节点及副本的 INFO 信息发现;其他 Sentinel 通过 Redis Pub/Sub 频道:
__sentinel__:hello
互相发现。
Hello 消息包含 Sentinel 的地址、run ID 和当前主节点配置。收到更高配置纪元的 Sentinel 会更新自己的本地配置。官方实现通常每两秒发布一次 Hello 信息。 (Redis)
6.3 故障转移状态机
从实现角度,故障转移不是一个单命令,而是状态机:
WAIT_START
↓
SELECT_REPLICA
↓
SEND_REPLICAOF_NO_ONE
↓
WAIT_PROMOTION
↓
RECONFIGURE_REPLICAS
↓
UPDATE_CONFIG
状态机设计有三个好处:
- 网络命令和角色变化是异步的,不能在一个同步函数里假设全部立即成功。
- 每个阶段可以设置超时并记录事件。
- Sentinel 进程、目标副本或网络在中间阶段异常时,可以识别失败并重新尝试。
6.4 时间复杂度与空间成本
设一个 Sentinel 监控:
M个主节点组。R个副本。S个其他 Sentinel 记录。
主要复杂度可概括为:
| 操作 | 典型复杂度 |
|---|---|
| 按名称查找主节点 | 字典平均 O(1) |
| 周期性遍历被监控实例 | O(M + R + S) |
| 筛选候选副本 | O(R) |
| 对候选副本排序 | 通常可视为 O(R log R) |
| 保存拓扑状态 | O(M + R + S) 空间 |
Sentinel 通常只监控少量 Redis 拓扑,因此副本排序不是主要性能瓶颈。更值得关注的是 Sentinel 数量过多、跨高延迟网络部署、DNS 阻塞和频繁网络抖动带来的控制面压力。
6.5 配置持久化
Sentinel 会把以下状态写回 sentinel.conf:
- 自己的稳定 ID。
- 已发现副本和 Sentinel。
- 当前主节点地址。
- 配置纪元。
- 故障转移结果。
这样 Sentinel 重启后可以继续使用最新拓扑,而不是退回最初配置中的旧主节点地址。配置管理系统如果每次部署都强行覆盖 Sentinel 自动重写的配置,可能使节点重启后加载过期状态。 (Redis)
6.6 版本差异
- Sentinel 2 自 Redis 2.8 起作为稳定实现提供。
- Redis 2.8.4 起支持运行时添加、删除和修改监控配置。
SENTINEL REPLICAS标注为 Redis 5.0+。- Redis 5 起官方术语逐步由 slave 改为 replica,但部分兼容协议和源码符号仍保留旧名称。
- Redis 6.2+ 支持 Sentinel ACL、
SENTINEL CONFIG GET/SET、SENTINEL MYID,并可选择启用主机名支持。 - 主机名支持默认关闭;启用后应保证 DNS 解析稳定,并确认客户端能够处理 Sentinel 返回的主机名。 (Redis)
Redis 8.x 中,Sentinel 的核心模型仍然是单主多副本、故障检测、Leader 授权、配置纪元和客户端服务发现。
7. 高性能、高并发、高可用分析
7.1 高性能
Sentinel 不代理常规业务命令,因此正常状态下几乎不会增加每次 Redis 请求的网络跳数。
主要性能影响来自:
- Sentinel 对实例执行
PING、INFO和 Pub/Sub 通信。 - 故障转移时大量客户端同时重连。
- 新主提升后,多个副本执行全量或部分重同步。
- DNS、NAT 或网络延迟拖慢服务发现。
parallel-syncs过大导致多个副本同时失去读服务能力。down-after-milliseconds过小,导致短暂抖动频繁触发异常判断。
不能为了追求更小 RTO 就盲目把检测时间配置成几百毫秒。真实网络存在调度暂停、瞬时丢包、虚拟机抖动和 GC 停顿,过度敏感会增加误切换。
7.2 高并发
主节点故障时,数百或数千个应用实例可能同时:
- 发现连接断开。
- 查询 Sentinel。
- 创建到新主节点的连接。
- 重放失败命令。
- 回源数据库或其他依赖。
这可能形成重连风暴和缓存雪崩。
客户端应做到:
- 使用共享连接池,而不是请求级创建客户端。
- 设置较短但合理的连接超时和命令超时。
- 设置有限重试次数及退避。
- 对重试增加随机抖动。
- 区分幂等命令和非幂等命令。
- 对缓存回源使用
singleflight等请求合并机制。 - 在故障期间允许部分降级,而不是无限排队。
例如,客户端发送 INCR 后连接断开,无法判断服务端是否已经执行该命令。此时自动重试可能产生重复递增。Sentinel 只负责发现新主节点,不提供端到端“恰好一次”执行保证。
7.3 高可用
一个合理的 Sentinel 部署至少应满足:
- 三个或五个 Sentinel。
- Sentinel 位于独立故障域。
- 至少一个可提升副本。
- 客户端显式支持 Sentinel。
- Sentinel 端口互通。
- Sentinel 和 Redis 的 ACL 配置正确。
- 配置文件可写。
- 定期执行
SENTINEL CKQUORUM。 - 定期进行故障演练。
- 监控复制延迟和候选副本资格。
- 根据业务选择是否配置
min-replicas-to-write。
Sentinel 提高的是故障后的自动恢复能力,而不是把系统变成强一致系统。官方文档明确指出,异步复制下已经确认的写入仍可能在故障中丢失。 (Redis)
8. 常见错误与生产事故
8.1 只有两个 Sentinel
- 现象: 一台机器故障后,Redis 副本仍然存活,但系统没有自动切换。
- 根因: 两个 Sentinel 的多数派是两个。如果主节点和其中一个 Sentinel 同机故障,剩余 Sentinel 无法获得多数派授权。
- 排查方法: 执行
SENTINEL CKQUORUM,检查num-other-sentinels和网络连通性。 - 修复方案: 增加第三个独立 Sentinel。
- 如何预防: Sentinel 数量使用奇数,并跨独立故障域部署。
8.2 把三个 Sentinel 放在同一台机器
- 现象: 单个宿主机宕机后,监控和自动故障转移完全失效。
- 根因: 进程数量增加了,但故障域没有增加。
- 排查方法: 检查 Sentinel 所在物理机、虚拟机和可用区分布。
- 修复方案: 将 Sentinel 分散到至少三个独立节点。
- 如何预防: 架构评审时关注故障域,而不是只统计实例数量。
8.3 客户端硬编码旧主节点地址
- 现象: Sentinel 已成功切换,但应用持续连接旧主,出现连接失败或
READONLY。 - 根因: 客户端没有使用 Sentinel-aware 连接方式。
- 排查方法: 对比应用连接目标与
SENTINEL GET-MASTER-ADDR-BY-NAME返回值。 - 修复方案: 使用
redis.NewFailoverClient,配置主节点名称和多个 Sentinel 地址。 - 如何预防: 禁止将当前主节点 IP 作为永久业务配置。
8.4 quorum 满足但故障转移仍未执行
- 现象: 日志中已经出现 ODOWN,但没有新主节点。
- 根因: 可以满足 quorum,却无法联系 Sentinel 多数派;或者没有合格副本。
- 排查方法: 执行
SENTINEL CKQUORUM,检查副本的 SDOWN 状态、优先级、复制链路和复制偏移量。 - 修复方案: 恢复 Sentinel 网络多数派,保证至少一个合格副本。
- 如何预防: 同时监控 quorum、可达 Sentinel 数量和可提升副本数量。
8.5 网络分区后出现数据回退
- 现象: 网络恢复后,部分已经返回成功的数据消失。
- 根因: 客户端在隔离的旧主上继续写入;多数派侧已经提升新主,旧主恢复后被降级并覆盖。
- 排查方法: 检查切换时间、客户端连接地址、复制偏移量以及旧主日志。
- 修复方案: 根据业务配置
min-replicas-to-write和min-replicas-max-lag,对重要写入增加业务幂等与外部记录。 - 如何预防: 明确 RPO,禁止宣传 Sentinel 为零数据丢失方案。
8.6 down-after-milliseconds 设置过小
- 现象: 网络轻微抖动时频繁出现 SDOWN、ODOWN 和主从切换。
- 根因: 故障检测阈值低于基础设施正常抖动范围。
- 排查方法: 对照 Sentinel 事件、主机负载、网络丢包和延迟监控。
- 修复方案: 调大阈值,改善网络和宿主机调度。
- 如何预防: 根据延迟分布和演练结果设置阈值,而不是照搬极端低值。
8.7 Docker、NAT 或 Kubernetes 地址通告错误
- 现象: Sentinel 能发现节点,但返回的 IP 或端口对客户端不可达。
- 根因: 实例通告的是容器内部地址,端口映射破坏自动发现。
- 排查方法: 查看
SENTINEL MASTER、REPLICAS、SENTINELS中的地址,并从客户端网络实际连接。 - 修复方案: 正确配置
announce-ip、announce-port,或使用稳定、可路由的网络方案。 - 如何预防: 在真实网络路径中完成故障演练,不只在容器内部验证。
9. 方案选型与权衡
| 方案 | 自动故障转移 | 数据分片 | 一致性 | 运维复杂度 | 适用场景 |
|---|---|---|---|---|---|
| 单机 Redis | 无 | 无 | 单节点语义 | 低 | 开发、可丢失缓存 |
| 主从复制,人工切换 | 无 | 无 | 异步复制 | 中 | 有人工值守、低切换频率 |
| Redis + Sentinel | 有 | 无 | 异步复制,可能丢失写入 | 中 | 单分片高可用缓存或状态服务 |
| Redis Cluster | 有 | 有 | 异步复制,非强一致 | 高 | 数据量或吞吐超过单节点 |
| 托管 Redis 服务 | 通常有 | 依产品而定 | 依产品能力 | 应用侧较低 | 希望降低基础设施运维成本 |
| 强一致分布式数据库 | 有 | 通常有 | 共识复制 | 高 | 不能接受已确认写入丢失 |
选择 Sentinel 的前提通常是:
数据和流量仍可由单个主节点承载,但业务不能接受主节点故障后依赖人工切换。
一旦核心诉求是水平分片,应该评估 Redis Cluster;一旦核心诉求是已确认写入必须在多故障下保持,应该评估具备共识复制的存储系统,而不是继续堆叠 Sentinel 参数。
10. 高频面试题
问题 1:Redis Sentinel 解决了什么问题?
推荐回答
现场回答:
Sentinel 为非 Cluster 的 Redis 主从部署提供监控、通知、自动故障转移和客户端服务发现。它能在主节点故障后提升副本,并让客户端发现新主,但不负责数据分片,也不能保证零数据丢失。
详细展开:
仅有主从复制时,副本不会自动接管业务。Sentinel 通过多个监控实例共同判断主节点故障,选出本次故障转移的协调者,提升副本、重配其他副本并发布新主地址。
面试官追问
- Sentinel 是否位于业务请求链路中?
- Sentinel 是否能替代 Redis Cluster?
- Sentinel 能否保证已确认写入不丢失?
常见错误回答
“Sentinel 就是 Redis 集群,既能分片又能保证强一致。”
评分点
- 初级: 知道 Sentinel 能自动切主。
- 中级: 能说出四项能力和不负责分片。
- 高级: 能进一步说明异步复制、服务发现和 RTO/RPO 边界。
问题 2:SDOWN 和 ODOWN 有什么区别?
推荐回答
现场回答:
SDOWN 是单个 Sentinel 的主观判断;ODOWN 是达到 quorum 数量的 Sentinel 都认为主节点不可达。SDOWN 可应用于主节点、副本和 Sentinel,ODOWN 只用于主节点,并且只有 ODOWN 才能触发故障转移流程。
详细展开:
Sentinel 连续超过 down-after-milliseconds 没收到有效回复后形成 SDOWN。随后询问其他 Sentinel,达到 quorum 后形成 ODOWN。但真正执行切换还需获得多数派授权。
面试官追问
- 副本为什么没有 ODOWN?
- SDOWN 的副本能否被提升?
- ODOWN 后是否一定会切换?
常见错误回答
“SDOWN 是副本下线,ODOWN 是主节点下线。”
评分点
- 初级: 能区分单节点判断和集体判断。
- 中级: 能指出 ODOWN 只用于主节点。
- 高级: 能继续解释 ODOWN、quorum 和多数派授权之间的关系。
问题 3:quorum 与多数派有什么区别?
推荐回答
现场回答:
quorum 用于确认 ODOWN,多数派用于授权某个 Sentinel 执行故障转移。五个 Sentinel、quorum 为二时,两个 Sentinel 可以判定 ODOWN,但至少三个 Sentinel 可达才能真正执行切换。
详细展开:
所需授权数可以近似理解为 max(quorum, floor(N/2)+1)。Sentinel 不会在只有少数派的网络分区中启动新的自动故障转移。
面试官追问
- 三个 Sentinel、quorum 为一会怎样?
- 五个 Sentinel、quorum 为四需要多少授权?
- quorum 越大是否越安全?
常见错误回答
“quorum 就是集群节点数的一半加一。”
评分点
- 初级: 知道 quorum 是投票数。
- 中级: 能区分故障判定和故障转移授权。
- 高级: 能分析不同 quorum 对误判敏感度和可用性的影响。
问题 4:为什么一般至少部署三个 Sentinel?
推荐回答
现场回答:
因为故障转移需要多数派授权。两个 Sentinel 的多数派仍然是两个,其中一个与主节点同机故障后,剩余 Sentinel 无法切换。三个 Sentinel 可以在失去一个后仍保留两个成员的多数派。
详细展开:
三个 Sentinel 还必须放在独立故障域。三个进程位于同一宿主机不能抵御宿主机故障。
面试官追问
- 两个 Redis 节点能否部署三个 Sentinel?
- 五个 Sentinel 相比三个有什么收益?
- Sentinel 是否必须和 Redis 同机?
常见错误回答
“奇数节点只是为了避免投票平局,两个也可以正常生产使用。”
评分点
- 初级: 知道推荐三个。
- 中级: 能算多数派。
- 高级: 能结合故障域、网络分区和应用节点部署 Sentinel。
问题 5:描述一次 Sentinel 故障转移的完整流程。
推荐回答
现场回答:
主节点先被各 Sentinel 判定 SDOWN,达到 quorum 后进入 ODOWN;随后选出本次故障转移 Leader;Leader筛选副本,向候选副本发送 REPLICAOF NO ONE,确认其成为主节点;再发布新配置,让其他副本复制新主,旧主恢复后也会被降级。
详细展开:
配置传播依赖配置纪元。更高纪元的主节点配置会覆盖旧配置,使网络恢复后的 Sentinel 最终收敛。
面试官追问
- 什么时候算故障转移成功?
- 其他副本还没同步完成时算成功吗?
- Leader 中途宕机会怎样?
常见错误回答
“Sentinel 随机挑一个副本改成主节点,然后结束。”
评分点
- 初级: 能说出检测、选副本和切换。
- 中级: 能描述 ODOWN、Leader、重配副本。
- 高级: 能解释配置纪元和中途失败重试。
问题 6:Sentinel 如何选择要提升的副本?
推荐回答
现场回答:
先排除不可达、断链过久和优先级为零的副本,再按 replica-priority 从小到大、复制偏移量从大到小、run ID 字典序从小到大选择。
详细展开:
偏移量越大通常表示接收的数据越多,但优先级比较在偏移量之前。因此人工设置优先级时必须确认目标副本的网络和复制质量。
面试官追问
replica-priority=0有什么作用?- 为什么 run ID 也参与比较?
- 优先级更高但数据更旧会怎样?
常见错误回答
“永远选择延迟最低或偏移量最大的副本。”
评分点
- 初级: 知道会从副本中选一个。
- 中级: 能说出优先级和偏移量。
- 高级: 能说明候选过滤、比较顺序及错误优先级配置的风险。
问题 7:Sentinel 能完全避免脑裂吗?
推荐回答
现场回答:
不能。Sentinel 可以避免少数派 Sentinel 再发起一次经过授权的故障转移,但隔离的旧主可能继续接受客户端写入。网络恢复后旧主被降级,这部分分叉写入会丢失。
详细展开:
Redis 实例不会因为“其他分区已提升新主”而自动知道自己失去主角色。min-replicas-to-write 可以限制旧主无副本确认时继续写入的时间,但不能提供同步复制保证。
面试官追问
- 为什么旧主不会自动停止写入?
- 如何减少分区写丢失?
- 牺牲可用性是否能换取更小 RPO?
常见错误回答
“有多数派,所以系统任何时候只可能有一个可写主节点。”
评分点
- 初级: 知道可能脑裂。
- 中级: 能描述旧主和新主同时可写。
- 高级: 能区分控制面授权安全与数据面写入行为。
问题 8:min-replicas-to-write 有什么作用和代价?
推荐回答
现场回答:
它让主节点只有在存在足够数量、延迟在限制内的副本时才接受写入,可缩小网络分区中的数据分叉窗口。代价是副本故障或网络抖动时主节点会主动拒绝写入,降低可用性。
详细展开:
它根据副本异步确认和最大允许延迟进行判断,并不是每次写入都等待副本同步提交,因此不能等同于同步复制。
面试官追问
- 它是否能保证零数据丢失?
- 缓存业务是否一定要开启?
- 参数设置过严会发生什么?
常见错误回答
“开启后 Redis 就变成强一致数据库。”
评分点
- 初级: 知道它与副本数量有关。
- 中级: 能说出降低数据丢失与降低可用性的权衡。
- 高级: 能结合网络分区和异步复制解释其边界。
问题 9:Sentinel 场景中的 RTO 和 RPO 分别由什么决定?
推荐回答
现场回答:
RTO 由故障检测、ODOWN 协商、Leader 授权、副本提升以及客户端重连共同决定;RPO 主要由异步复制延迟和最终提升副本的数据进度决定。
详细展开:
降低 down-after-milliseconds 可能缩短检测时间,但也提高误判风险。RPO 不能通过缩短故障检测时间完全解决,因为写入可能在主节点返回成功后尚未复制。
面试官追问
down-after-milliseconds=5s是否意味着 RTO 为 5 秒?- 客户端超时如何影响 RTO?
- 哪些手段可以降低 RPO?
常见错误回答
“RTO 等于 failover-timeout,RPO 永远为零。”
评分点
- 初级: 知道两个概念的定义。
- 中级: 能列出主要组成因素。
- 高级: 能建立完整故障时间线并讨论参数权衡。
问题 10:Go 客户端如何在故障转移后连接新主?
推荐回答
现场回答:
使用 redis.NewFailoverClient,配置 MasterName 和多个 Sentinel 地址。客户端在建立或重建连接时向 Sentinel 查询当前主节点,并清理指向旧主的连接。一个客户端实例可以被多个 goroutine 共享。
详细展开:
还要分别配置 Sentinel ACL 和 Redis ACL,设置连接、读写、命令超时,并限制重试。故障转移期间的非幂等命令不能无脑重放。
面试官追问
NewSentinelClient和NewFailoverClient有什么区别?- 为什么要配置多个 Sentinel 地址?
INCR超时后是否可以直接重试?
常见错误回答
“应用只需监听 Sentinel Pub/Sub,收到事件后修改全局 IP。”
评分点
- 初级: 会创建 FailoverClient。
- 中级: 能处理超时、连接池和
redis.Nil。 - 高级: 能讨论重试不确定性、幂等性和重连风暴。
问题 11:parallel-syncs 应该如何设置?
推荐回答
现场回答:
它控制故障转移后同时重新配置多少个副本。数值大可以更快恢复完整复制拓扑,但多个副本可能同时重新同步而暂时不能稳定提供读服务;数值小恢复较慢,但影响更平滑。
详细展开:
需要结合副本数量、全量同步概率、读流量和磁盘、网络能力设置。常见起点是 1,但不是固定最佳值。
面试官追问
- 它是否影响新主提升速度?
- 全量同步时有哪些资源风险?
- 所有副本都恢复前,故障转移是否已经成功?
常见错误回答
“设置得越大越好,因为可以加速切换。”
评分点
- 初级: 知道它控制副本同步数量。
- 中级: 能说明速度与读可用性的权衡。
- 高级: 能结合全量同步、网络和容量规划分析。
问题 12:Sentinel 与 Redis Cluster 应该如何选择?
推荐回答
现场回答:
数据和流量能由单个主节点承载,只需要单分片自动故障转移时选 Sentinel;需要水平分片、容量超过单节点或希望多主分担写入时选 Redis Cluster。两者都不应被描述成强一致系统。
详细展开:
Sentinel 只有一个业务主节点,数据集完整复制到副本;Cluster 把 Key 分布到 16384 个 Slot,由多个主节点分别负责。Cluster 的客户端还需要处理 Slot 路由和重定向。
面试官追问
- Sentinel 能否管理多个互不相关的主节点组?
- 多个 Sentinel 主节点组是否等同于 Cluster?
- Cluster 是否还需要独立 Sentinel?
常见错误回答
“Sentinel 是小型 Cluster,节点多了自然就能分片。”
评分点
- 初级: 知道 Cluster 支持分片。
- 中级: 能比较单主多副本和多分片。
- 高级: 能结合容量、一致性、客户端和运维复杂度选型。
11. 一分钟面试回答
Redis Sentinel 是非 Cluster Redis 的高可用方案,主要提供监控、通知、自动故障转移和客户端服务发现。单个 Sentinel 连续超过 down-after-milliseconds 无法联系实例时会形成 SDOWN;达到配置的 quorum 后,主节点进入 ODOWN。但 quorum 只负责故障判定,真正执行切换还要有 Sentinel 多数派授权,并选出本次故障转移的 Leader。Leader 会排除不可用副本,然后按副本优先级、复制偏移量和 run ID 选择候选节点,执行 REPLICAOF NO ONE,发布更高配置纪元,再让其他副本复制新主。客户端应使用 NewFailoverClient 根据主节点名称向多个 Sentinel 查询当前主节点。Sentinel 不负责数据分片,也不能保证零数据丢失;异步复制和网络分区下,隔离旧主上的写入仍可能在恢复后丢失。生产环境应至少部署三个跨独立故障域的 Sentinel,并结合 RTO、RPO、min-replicas-to-write、超时、有限重试和故障演练进行设计。
12. 本章总结
Sentinel 的核心价值不是“多运行几个监控进程”,而是建立一个由多个观察者共同驱动的自动故障转移控制面。
必须掌握以下结论:
- SDOWN 是单个 Sentinel 的主观判断,ODOWN 是达到 quorum 后对主节点的客观判断。
- quorum 用于故障判定,多数派用于授权故障转移,两者不能混为一谈。
- Sentinel Leader 是一次故障转移的协调者,不是永久 Leader。
- 副本提升不是随机选择,而是经过资格过滤、优先级、偏移量和 run ID 比较。
- 客户端必须支持 Sentinel,并在断线后重新发现主节点。
- Sentinel 可以提高可用性,但异步复制下仍可能丢失已确认写入。
- 多数派能约束新的故障转移,却不能自动关闭网络隔离的旧主。
min-replicas-to-write是可用性和更小数据分叉窗口之间的权衡。- Sentinel 不能解决分片问题,容量超过单节点时应评估 Redis Cluster。
- 没有经过故障演练的高可用配置,不能视为已经验证的高可用系统。
13. 自测清单
- 为什么主节点已经 SDOWN,Sentinel 仍可能不执行故障转移?
- 三个 Sentinel、
quorum=1时,形成 ODOWN 和执行切换分别需要几个 Sentinel? - 为什么两个 Sentinel 不是可靠的生产部署?
- 副本
replica-priority=0会产生什么效果? - 为什么复制偏移量最大的副本不一定被优先提升?
- Sentinel 如何使网络分区恢复后的配置最终收敛?
- 为什么旧主在网络分区中仍可能继续接受写入?
min-replicas-to-write为什么不能提供同步复制保证?- Go 客户端在故障转移期间重试
INCR有什么风险? - 如何区分 Sentinel 的 RTO 参数调优和 RPO 风险控制?