7.2 Sentinel哨兵:三个法定人数 本节摘要:Sentinel 是独立运行的裁判进程集群,持续探测主库,经"主观下线加客观下线"两阶段确认故障,再按优先级选出接班从库执行切换。法定人数机制防止单个裁判误判,多哨兵互相监听防止单点。 为什么裁判也要是集群 主库挂了让谁来切?如果只有一个裁判进程,它自己挂了就没人切——裁判也需要高可用。于是哨兵自身组成集群(建议至少 3 个、奇数、跨机器),两件事分开做: 主观下线:单个哨兵 ping 主库超时,它"个人认为"主库挂了。 客观下线:该哨兵询问其他哨兵,认同挂的票数达到 (通常过半),才定性为真故障。 这个两阶段设计与分布式系统里的多数派裁决同构:个别哨兵网络抖动不会引发误切换,代价是确认延迟多了几秒。
本节摘要:Sentinel 是独立运行的裁判进程集群,持续探测主库,经"主观下线加客观下线"两阶段确认故障,再按优先级选出接班从库执行切换。法定人数机制防止单个裁判误判,多哨兵互相监听防止单点。
主库挂了让谁来切?如果只有一个裁判进程,它自己挂了就没人切——裁判也需要高可用。于是哨兵自身组成集群(建议至少 3 个、奇数、跨机器),两件事分开做:
quorum(通常过半),才定性为真故障。这个两阶段设计与分布式系统里的多数派裁决同构:个别哨兵网络抖动不会引发误切换,代价是确认延迟多了几秒。
从库们按三层规则排序:先看 replica-priority(数字小者优先,0 表示永不参选);平局比复制偏移量(数据更全的赢);再平局比 runid 字典序。切换执行时哨兵还要完成一串收尾:对旧主执行 SLAVEOF NO ONE 提升、其余从库改指向新主、把新地址广播给客户端与其它哨兵。
三个哨兵的配置几乎相同,以其中一个为例:
port 26379 sentinel monitor mymaster 10.0.0.11 6390 2 # 主名 IP 端口 quorum sentinel down-after-milliseconds mymaster 5000 sentinel failover-timeout mymaster 60000 sentinel parallel-syncs mymaster 1 # 切换后同时改向的从库数
redis-server sentinel.conf --sentinel # 启动哨兵 redis-cli -p 26379 SENTINEL get-master-addr-by-name mymaster # 查当前主
down-after-milliseconds 是敏感参数:太短网络抖动就触发切换(切换本身有秒级写中断),太长故障恢复慢。内网稳定环境 5 秒是常见值;跨机房抖动大,10 秒起步。
切换不是玄学,可以当演练来做。背景:一套一主两从加三哨兵的测试环境,想验证故障转移真的会发生、要多久;操作:
# 1号哨兵上查询当前主库地址 > redis-cli -p 26379 SENTINEL get-master-addr-by-name mymaster 1) "10.0.0.11" 2) "6390" # 观察哨兵日志或事件流 > redis-cli -p 26379 SENTINEL myid # 各哨兵的身份 # 约 5秒主观下线,再加1到2秒投票,完成切换后查询 > redis-cli -p 26379 SENTINEL get-master-addr-by-name mymaster 1) "10.0.0.12" 2) "6390" # 新主已上任 > redis-cli -p 26379 SENTINEL replicas mymaster # 旧主回来后出现在从库名单里,自动改从新主
解读时间线:down-after-milliseconds 决定"多快起疑",quorum 决定"多快定性",failover-timeout 决定"一次转移的整体预算"。演练时掐表记录从杀进程到新主可写的总时长,这个数字就是给业务方的"写中断承诺"。演练还有个隐藏收益:旧主恢复后的行为(自动降级为从并追同步)被亲眼看过一次,真事故时就不慌。
应用不直连主库地址,而是问哨兵要。主流客户端库都内置哨兵模式,行为是:启动时从任一哨兵拿主地址并缓存;命令报"主库已变"类错误时重新询问并重连。自己的实现里要写的就是这两个钩子,重连逻辑缺失是哨兵体系最常见的翻车点。
验收客户端配合度有个简单办法:在测试环境手动触发一次切换,观察应用日志——正常表现是"一批命令报错后一秒内恢复";若持续报错直到重启才恢复,就是重连钩子没接上。这个实验应该在上线前做,而不是等第一次真实故障来做。
⚠️ 常见坑:quorum 设 1 等于放弃多数派保护,单哨兵误判即切换;哨兵与 Redis 混部署在同一台宿主机,宿主机死机时裁判与选手同时退场;写中断窗口客观存在(切换期间秒级),秒杀类业务要有降级预案而不是指望切换零感知。
再补一个进阶但常见的问题:脑裂的缓兵之计。网络分区时旧主还活着、哨兵已经选出新主,两台"主库"并存,旧主上的写入在分区愈合后会被新主的同步覆盖,悄悄蒸发。完全杜绝需要共识层面的改造,Redis 给的缓兵之计是给主库配 min-replicas-to-write 与 min-replicas-max-lag:从库数量不足或延迟过大时,主库拒绝写入。旧主被分区孤立后一个从库都够不着,写入口自动关闭——丢数据的窗口从"分区期间全程"缩成"检测延迟那几秒"。代价是把可用性换了一部分给一致性,参数怎么配,取决于业务更恨丢数据还是更恨写不进。