Redis 哨兵模式部署与客户端接入:三节点高可用实战

单机 Redis 一旦宕机,缓存、会话、分布式锁会一起出问题。哨兵(Sentinel)是 Redis 官方的高可用方案:监控主从状态,主库故障时自动把一台从库提升为新主,并通知客户端切换。本文部署一套一主两从 + 三哨兵的标准架构,讲清每个关键配置项的含义和切换演练的观察方法。

适用环境:Redis 7.2,三台主机 redis-01(192.0.2.31)、redis-02(192.0.2.32)、redis-03(192.0.2.33)。每台主机各运行一个 Redis 实例和一个哨兵进程,Redis 端口 6379,哨兵端口 26379。口令均为占位符,生产使用请替换为强口令。

前置条件

  • 三台主机已安装同版本 Redis,时间同步正常;
  • 防火墙放行 6379 与 26379,且这三台主机之间互通;
  • 三台主机的 Redis 口令规划一致(含哨兵访问主库用的口令,见下文);
  • 数据目录与日志目录已创建并授权给 redis 运行账号。

第一步:配置 Redis 主从

redis-01 为主库,其 redis.conf 关键项:

bind 0.0.0.0
port 6379
protected-mode yes
requirepass CHANGE_ME_STRONG
masterauth CHANGE_ME_STRONG
appendonly yes
appendfsync everysec
maxmemory-policy noeviction
min-replicas-to-write 1
min-replicas-max-lag 10

逐项说明:requirepass 是客户端与从库连本机要用的口令;masterauth 是本机作为从库时连主库用的口令,主从角色会互换,所以每台都要同时配这两个且值一致,这也是后文「密码一致性」的由来。appendonly 开启 AOF 持久化,切换后新主的数据完整性靠它兜底。最后两行是防脑裂的关键:一台主库至少要有 1 个延迟不超过 10 秒的从库确认,才接受写入;被网络隔离的旧主因为连不上从库会自动拒写,具体原理见「常见坑」。

redis-02、redis-03 的配置相同,只比主库多一行 replicaof:

replicaof 192.0.2.31 6379

依次启动三台 Redis,在从库上用 INFO replication 确认 role 为 slave 且 master_link_status 为 up。

第二步:配置哨兵

三台主机的 sentinel.conf 内容相同:

port 26379
daemonize yes
dir /var/lib/redis
logfile /var/log/redis/sentinel.log
sentinel monitor mymaster 192.0.2.31 6379 2
sentinel auth-pass mymaster CHANGE_ME_STRONG
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 60000
sentinel parallel-syncs mymaster 1

逐项解释:

  • sentinel monitor mymaster 主库IP 6379 2:给被监控的主库起名 mymaster(客户端接入时就用这个名字),最后的 2 是法定人数(quorum)——至少 2 个哨兵都认为主库下线,才判定为客观下线并启动切换。三哨兵配 quorum=2 是标准组合:挂 1 个哨兵不影响判断。
  • sentinel auth-pass:哨兵监控主库也要过口令校验,主库设了 requirepass 就必须配这一行,漏配会看到哨兵日志反复报 NOAUTH。
  • down-after-milliseconds 5000:哨兵 ping 不通主库超过 5 秒先判主观下线,达到 quorum 后判客观下线。设太小会因网络抖动误切,一般 3–10 秒,跨机房部署还要更大。
  • failover-timeout 60000:一次故障切换的超时上限,超时未完成视为失败,间隔一段时间后才允许重试。
  • parallel-syncs 1:切换后同时向新主同步的从库数量,设 1 表示逐个同步,避免多台从库同时全量同步把新主打满。

用 redis-server /etc/redis/sentinel.conf --sentinel 方式启动三台哨兵。注意 sentinel.conf 会被哨兵进程运行时改写(记录已知从库与哨兵、纪元号等状态),所以这个文件必须可写,且不要再手工覆盖它,要改参数用 SENTINEL SET 命令动态修改。

第三步:观察故障切换

哨兵状态用 INFO sentinel 查看,重点确认 master 的 num-slaves=2、num-other-sentinels=2。模拟主库宕机:

# 在 redis-01 上停掉 Redis(模拟故障)
redis-cli -a CHANGE_ME_STRONG DEBUG sleep 0 && systemctl stop redis
# 或在测试环境直接
redis-cli -h 192.0.2.31 -a CHANGE_ME_STRONG SHUTDOWN NOSAVE

# 观察哨兵日志,应依次出现
tail -f /var/log/redis/sentinel.log

日志的关键序列:+sdown(主观下线)→ +odown(客观下线)→ +try-failover → +selected-slave(选出新主)→ +promoted-slave → +switch-master mymaster 192.0.2.31 6379 192.0.2.32 6379。看到 +switch-master 就表示切换完成,整条链路正常情况下在 down-after 时间加几秒内走完。切换后在新主上执行 INFO replication,确认原从库已指向新主;旧主恢复后会被哨兵自动改造成新主的从库(它的 replicaof 会被哨兵改写),这点不用手工干预,但要去确认它确实同步上了。

第四步:客户端接入

客户端绝不能把主库 IP 写死,必须通过哨兵发现当前主库。以 Java 的 Jedis 为例:

Set<String> sentinels = new HashSet<>(Arrays.asList(
    "192.0.2.31:26379", "192.0.2.32:26379", "192.0.2.33:26379"));
JedisSentinelPool pool = new JedisSentinelPool(
    "mymaster", sentinels, poolConfig, 2000, "CHANGE_ME_STRONG");

接入要点:

  • 给客户端配全部三个哨兵地址,只配一个等于把高可用做回单点;
  • 哨兵地址建议用 IP 直连或短 TTL 的 DNS,避免 DNS 缓存拖慢故障感知;
  • 连接池要开启连接有效性检测(如 testOnBorrow 或空闲探活),切换后池子里的旧主连接不会自动失效,不检测就会持续报 READONLY 错误;
  • Python 的 redis-py 用 Sentinel 类、Go 的 go-redis 用 NewFailoverClient,思路一致:传入哨兵列表与 master 名,由客户端库订阅哨兵的切换事件。

第五步:切换演练流程

  1. 演练前记录基线:主从拓扑、各实例 INFO replication 输出、业务错误率;
  2. 业务低峰期执行,先 SHUTDOWN 一台从库,验证哨兵只告警不切换、业务无感;
  3. 再停主库,掐表记录从故障发生到客户端恢复写入的耗时,与预期(down-after + 切换耗时)对照;
  4. 检查数据一致性:切换前主库写入的计数键,在新主上核对数值;
  5. 恢复旧主,确认它自动成为新主的从库且同步追平;
  6. 把三台机器的口令、quorum、down-after 等参数做一次复核,演练结论写入记录。

验证

redis-cli -p 26379 INFO sentinel
redis-cli -p 26379 SENTINEL master mymaster | grep -E "num-slaves|num-other|flags"
redis-cli -h 192.0.2.32 -a CHANGE_ME_STRONG INFO replication | grep role

预期:哨兵视角下 mymaster 状态正常,从库与哨兵数量正确;模拟停主后 30 秒内完成切换,客户端无需改配置即可恢复写入。

常见问题

  • 脑裂与数据丢失:网络分区时旧主可能还在接受写入,恢复后这部分数据在切换中被回滚丢弃。缓解靠主库的 min-replicas-to-write 与 min-replicas-max-lag(本文已配),以及客户端写入时尽量走多数派确认。Redis 哨兵是异步复制,极端情况下丢少量数据无法完全避免,强一致场景不应选它。
  • 哨兵自身重启导致状态异常:sentinel.conf 被哨兵改写后,部署工具若用旧模板覆盖重启,纪元信息丢失可能引发重复选举。配置管理工具应把 sentinel.conf 排除在覆盖范围之外,或改参数统一走 SENTINEL SET。
  • 密码不一致引发的连锁报错:改口令要同时改三处——各实例的 requirepass 与 masterauth、哨兵的 auth-pass、客户端连接参数。只改一处会出现从库连不上主(NOAUTH)、哨兵监控失败等不同表象,排查时先核对口令再查网络。
  • 哨兵全部部署在同一台宿主机:三哨兵必须分散在不同故障域,挤在同一台物理机或同一可用区,宿主机一挂高可用形同虚设;同理不要拿两哨兵凑数,偶数哨兵在分区时两边都达不到 quorum。

小结:哨兵模式的部署不复杂,成败取决于三件事——quorum 与 down-after 取值是否匹配网络质量、客户端是否真正走了哨兵发现、口令与配置是否三台一致。把切换演练做成例行动作,而不是上线时的一次性表演。

发表回复

后才能评论