Redis哨兵模式详解:高可用架构与故障转移实战

发布时间:2026/10/21 16:25:08
Redis哨兵模式详解:高可用架构与故障转移实战 1. 从一次通宵故障说起主从复制为什么救不了你1.1 那晚到底发生了什么凌晨一点半某业务的告警群里突然炸了——接口大面积 5xx前端页面白屏所有登录状态全部丢失。查了一圈定位到 Redis 主节点所在机器内存耗尽进程被系统 OOM Killer 杀掉。当时架构里是有一套主从复制的主库挂了以后从库上有完整数据但业务代码里连接的是主库 IP主库一挂整个缓存层就瘫了。那晚最后是运维手动改了从节点的配置执行SLAVEOF NO ONE才恢复写入整个过程花了二十多分钟。这不是个例。很多团队都在用 Redis 主从复制但主从复制本身解决的是“数据有备份”的问题不是“服务不中断”的问题。主库挂了从库不会自动顶上去业务方也不会自动把流量切到从库。Redis哨兵模式Sentinel就是奔着这个痛点来的——它能自动盯着主节点发现主节点挂了以后自动把某个从节点提升为新的主节点并且把其他从节点重新指向新主。这篇文章我会从哨兵的工作原理、配置部署、故障转移全流程、客户端接入、踩坑经验几个角度完整过一遍适合正在用主从复制想升级到高可用或者已经配了哨兵但想搞清楚底层机制的人。内容偏实操也会把“为什么这样设计”讲清楚。1.2 哨兵模式到底干了几件事Sentinel 看上去是个单独的进程默认端口 26379跟 Redis 数据节点不是一回事。它不存业务数据专门做“监工”的活核心职责可以分成四项监控持续检查主从节点是否存活判断有没有出问题。通知通过 Pub/Sub 机制把故障转移等事件广播给客户端或其他哨兵。自动故障转移主节点挂了以后自动执行从节点提升、从节点重新配置、旧主降级。配置提供客户端可以通过询问哨兵拿到当前真正的主节点地址。前两项好理解后两项是真正让它“高可用”的关键。很多人以为哨兵就是“能发现主挂了”真正的杀手锏是后面半段——发现挂了之后它要自己完成整个主从切换动作整个过程不需要人工介入。1.3 哨兵不是数据库集群它只是一组“监工”先澄清一个容易搞混的概念哨兵不是把 Redis 做成了分布式数据库集群它跟 Redis Cluster 是两条路线。哨兵模式下依然只有一个主节点承担写入从节点主要做读扩展和备份系统整体写能力没有提升Cluster 则把数据分片到多个主节点上。哨兵的核心目标只有一个高可用不是横向扩容。另外哨兵本身也是分布式部署的。通常建议至少三个哨兵进程互为主备。为什么不能只部署一个因为它自己也可能挂一个哨兵挂了没人管主节点等于没高可用。部署两个也不理想因为故障转移涉及“投票”偶数节点在票数相同时容易僵住具体机制下一节讲。2. 心跳、口水与投票Sentinel 的监控机制拆解2.1 三个定时任务在后台干了啥每个 Sentinel 进程内部会周期性执行三类任务这是它的一切判断基础任务频率作用向数据节点发 INFO每 10 秒一次获取主从拓扑、复制偏移量、运行 ID 等信息向数据节点发 PING每秒一次心跳检测判断节点是否活着通过__sentinel__:hello频道广播每 2 秒一次与其他哨兵交换对节点的看法发现新哨兵INFO 这条很关键。哨兵靠它拿到每个从节点的slave_repl_offset判断谁的数据更完整。出现故障时哨兵会用这些信息决定优先提升哪个从节点。PING 则是快速探测手段持续一段时间没收到有效响应就会进入下线判断流程。2.2 主观下线与客观下线为什么不能一个人说了算Sentinel 对“节点挂了”的判断分了两层主观下线sdown和客观下线odown。单个哨兵发现自己在一个down-after-milliseconds的时间窗口内没收到主节点的有效 PING 回复就会把主节点标记为主观下线。注意主观下线只是“我觉得它挂了”不代表真的挂——可能只是网络抖动、哨兵自身所在机器负载过高、GC 停顿导致心跳包延迟。关键的地方来了如果主节点被标记为 sdown哨兵并不会立刻发起故障转移而是会通过is-master-down-by-addr命令向其他哨兵询问“你们觉得这个主节点还活着吗”。当收到足够多超过 quorum 配置值的哨兵确认主节点不可达才会把主节点标记为客观下线这时候才真正进入故障转移流程。这个设计的本质是避免“误杀”。网络环境里瞬时抖动很常见一个哨兵自己看走眼不算数多个哨兵都认为主节点有问题才算坐实。这跟现实里开会表决是一个道理一个人说同事失联不算数大多数人都联系不上才可能真出事了。2.3 为什么哨兵之间要“讲票数”从 CAP 角度看理解投票机制还得从分布式系统的分区partition说起。假设主节点其实活得好好的但三个哨兵里有两个所在的网络区域跟主节点隔离了这时候大多数哨兵会认为主节点挂了于是触发故障转移。可旧主节点在网络分区的那一侧依然能接收写入等分区恢复后旧主降级为从节点时会丢弃分区期间写入的数据——这就是经典的脑裂数据丢失问题。哨兵的多数派设计并不能完全消灭脑裂但它保证了故障转移只由“大多数人”那侧发起不会出现两边同时认为自己是主节点的僵局。这也是为什么哨兵建议奇数个并且最好分散部署在不同故障域里。有一个事实要接受哨兵保证的是可用性优先在极端分区场景下可能丢数据。后面第 6 节会专门讲怎么缓解这个问题。3. 动手规划一个标准部署Sentinel 配置逐项解读3.1 先定拓扑三台哨兵 一主两从是及格线我自己搭哨兵环境时最常用的收敛配置是这样角色节点数说明Redis 主节点1正常只承担写流量Redis 从节点2一个用于读扩展一个用于兜底也参与 failover 投票Sentinel3分别部署在不同的物理机或可用区quorum2至少两个哨兵同意才算客观下线三个哨兵是经验值因为多数派要N/21三台哨兵必须有 2 台确认才能切换五台哨兵则需要 3 台七台 4 台。奇数节点能让多数派稳定形成偶数节点在故障时可能出现一半对一半的僵局。部署位置有个容易犯的错把三个哨兵全部跟三个 Redis 数据节点放在同一批机器上。这样一台机器挂了影响的可能是一个哨兵加一个数据节点会削弱决策能力。尽量让哨兵独立部署或者至少保证数据节点故障不会同时带走大多数哨兵。3.2 sentinel.conf 里每个关键参数别瞎抄哨兵的配置项不算多但每个都很重要。下面这份配置是我常用的一份模板port 26379 daemonize yes pidfile /var/run/redis-sentinel.pid logfile /var/log/redis/sentinel.log dir /var/lib/redis/sentinel sentinel monitor mymaster 127.0.0.1 6379 2 sentinel down-after-milliseconds mymaster 10000 sentinel failover-timeout mymaster 180000 sentinel parallel-syncs mymaster 1 sentinel auth-pass mymaster YourPassword sentinel notification-script mymaster /etc/redis/sentinel-notify.sh逐项解释一下关键配置sentinel monitor mymaster 127.0.0.1 6379 2监控名为mymaster的主节点地址为当前主库 IP 和端口最后的 2 就是 quorum。quorum 不是“票数总数”而是“客观下线所需的最小同意数”。down-after-milliseconds主节点连续多少毫秒没回复哨兵才认为它进入主观下线。这个值不是越小越好后面专门讲。failover-timeout一次故障转移的最长容忍时间窗包含判断、选举、切换的整个过程。如果超时当前这次 failover 会被判定失败哨兵会等待冷却后再尝试。这个值也影响旧主降级后重新同步数据的时间容忍度。parallel-syncs故障转移后同时允许几个从节点向新主做全量同步。设成 1 表示一个个来避免多个从节点同时全量复制把新主 IO 打满。auth-pass如果 Redis 开启了requirepass必须配这个否则哨兵发 INFO 命令会被拒绝它会一直认为主节点不可达。notification-script故障转移事件触发时执行的外部脚本可以拿来做告警通知。脚本需要自己写并保证可执行权限。注意这些配置项都属于某个主节点这里叫mymaster如果哨兵监控多套主从可以写多组。3.3 启动顺序与验证方法启动顺序有讲究顺序错了会出现哨兵把自己搞晕的情况先把主节点和从节点全部启动确认主从复制链路正常。再逐台启动哨兵进程使用redis-server /path/to/sentinel.conf --sentinel。启动完用redis-cli -p 26379 info sentinel查看哨兵已知的 master、slave、sentinel 数量。验证拓扑是否完整的命令我常用这几个redis-cli -p 26379 sentinel master mymaster redis-cli -p 26379 sentinel replicas mymaster redis-cli -p 26379 sentinel sentinels mymaster第一条返回当前主节点信息第二条列出所有从节点第三条列出其他已知哨兵。如果三条输出都符合预期说明整个哨兵网络已经互认准备进入状态。更彻底的自测方式是主动模拟故障对主节点执行debug sleep 30或者直接pkill -9掉 redis-server 进程观察哨兵会不会在预期时间内完成切换。这个动作我在上线前一定会做而且要留出观察窗口避免影响正常业务。提醒一下生产环境模拟故障要挑业务低峰期并且提前告知团队。4. 故障转移全链路拆解从“主观下线”到“新主上位”4.1 第一步判死阶段sdown - odown故障转移不是瞬间发生的整个过程可以拆成几个阶段。首先是判死某个哨兵发现主节点 PING 无响应持续超过down-after-milliseconds后标记 sdown接着它通过is-master-down-by-addr命令询问其他哨兵当收到足够的同意票达到 quorum后主节点变更为 odown。这里有一个细节很多人没注意is-master-down-by-addr这条命令同时承担了两个意图——确认主节点是否客观下线以及请求对方哨兵投票给自己作为故障转移执行者。也就是说发起询问的哨兵在问“你觉得它死了吗”的同时也在说“如果确认死了选我当执行人”。这是想成为“判官”leader的哨兵拉票的一种方式。4.2 第二步选判官Sentinel leader 选举客观下线确认后进入 leader 选举。所有主观认为主节点客观下线的哨兵都有资格参选选举规则是典型的多数派原则获得超过一半哨兵同意的候选者胜出。这套机制不是“谁先发现谁执行”而是保证同一时间只有一个哨兵在主持故障转移避免多个哨兵并发操作把集群搞乱。选举有超时保护如果一次没选出 leader哨兵会等待failover-timeout之后再次尝试。多数情况下选举过程非常快毫秒级完成。4.3 第三步挑新主过滤、打分、拍板选出的 leader 哨兵负责挑选新主节点筛选逻辑是层层过滤的不是随便挑一个排除不健康的从节点断线时间超过阈值的、长时间没回复 INFO 的、与主节点失联时长超过down-after-milliseconds十倍的全部淘汰。看优先级replica-priority配置值越小优先级越高0 表示该节点永远不会被选为主节点。这个字段在 redis.conf 里配置。看数据新旧如果有多个候选优先级相同比较slave_repl_offset偏移量越大说明数据越接近原主节点优先选。最终兜底再相同就比对 runid按字典序取最小的一个。挑出目标后leader 执行SLAVEOF NO ONE让该从节点变成主节点并等待它确认切换成功通过每秒 INFO 监控其角色是否变成 master。4.4 第四步改配置、通知小弟、旧主降级新主上位后leader 要向剩下的从节点发SLAVEOF 新主IP 新主端口让它们全部改认新主然后通过发布订阅广播故障转移结果。旧主节点恢复后哨兵会发现它还在线但已然不是主节点会主动对它发SLAVEOF 新主IP 新主端口让它降级为老实的从节点。做完这一切哨兵会把新拓扑写进自己的sentinel.conf文件。这个重写机制非常重要意味着哨兵的配置文件不能随便手工改否则可能被自动覆盖。想改配置要走SENTINEL SET命令。4.5 一次完整切换的时间线用三个哨兵 quorum2 的配置举一个例子时间点事件0ms主节点进程崩溃0ms - 10000ms三个哨兵持续 PING 无响应陆续进入主观下线判断10000ms 左右至少两个哨兵确认无响应主节点标记客观下线10010ms 左右leader 哨兵选举完成开始删选从节点10020ms 左右向选中的从节点发送SLAVEOF NO ONE10030ms 左右从节点晋升为主其余从节点改指新主10050ms 左右配置重写完成故障转移结束整个切换可以在 10 秒左右完成。如果这个时间不可接受可以调小down-after-milliseconds但调小会引入更多误判风险是个取舍问题后面细说。5. 客户端接入是最容易被忽略的坑切换完成后应用还是连不上5.1 地址写死 vs 通过哨兵发现很多人搭完哨兵觉得万事大吉结果真发生切换时业务照样崩。原因在于应用配置里写死的还是旧主节点 IP故障转移后主节点地址变了应用不会自己发现新地址。这是哨兵模式最容易被忽略的一环。哨兵本身完成了“数据库层”的高可用但如果客户端不感知哨兵高可用就是单向的。要让整套系统真正可用客户端必须配合做两件事之一直连哨兵获取当前主节点地址或者在故障转移后重新获取地址。5.2 主流客户端怎么接Java 生态里Spring Data Redis 的配置通常是这样以 Lettuce 为例spring: data: redis: sentinel: master: mymaster nodes: - 哨兵1地址:26379 - 哨兵2地址:26379 - 哨兵3地址:26379交给框架后Lettuce 会周期性向哨兵查询当前主节点地址并在主节点切换后自动刷新连接池应用层的读写自动走新主。Redisson 的写法也类似Config config new Config(); config.useSentinelServers() .setMasterName(mymaster) .addSentinelAddress(redis://哨兵1地址:26379, redis://哨兵2地址:26379, redis://哨兵3地址:26379); RedissonClient client Redisson.create(config);这些框架之所以能“感知”切换核心原理是订阅了哨兵的 Pub/Sub 消息频道switch-master等事件。事件一触发客户端立刻拿到新主地址并重建连接。如果你的业务是自己封装 Redis 连接、不走框架那就要自己处理这套逻辑或者考虑在前面挂一个代理/VIP 层来做地址收敛。5.3 为什么有时候切完主库连接却全断了还有一类问题是切换成功了但应用侧大量报连接超时。常见原因有连接池里的旧连接仍然指向旧主节点框架的拓扑刷新有延迟应用侧没有重试机制。在切换瞬间写入请求会短暂失败合理做法是配置客户端重试比如 Lettuce 的retry和熔断降级把切换期间的报错控制在上层可接受范围。不要指望一次切换完全无感知秒级抖动通常需要通过业务重试来消化。6. 高可用不是银弹哨兵模式下踩过的真实深坑6.1 网络分区与“脑裂”旧主还在接受写入怎么办前面提过脑裂场景主节点和哨兵之间网络断裂哨兵判定主节点挂了并切出新主但旧主节点在另一侧还活着业务写入依然在旧主。分区恢复后旧主变成新主的从节点分区期间写在旧主上的数据会随着复制同步被清掉直接造成数据丢失。这个坑没法百分之百消除能做的只有缓解。Redis 提供了两个参数min-replicas-to-write 1 min-replicas-max-lag 10含义是主节点只有在至少有 1 个从节点正常同步且延迟不超过 10 秒时才接受写入。如果分区导致没有从节点能跟上旧主会主动拒绝写入从而把脑裂期间的数据丢失窗口缩到最小。代价是牺牲一部分可用性数据安全和高可用在这里确实是矛盾的。6.2 down-after-milliseconds 太小会造成“抖一下就切换”我曾见过某个团队把down-after-milliseconds设成 3000ms理由是“切换要快”。结果线上偶发的 Java GC 停顿、网卡丢包、Redis 执行慢命令导致几百毫秒到一两秒的响应延迟就被哨兵误判为“主节点挂了”触发了一轮无意义的故障转移。切换过程本身有短暂不可用本来可以避免的故障反而被“高可用”放大了。我的经验值是内网环境 10000ms 起步跨机房或网络条件一般时设 15000ms-30000ms。切换的瓶颈其实不在判死而在整个链路是否顺畅把判死时间调得过于激进只会增加抖动成本。6.3 小心 Sentinel 重写配置文件哨兵运行过程中会不断重写自己的配置文件把当前识别的 master、slave、其他 sentinel 信息固化下来。如果你手动改了sentinel.conf里的某个参数过一会儿再看可能已经被覆盖了。正确的做法是动态修改配置用SENTINEL SET命令例如redis-cli -p 26379 sentinel set mymaster down-after-milliseconds 15000这样修改后哨兵会自己把新值写进配置文件并持久化。这个机制本身是优点它让拓扑信息能够跨重启保留但很多人不知道直接改文件导致配置总不生效排查半天。6.4 权限与安全别裸奔哨兵节点默认监听所有网卡如果没做限制外部可以直接通过哨兵获取你的主从拓扑甚至发起SENTINEL failover手动触发切换。所以至少做到bind内网 IP、开启protected-mode yes、用sentinel auth-pass和requirepass保护数据节点Redis 6 之后还可以用 ACL 把哨兵和数据节点的权限分开管理。生产环境建议把哨兵端口限制在可控网段内不暴露到公网。7. 交给运维时的自检清单和我的习惯7.1 上生产前必须验证的几件事配置写得再漂亮不演练都是纸面功夫。我每次给团队交付哨兵方案一定拉着运维做三轮验证杀主演练在低峰期 kill 掉主节点进程观察哨兵是否在预期时间内完成切换客户端是否自动恢复数据是否有丢失。网络分区演练用防火墙规则临时屏蔽主节点与哨兵之间的通信不直接杀进程观察哨兵判死和切换行为。这一步能暴露出很多真实故障才有的问题。哨兵自身故障演练停掉一个哨兵确认剩余哨兵依然构成多数派再停掉另一个确认系统会停止故障转移宁可不可用也不要脑裂这是预期行为。演练后还要检查 sentinel 日志里有没有异常报错客户端日志有没有大规模超时监控告警有没有正确触发。把这些跑顺才有底气上生产。7.2 我的配置基线仅供参考下表是我在常规项目里的落地参数按生产实践调整过参数推荐值说明Sentinel 数量3奇数quorum2可扩展为 5quorum2最少同意数一般取N/21down-after-milliseconds10000局域网内合理跨机房适当调大failover-timeout180000默认即可也可以压到 60000 加速重试parallel-syncs1串行同步避免瞬间全量复制风暴replica-priority100数值越小越优先0 表示不参与选举min-replicas-to-write1缓解脑裂丢数据min-replicas-max-lag10配合上一个参数使用7.3 最后提醒哨兵只是高可用的起点哨兵模式能让 Redis 在主节点故障时自动恢复服务但它不解决数据持久化、不解决容量瓶颈、不解决跨机房容灾。真正的高可用从来不是靠某一个组件扛起来的。我见过太多团队配了哨兵就以为万事大吉结果磁盘损坏导致主从数据全没或者切换后客户端没感知又得人肉救火。按照我的习惯哨兵落地后还得保证三件事并行推进RDB/AOF 持久化策略按业务容忍度定期验证恢复监控覆盖哨兵自身状态、主从复制延迟、内存增长趋势每隔一段时间主动做一次故障演练把切换流程练成肌肉记忆。把这些基础打好哨兵模式才能稳稳地托住业务。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询