Redis 从入门到精通(五):哨兵模式(Sentinel)—— 自动故障转移的完整原理与实战

Redis 从入门到精通(五):哨兵模式(Sentinel)------ 自动故障转移的完整原理与实战


一、Sentinel 是什么?为什么需要它?

1.1 从主从复制到高可用的最后一公里

主从架构:

复制代码
Master(主库) ← 写请求
    ↓
Slave-1, Slave-2, Slave-3(从库) ← 读请求

如果主库宕机了:

  • 写请求全部失败
  • 需要手动选一个从库升级为主库
  • 需要通知所有客户端新的主库地址
  • 需要把其他从库指向新主库

这一套操作如果靠人工,少则几分钟,多则半小时。Sentinel 把这个过程自动化到了秒级

1.2 Sentinel 的四大核心能力

#mermaid-svg-EWAPvBwoOkcGN7si{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-EWAPvBwoOkcGN7si .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-EWAPvBwoOkcGN7si .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-EWAPvBwoOkcGN7si .error-icon{fill:#552222;}#mermaid-svg-EWAPvBwoOkcGN7si .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-EWAPvBwoOkcGN7si .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-EWAPvBwoOkcGN7si .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-EWAPvBwoOkcGN7si .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-EWAPvBwoOkcGN7si .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-EWAPvBwoOkcGN7si .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-EWAPvBwoOkcGN7si .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-EWAPvBwoOkcGN7si .marker{fill:#333333;stroke:#333333;}#mermaid-svg-EWAPvBwoOkcGN7si .marker.cross{stroke:#333333;}#mermaid-svg-EWAPvBwoOkcGN7si svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-EWAPvBwoOkcGN7si p{margin:0;}#mermaid-svg-EWAPvBwoOkcGN7si .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-EWAPvBwoOkcGN7si .cluster-label text{fill:#333;}#mermaid-svg-EWAPvBwoOkcGN7si .cluster-label span{color:#333;}#mermaid-svg-EWAPvBwoOkcGN7si .cluster-label span p{background-color:transparent;}#mermaid-svg-EWAPvBwoOkcGN7si .label text,#mermaid-svg-EWAPvBwoOkcGN7si span{fill:#333;color:#333;}#mermaid-svg-EWAPvBwoOkcGN7si .node rect,#mermaid-svg-EWAPvBwoOkcGN7si .node circle,#mermaid-svg-EWAPvBwoOkcGN7si .node ellipse,#mermaid-svg-EWAPvBwoOkcGN7si .node polygon,#mermaid-svg-EWAPvBwoOkcGN7si .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-EWAPvBwoOkcGN7si .rough-node .label text,#mermaid-svg-EWAPvBwoOkcGN7si .node .label text,#mermaid-svg-EWAPvBwoOkcGN7si .image-shape .label,#mermaid-svg-EWAPvBwoOkcGN7si .icon-shape .label{text-anchor:middle;}#mermaid-svg-EWAPvBwoOkcGN7si .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-EWAPvBwoOkcGN7si .rough-node .label,#mermaid-svg-EWAPvBwoOkcGN7si .node .label,#mermaid-svg-EWAPvBwoOkcGN7si .image-shape .label,#mermaid-svg-EWAPvBwoOkcGN7si .icon-shape .label{text-align:center;}#mermaid-svg-EWAPvBwoOkcGN7si .node.clickable{cursor:pointer;}#mermaid-svg-EWAPvBwoOkcGN7si .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-EWAPvBwoOkcGN7si .arrowheadPath{fill:#333333;}#mermaid-svg-EWAPvBwoOkcGN7si .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-EWAPvBwoOkcGN7si .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-EWAPvBwoOkcGN7si .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-EWAPvBwoOkcGN7si .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-EWAPvBwoOkcGN7si .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-EWAPvBwoOkcGN7si .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-EWAPvBwoOkcGN7si .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-EWAPvBwoOkcGN7si .cluster text{fill:#333;}#mermaid-svg-EWAPvBwoOkcGN7si .cluster span{color:#333;}#mermaid-svg-EWAPvBwoOkcGN7si div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-EWAPvBwoOkcGN7si .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-EWAPvBwoOkcGN7si rect.text{fill:none;stroke-width:0;}#mermaid-svg-EWAPvBwoOkcGN7si .icon-shape,#mermaid-svg-EWAPvBwoOkcGN7si .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-EWAPvBwoOkcGN7si .icon-shape p,#mermaid-svg-EWAPvBwoOkcGN7si .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-EWAPvBwoOkcGN7si .icon-shape .label rect,#mermaid-svg-EWAPvBwoOkcGN7si .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-EWAPvBwoOkcGN7si .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-EWAPvBwoOkcGN7si .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-EWAPvBwoOkcGN7si :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 4. 配置提供者(Configuration Provider)
客户端通过 Sentinel

发现当前主库地址
3. 自动故障转移(Failover)
主库下线 → 选举新主库

→ 切换从库指向

→ 通知客户端
2. 通知(Notification)
通过 Pub/Sub 或 API

通知管理员/客户端

节点状态变化

  1. 监控(Monitoring)
    定期 PING 主库和从库

检查存活状态
Sentinel 集群
Sentinel-1
Sentinel-2
Sentinel-3

1.3 Sentinel 本身的特点

特性 说明
独立进程 Sentinel 是独立于 Redis 的进程(redis-sentinelredis-server --sentinel
不存业务数据 Sentinel 只做监控和协调,不存储业务数据
需要集群部署 至少 3 个 Sentinel 实例(避免单点故障和脑裂)
多数派决策 故障判断和选举需要过半数 Sentinel 同意
基于 Pub/Sub Sentinel 之间通过 Redis 的 Pub/Sub 发现彼此

二、Sentinel 的监控机制:如何发现节点和彼此?

2.1 节点发现的三种方式

#mermaid-svg-bdClM0MYcG2MTqfe{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-bdClM0MYcG2MTqfe .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-bdClM0MYcG2MTqfe .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-bdClM0MYcG2MTqfe .error-icon{fill:#552222;}#mermaid-svg-bdClM0MYcG2MTqfe .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-bdClM0MYcG2MTqfe .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-bdClM0MYcG2MTqfe .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-bdClM0MYcG2MTqfe .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-bdClM0MYcG2MTqfe .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-bdClM0MYcG2MTqfe .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-bdClM0MYcG2MTqfe .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-bdClM0MYcG2MTqfe .marker{fill:#333333;stroke:#333333;}#mermaid-svg-bdClM0MYcG2MTqfe .marker.cross{stroke:#333333;}#mermaid-svg-bdClM0MYcG2MTqfe svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-bdClM0MYcG2MTqfe p{margin:0;}#mermaid-svg-bdClM0MYcG2MTqfe .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-bdClM0MYcG2MTqfe .cluster-label text{fill:#333;}#mermaid-svg-bdClM0MYcG2MTqfe .cluster-label span{color:#333;}#mermaid-svg-bdClM0MYcG2MTqfe .cluster-label span p{background-color:transparent;}#mermaid-svg-bdClM0MYcG2MTqfe .label text,#mermaid-svg-bdClM0MYcG2MTqfe span{fill:#333;color:#333;}#mermaid-svg-bdClM0MYcG2MTqfe .node rect,#mermaid-svg-bdClM0MYcG2MTqfe .node circle,#mermaid-svg-bdClM0MYcG2MTqfe .node ellipse,#mermaid-svg-bdClM0MYcG2MTqfe .node polygon,#mermaid-svg-bdClM0MYcG2MTqfe .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-bdClM0MYcG2MTqfe .rough-node .label text,#mermaid-svg-bdClM0MYcG2MTqfe .node .label text,#mermaid-svg-bdClM0MYcG2MTqfe .image-shape .label,#mermaid-svg-bdClM0MYcG2MTqfe .icon-shape .label{text-anchor:middle;}#mermaid-svg-bdClM0MYcG2MTqfe .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-bdClM0MYcG2MTqfe .rough-node .label,#mermaid-svg-bdClM0MYcG2MTqfe .node .label,#mermaid-svg-bdClM0MYcG2MTqfe .image-shape .label,#mermaid-svg-bdClM0MYcG2MTqfe .icon-shape .label{text-align:center;}#mermaid-svg-bdClM0MYcG2MTqfe .node.clickable{cursor:pointer;}#mermaid-svg-bdClM0MYcG2MTqfe .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-bdClM0MYcG2MTqfe .arrowheadPath{fill:#333333;}#mermaid-svg-bdClM0MYcG2MTqfe .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-bdClM0MYcG2MTqfe .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-bdClM0MYcG2MTqfe .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-bdClM0MYcG2MTqfe .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-bdClM0MYcG2MTqfe .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-bdClM0MYcG2MTqfe .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-bdClM0MYcG2MTqfe .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-bdClM0MYcG2MTqfe .cluster text{fill:#333;}#mermaid-svg-bdClM0MYcG2MTqfe .cluster span{color:#333;}#mermaid-svg-bdClM0MYcG2MTqfe div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-bdClM0MYcG2MTqfe .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-bdClM0MYcG2MTqfe rect.text{fill:none;stroke-width:0;}#mermaid-svg-bdClM0MYcG2MTqfe .icon-shape,#mermaid-svg-bdClM0MYcG2MTqfe .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-bdClM0MYcG2MTqfe .icon-shape p,#mermaid-svg-bdClM0MYcG2MTqfe .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-bdClM0MYcG2MTqfe .icon-shape .label rect,#mermaid-svg-bdClM0MYcG2MTqfe .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-bdClM0MYcG2MTqfe .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-bdClM0MYcG2MTqfe .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-bdClM0MYcG2MTqfe :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Sentinel 节点发现机制

  1. 配置文件指定主库地址

sentinel monitor mymaster 192.168.1.100 6379 2
2. 向主库发送 INFO replication

获取所有从库信息
3. 订阅主库的 sentinel:hello 频道

发现其他 Sentinel

第一步:每个 Sentinel 启动时,从配置文件读取要监控的主库信息。

bash 复制代码
# sentinel.conf
sentinel monitor mymaster 192.168.1.100 6379 2
#              ↑ 名称    ↑ 主库 IP      ↑ 端口  ↑ quorum(法定人数)

第二步 :Sentinel 连接到主库后,每 10 秒发送一次 INFO replication,获取从库列表并自动发现和监控所有从库。

第三步 :Sentinel 每秒向主库和从库的 __sentinel__:hello 频道发布一条消息(包含自己的 IP、端口、runid),同时也订阅这个频道。这样,所有监控同一个主库的 Sentinel 就能彼此发现了。

2.2 三个定时任务

Sentinel 有三个核心定时任务,构成了监控的基础:

任务 频率 目标 作用
PING 每秒 主库、从库、其他 Sentinel 心跳检测,判断节点是否存活
INFO 每 10 秒 主库、从库 获取拓扑信息(从库列表、主库状态)
Pub/Sub 每 2 秒 主库的 __sentinel__:hello 发布自身信息,发现其他 Sentinel

三、主观下线(SDOWN)与客观下线(ODOWN):Sentinel 最核心的判断逻辑

3.1 为什么需要两步判断?

这是 Sentinel 设计中最精妙的地方。考虑这个场景:

复制代码
Sentinel-1 和主库之间的网络断了,但主库本身还活着,
Sentinel-2 和 Sentinel-3 都能正常连接主库。

如果 Sentinel-1 单方面判定主库死了,触发故障转移,
就会出现两个主库------脑裂(Split-Brain)。

解决方案:不能只听一个人说"主库死了",需要多数派确认。
#mermaid-svg-ZdAVkv5VERRyUrQw{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ZdAVkv5VERRyUrQw .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ZdAVkv5VERRyUrQw .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ZdAVkv5VERRyUrQw .error-icon{fill:#552222;}#mermaid-svg-ZdAVkv5VERRyUrQw .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ZdAVkv5VERRyUrQw .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ZdAVkv5VERRyUrQw .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ZdAVkv5VERRyUrQw .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ZdAVkv5VERRyUrQw .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ZdAVkv5VERRyUrQw .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ZdAVkv5VERRyUrQw .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ZdAVkv5VERRyUrQw .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ZdAVkv5VERRyUrQw .marker.cross{stroke:#333333;}#mermaid-svg-ZdAVkv5VERRyUrQw svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ZdAVkv5VERRyUrQw p{margin:0;}#mermaid-svg-ZdAVkv5VERRyUrQw .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ZdAVkv5VERRyUrQw .cluster-label text{fill:#333;}#mermaid-svg-ZdAVkv5VERRyUrQw .cluster-label span{color:#333;}#mermaid-svg-ZdAVkv5VERRyUrQw .cluster-label span p{background-color:transparent;}#mermaid-svg-ZdAVkv5VERRyUrQw .label text,#mermaid-svg-ZdAVkv5VERRyUrQw span{fill:#333;color:#333;}#mermaid-svg-ZdAVkv5VERRyUrQw .node rect,#mermaid-svg-ZdAVkv5VERRyUrQw .node circle,#mermaid-svg-ZdAVkv5VERRyUrQw .node ellipse,#mermaid-svg-ZdAVkv5VERRyUrQw .node polygon,#mermaid-svg-ZdAVkv5VERRyUrQw .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ZdAVkv5VERRyUrQw .rough-node .label text,#mermaid-svg-ZdAVkv5VERRyUrQw .node .label text,#mermaid-svg-ZdAVkv5VERRyUrQw .image-shape .label,#mermaid-svg-ZdAVkv5VERRyUrQw .icon-shape .label{text-anchor:middle;}#mermaid-svg-ZdAVkv5VERRyUrQw .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ZdAVkv5VERRyUrQw .rough-node .label,#mermaid-svg-ZdAVkv5VERRyUrQw .node .label,#mermaid-svg-ZdAVkv5VERRyUrQw .image-shape .label,#mermaid-svg-ZdAVkv5VERRyUrQw .icon-shape .label{text-align:center;}#mermaid-svg-ZdAVkv5VERRyUrQw .node.clickable{cursor:pointer;}#mermaid-svg-ZdAVkv5VERRyUrQw .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ZdAVkv5VERRyUrQw .arrowheadPath{fill:#333333;}#mermaid-svg-ZdAVkv5VERRyUrQw .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ZdAVkv5VERRyUrQw .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ZdAVkv5VERRyUrQw .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ZdAVkv5VERRyUrQw .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ZdAVkv5VERRyUrQw .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ZdAVkv5VERRyUrQw .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ZdAVkv5VERRyUrQw .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ZdAVkv5VERRyUrQw .cluster text{fill:#333;}#mermaid-svg-ZdAVkv5VERRyUrQw .cluster span{color:#333;}#mermaid-svg-ZdAVkv5VERRyUrQw div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ZdAVkv5VERRyUrQw .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ZdAVkv5VERRyUrQw rect.text{fill:none;stroke-width:0;}#mermaid-svg-ZdAVkv5VERRyUrQw .icon-shape,#mermaid-svg-ZdAVkv5VERRyUrQw .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ZdAVkv5VERRyUrQw .icon-shape p,#mermaid-svg-ZdAVkv5VERRyUrQw .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ZdAVkv5VERRyUrQw .icon-shape .label rect,#mermaid-svg-ZdAVkv5VERRyUrQw .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ZdAVkv5VERRyUrQw .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ZdAVkv5VERRyUrQw .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ZdAVkv5VERRyUrQw :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是



Sentinel PING 主库
响应超时?

(down-after-milliseconds)
标记为主观下线 SDOWN

(Subjective Down)
主库正常
向其他 Sentinel 询问

SENTINEL is-master-down-by-addr
同意票数 ≥ quorum ?
标记为客观下线 ODOWN

(Objectively Down)
保持 SDOWN

不触发故障转移
启动故障转移流程

3.2 SDOWN(主观下线)

单个 Sentinel 的主观判断 。Sentinel 每秒向所有节点发送 PING,如果在 down-after-milliseconds(默认 30 秒)内没有收到有效回复,就认为该节点主观下线

bash 复制代码
# sentinel.conf
sentinel down-after-milliseconds mymaster 30000
# 30 秒无响应 → SDOWN

关键 :这只是一个 Sentinel 的个人意见,不足以触发故障转移。

3.3 ODOWN(客观下线)

多个 Sentinel 达成共识后的客观判断 。当某个 Sentinel 将主库标记为 SDOWN 后,它会通过 SENTINEL is-master-down-by-addr 命令询问其他 Sentinel 的意见。

同意票数 ≥ quorum 时,该 Sentinel 将主库标记为 ODOWN,并开始故障转移

bash 复制代码
# quorum 的含义
sentinel monitor mymaster 192.168.1.100 6379 2
#                                            ↑
#         至少需要 2 个 Sentinel 同意才能判定 ODOWN

3.4 quorum 怎么设?

这是 Sentinel 配置中最容易搞错的参数:

Sentinel 总数 推荐 quorum 容错能力
3 2 允许 1 个 Sentinel 挂掉
5 3 允许 2 个 Sentinel 挂掉
7 4 允许 3 个 Sentinel 挂掉

规律quorum = N/2 + 1(过半数原则)。设置为 Sentinel 总数的一半加一。

⚠️ ODOWN 判定用 quorum,Leader 选举用 majority(过半数),两者可能不同! quorum 设小了会导致误判(网络抖动就触发故障转移),设大了会导致故障转移无法触发(宕机了但票数不够)。


四、哨兵集群的领导者选举(Leader Election)

4.1 为什么需要选举?

ODOWN 判定通过后,所有 Sentinel 都知道主库挂了。但只能有一个 Sentinel 来执行故障转移,否则会出现多个 Sentinel 同时选不同从库当主库的混乱局面。

4.2 Raft 协议的简化版

Sentinel 的选举逻辑参考了 Raft 算法,但做了大幅简化:
Sentinel-C Sentinel-B Sentinel-A(候选者) Sentinel-C Sentinel-B Sentinel-A(候选者) #mermaid-svg-XOItEKOPLHSqPusk{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-XOItEKOPLHSqPusk .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-XOItEKOPLHSqPusk .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-XOItEKOPLHSqPusk .error-icon{fill:#552222;}#mermaid-svg-XOItEKOPLHSqPusk .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-XOItEKOPLHSqPusk .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-XOItEKOPLHSqPusk .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-XOItEKOPLHSqPusk .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-XOItEKOPLHSqPusk .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-XOItEKOPLHSqPusk .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-XOItEKOPLHSqPusk .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-XOItEKOPLHSqPusk .marker{fill:#333333;stroke:#333333;}#mermaid-svg-XOItEKOPLHSqPusk .marker.cross{stroke:#333333;}#mermaid-svg-XOItEKOPLHSqPusk svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-XOItEKOPLHSqPusk p{margin:0;}#mermaid-svg-XOItEKOPLHSqPusk .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-XOItEKOPLHSqPusk text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-XOItEKOPLHSqPusk .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-XOItEKOPLHSqPusk .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-XOItEKOPLHSqPusk .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-XOItEKOPLHSqPusk .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-XOItEKOPLHSqPusk #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-XOItEKOPLHSqPusk .sequenceNumber{fill:white;}#mermaid-svg-XOItEKOPLHSqPusk #sequencenumber{fill:#333;}#mermaid-svg-XOItEKOPLHSqPusk #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-XOItEKOPLHSqPusk .messageText{fill:#333;stroke:none;}#mermaid-svg-XOItEKOPLHSqPusk .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-XOItEKOPLHSqPusk .labelText,#mermaid-svg-XOItEKOPLHSqPusk .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-XOItEKOPLHSqPusk .loopText,#mermaid-svg-XOItEKOPLHSqPusk .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-XOItEKOPLHSqPusk .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-XOItEKOPLHSqPusk .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-XOItEKOPLHSqPusk .noteText,#mermaid-svg-XOItEKOPLHSqPusk .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-XOItEKOPLHSqPusk .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-XOItEKOPLHSqPusk .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-XOItEKOPLHSqPusk .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-XOItEKOPLHSqPusk .actorPopupMenu{position:absolute;}#mermaid-svg-XOItEKOPLHSqPusk .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-XOItEKOPLHSqPusk .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-XOItEKOPLHSqPusk .actor-man circle,#mermaid-svg-XOItEKOPLHSqPusk line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-XOItEKOPLHSqPusk :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 主库被标记为 ODOWN给自己投一票SENTINEL is-master-down-by-addr(请求投票,附带自己的 runid)SENTINEL is-master-down-by-addr(请求投票)检查:我还没投过票?A 的纪元 > 我的纪元?同意投票检查:我还没投过票?A 的纪元 > 我的纪元?同意投票获得 3 票 ≥ majority(2)成为 Leader开始执行故障转移

选举规则

  1. 每个 Sentinel 发现 ODOWN 后,会等待一段时间(随机优先级 + 延迟),然后向其他 Sentinel 请求投票
  2. 每个 Sentinel 在每个纪元(epoch)中只能投一票,先到先得
  3. 获得 ≥ majority(N/2 + 1) 票的 Sentinel 成为 Leader
  4. 如果没有人获得多数票,等待 2 倍故障转移超时后重新选举

注意 :这里的 majorityquorum 可能不同。假设 5 个 Sentinel,quorum = 3,但 majority = 3,实际上是一样的。但如果 quorum = 2(不推荐),ODOWN 只需 2 票,但 Leader 仍需要 3 票。


五、自动故障转移(Failover):新主库的诞生

5.1 如何选新主库?

Leader Sentinel 选新主库不是随机的,有一套完整的评分逻辑:
#mermaid-svg-12CdgF0Rj5prohpI{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-12CdgF0Rj5prohpI .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-12CdgF0Rj5prohpI .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-12CdgF0Rj5prohpI .error-icon{fill:#552222;}#mermaid-svg-12CdgF0Rj5prohpI .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-12CdgF0Rj5prohpI .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-12CdgF0Rj5prohpI .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-12CdgF0Rj5prohpI .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-12CdgF0Rj5prohpI .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-12CdgF0Rj5prohpI .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-12CdgF0Rj5prohpI .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-12CdgF0Rj5prohpI .marker{fill:#333333;stroke:#333333;}#mermaid-svg-12CdgF0Rj5prohpI .marker.cross{stroke:#333333;}#mermaid-svg-12CdgF0Rj5prohpI svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-12CdgF0Rj5prohpI p{margin:0;}#mermaid-svg-12CdgF0Rj5prohpI .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-12CdgF0Rj5prohpI .cluster-label text{fill:#333;}#mermaid-svg-12CdgF0Rj5prohpI .cluster-label span{color:#333;}#mermaid-svg-12CdgF0Rj5prohpI .cluster-label span p{background-color:transparent;}#mermaid-svg-12CdgF0Rj5prohpI .label text,#mermaid-svg-12CdgF0Rj5prohpI span{fill:#333;color:#333;}#mermaid-svg-12CdgF0Rj5prohpI .node rect,#mermaid-svg-12CdgF0Rj5prohpI .node circle,#mermaid-svg-12CdgF0Rj5prohpI .node ellipse,#mermaid-svg-12CdgF0Rj5prohpI .node polygon,#mermaid-svg-12CdgF0Rj5prohpI .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-12CdgF0Rj5prohpI .rough-node .label text,#mermaid-svg-12CdgF0Rj5prohpI .node .label text,#mermaid-svg-12CdgF0Rj5prohpI .image-shape .label,#mermaid-svg-12CdgF0Rj5prohpI .icon-shape .label{text-anchor:middle;}#mermaid-svg-12CdgF0Rj5prohpI .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-12CdgF0Rj5prohpI .rough-node .label,#mermaid-svg-12CdgF0Rj5prohpI .node .label,#mermaid-svg-12CdgF0Rj5prohpI .image-shape .label,#mermaid-svg-12CdgF0Rj5prohpI .icon-shape .label{text-align:center;}#mermaid-svg-12CdgF0Rj5prohpI .node.clickable{cursor:pointer;}#mermaid-svg-12CdgF0Rj5prohpI .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-12CdgF0Rj5prohpI .arrowheadPath{fill:#333333;}#mermaid-svg-12CdgF0Rj5prohpI .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-12CdgF0Rj5prohpI .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-12CdgF0Rj5prohpI .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-12CdgF0Rj5prohpI .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-12CdgF0Rj5prohpI .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-12CdgF0Rj5prohpI .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-12CdgF0Rj5prohpI .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-12CdgF0Rj5prohpI .cluster text{fill:#333;}#mermaid-svg-12CdgF0Rj5prohpI .cluster span{color:#333;}#mermaid-svg-12CdgF0Rj5prohpI div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-12CdgF0Rj5prohpI .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-12CdgF0Rj5prohpI rect.text{fill:none;stroke-width:0;}#mermaid-svg-12CdgF0Rj5prohpI .icon-shape,#mermaid-svg-12CdgF0Rj5prohpI .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-12CdgF0Rj5prohpI .icon-shape p,#mermaid-svg-12CdgF0Rj5prohpI .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-12CdgF0Rj5prohpI .icon-shape .label rect,#mermaid-svg-12CdgF0Rj5prohpI .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-12CdgF0Rj5prohpI .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-12CdgF0Rj5prohpI .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-12CdgF0Rj5prohpI :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 获取所有从库列表
过滤:SDOWN / ODOWN

的从库排除
过滤:断连超过

down-after-milliseconds × 10

的从库排除
对剩余从库打分排序
第一优先级:

replica-priority 最小
第二优先级:

复制偏移量(offset)最大

即数据最完整
第三优先级:

runid 字典序最小
选出最优从库

从库优先级(replica-priority):可以手动配置,数值越小越优先。

bash 复制代码
# redis.conf(在从库上配置)
replica-priority 10    # 默认 100,越小越优先
replica-priority 0     # 永远不会被选为主库(纯备机)

5.2 故障转移的完整步骤

Slave_Old 客户端 其他从库 选中的从库 Leader Sentinel Slave_Old 客户端 其他从库 选中的从库 Leader Sentinel #mermaid-svg-gKhvQzdbFxRioPZC{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-gKhvQzdbFxRioPZC .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-gKhvQzdbFxRioPZC .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-gKhvQzdbFxRioPZC .error-icon{fill:#552222;}#mermaid-svg-gKhvQzdbFxRioPZC .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-gKhvQzdbFxRioPZC .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-gKhvQzdbFxRioPZC .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-gKhvQzdbFxRioPZC .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-gKhvQzdbFxRioPZC .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-gKhvQzdbFxRioPZC .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-gKhvQzdbFxRioPZC .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-gKhvQzdbFxRioPZC .marker{fill:#333333;stroke:#333333;}#mermaid-svg-gKhvQzdbFxRioPZC .marker.cross{stroke:#333333;}#mermaid-svg-gKhvQzdbFxRioPZC svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-gKhvQzdbFxRioPZC p{margin:0;}#mermaid-svg-gKhvQzdbFxRioPZC .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-gKhvQzdbFxRioPZC text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-gKhvQzdbFxRioPZC .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-gKhvQzdbFxRioPZC .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-gKhvQzdbFxRioPZC .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-gKhvQzdbFxRioPZC .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-gKhvQzdbFxRioPZC #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-gKhvQzdbFxRioPZC .sequenceNumber{fill:white;}#mermaid-svg-gKhvQzdbFxRioPZC #sequencenumber{fill:#333;}#mermaid-svg-gKhvQzdbFxRioPZC #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-gKhvQzdbFxRioPZC .messageText{fill:#333;stroke:none;}#mermaid-svg-gKhvQzdbFxRioPZC .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-gKhvQzdbFxRioPZC .labelText,#mermaid-svg-gKhvQzdbFxRioPZC .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-gKhvQzdbFxRioPZC .loopText,#mermaid-svg-gKhvQzdbFxRioPZC .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-gKhvQzdbFxRioPZC .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-gKhvQzdbFxRioPZC .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-gKhvQzdbFxRioPZC .noteText,#mermaid-svg-gKhvQzdbFxRioPZC .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-gKhvQzdbFxRioPZC .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-gKhvQzdbFxRioPZC .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-gKhvQzdbFxRioPZC .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-gKhvQzdbFxRioPZC .actorPopupMenu{position:absolute;}#mermaid-svg-gKhvQzdbFxRioPZC .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-gKhvQzdbFxRioPZC .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-gKhvQzdbFxRioPZC .actor-man circle,#mermaid-svg-gKhvQzdbFxRioPZC line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-gKhvQzdbFxRioPZC :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 如果旧主库恢复上线 1. 从从库列表中选出最佳候选者2. SLAVEOF NO ONE(升级为主库)执行命令,变为主库3. SLAVEOF <新主库 IP> <端口>(其他从库指向新主库)指向新主库,开始全量/增量复制4. 更新内部状态将旧主库标记为新从库5. SLAVEOF <新主库 IP> <端口>(旧主库降级为从库)6. 通过 Pub/Sub 通知客户端主库地址变更(+switch-master 频道)

5.3 客户端如何感知主库切换?

客户端通过订阅 Sentinel 的 +switch-master 频道,或定期向 Sentinel 查询主库地址来自动切换:

bash 复制代码
# 查询当前主库地址
redis-cli -h sentinel-host -p 26379 SENTINEL get-master-addr-by-name mymaster
# 返回:["192.168.1.101", "6379"]  ← 新主库地址

# 订阅切换事件
redis-cli -h sentinel-host -p 26379 SUBSCRIBE +switch-master

Java 客户端(如 Lettuce / Redisson)通常内置了 Sentinel 感知机制,能自动发现主库切换并更新连接池,开发者基本无感。


六、实战部署:从零搭建 Sentinel 集群

6.1 架构拓扑

复制代码
                ┌─────────────────────────┐
                │     Sentinel 集群         │
                │  Sentinel-1  : 26379     │
                │  Sentinel-2  : 26380     │
                │  Sentinel-3  : 26381     │
                └──────────┬──────────────┘
                           │ 监控
                           ↓
        ┌──────────────────┴──────────────────┐
        │                                      │
   ┌────▼─────┐                          ┌────▼─────┐
   │  Master  │◄─────── 复制 ──────────►│ Slave-1  │
   │ :6379    │                          │ :6380    │
   └──────────┘                          └──────────┘
                                               │
                                        ┌──────▼─────┐
                                        │  Slave-2   │
                                        │  :6381     │
                                        └────────────┘

6.2 部署步骤

第一步:准备 Redis 实例

bash 复制代码
# 主库配置(redis-6379.conf)
port 6379
bind 0.0.0.0
requirepass "your_redis_password"
masterauth "your_redis_password"    # 主库故障转移后变为从库时需要

# 从库1配置(redis-6380.conf)
port 6380
bind 0.0.0.0
replicaof 192.168.1.100 6379
masterauth "your_redis_password"
replica-read-only yes

# 从库2配置(redis-6381.conf)
port 6381
bind 0.0.0.0
replicaof 192.168.1.100 6379
masterauth "your_redis_password"
replica-read-only yes

第二步:准备 Sentinel 配置

bash 复制代码
# 每个 Sentinel 的配置完全一样(sentinel.conf)
port 26379                                              # Sentinel 端口
sentinel monitor mymaster 192.168.1.100 6379 2         # 监控主库,quorum=2
sentinel auth-pass mymaster your_redis_password        # 主库密码
sentinel down-after-milliseconds mymaster 30000        # 30秒超时
sentinel failover-timeout mymaster 180000              # 故障转移超时 3 分钟
sentinel parallel-syncs mymaster 1                      # 一次只让 1 个从库同步

第三步:启动

bash 复制代码
# 启动 Sentinel
redis-sentinel /path/to/sentinel.conf

# 或
redis-server /path/to/sentinel.conf --sentinel &

# 验证
redis-cli -p 26379 SENTINEL master mymaster
redis-cli -p 26379 SENTINEL slaves mymaster
redis-cli -p 26379 SENTINEL sentinels mymaster

6.3 Sentinel 配置参数详解

参数 默认值 建议值 说明
sentinel monitor mymaster IP port 2 监控配置,quorum 见上文分析
sentinel down-after-milliseconds 30000 5000-30000 心跳超时时间(毫秒),设置过小会导致网络抖动误判,过大则故障发现延迟增加
sentinel failover-timeout 180000 60000-180000 故障转移的全局超时(毫秒),包括选新主库、数据同步、切换等
sentinel parallel-syncs 1 1-2 故障转移后,允许多少个从库同时向新主库发起全量同步。设 1 最安全
sentinel deny-scripts-reconfig yes yes 禁止 SENTINEL SET 动态修改脚本路径(安全考虑)

七、常见坑点与排查指南

7.1 坑 1:脑裂(Split-Brain)

场景:Sentinel 集群与主库之间网络中断,Sentinel 判定主库 ODOWN 并选出新主库;但旧主库其实还活着,客户端还在往旧主库写数据。此时两个主库并存------脑裂。

复制代码
客户端A → 旧主库(写入数据A)  ← 这部分数据在故障恢复后会丢失
客户端B → 新主库(写入数据B)

解决方案

bash 复制代码
# redis.conf(主库配置)
min-replicas-to-write 1          # 至少要有 1 个从库在线
min-replicas-max-lag 10          # 从库延迟不超过 10 秒

# 含义:如果主库发现从库数量不足或延迟过大,拒绝写入
# 这样脑裂时旧主库无法写入,避免数据分歧

7.2 坑 2:Sentinel 无法达成一致

症状+odown 日志出现但没有 +failover。可能原因:

原因 排查方法
quorum 设置过大 SENTINEL master mymaster 检查 quorum
Sentinel 之间网络不通 SENTINEL sentinels mymaster 检查是否互相发现
Sentinel 时钟不同步 确认 NTP 服务正常
被监控的主库设置了密码但 Sentinel 没配置 sentinel auth-pass 检查

7.3 坑 3:故障转移后从库全部全量同步

场景:新主库选出来后,所有从库同时发起全量复制,新主库内存暴涨 OOM。

解决方案

bash 复制代码
sentinel parallel-syncs 1  # 一次只让 1 个从库同步,排队

7.4 坑 4:Sentinel 本身的单点问题

关键原则 :Sentinel 集群必须部署在不同的物理机或不同的故障域上。如果 3 个 Sentinel 全在同一台机器上,机器挂了全部不可用。

复制代码
✅ 正确部署:
Sentinel-1: 物理机A
Sentinel-2: 物理机B  
Sentinel-3: 物理机C

❌ 错误部署:
Sentinel-1~3 全在物理机A 上(或同一台虚拟机的 3 个容器中)

7.5 故障排查命令速查

bash 复制代码
# 查看 Sentinel 整体状态
SENTINEL master mymaster

# 关键字段解读
# flags=master                        ← 当前状态
# num-slaves=2                        ← 从库数量
# num-other-sentinels=2               ← 其他 Sentinel 数量
# quorum=2                            ← quorum 配置
# failover-timeout=180000
# parallel-syncs=1

# 查看主库的从库列表
SENTINEL slaves mymaster

# 查看所有 Sentinel 实例
SENTINEL sentinels mymaster

# 手动触发故障转移(测试用)
SENTINEL failover mymaster

# 查看当前纪元(epoch)
SENTINEL ckquorum mymaster

# 重置 Sentinel 状态(清理过时信息)
SENTINEL reset mymaster

八、总结

本文的核心知识图谱:
#mermaid-svg-rvIzFho6oPfXPteW{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-rvIzFho6oPfXPteW .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-rvIzFho6oPfXPteW .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-rvIzFho6oPfXPteW .error-icon{fill:#552222;}#mermaid-svg-rvIzFho6oPfXPteW .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-rvIzFho6oPfXPteW .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-rvIzFho6oPfXPteW .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-rvIzFho6oPfXPteW .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-rvIzFho6oPfXPteW .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-rvIzFho6oPfXPteW .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-rvIzFho6oPfXPteW .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-rvIzFho6oPfXPteW .marker{fill:#333333;stroke:#333333;}#mermaid-svg-rvIzFho6oPfXPteW .marker.cross{stroke:#333333;}#mermaid-svg-rvIzFho6oPfXPteW svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-rvIzFho6oPfXPteW p{margin:0;}#mermaid-svg-rvIzFho6oPfXPteW .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-rvIzFho6oPfXPteW .cluster-label text{fill:#333;}#mermaid-svg-rvIzFho6oPfXPteW .cluster-label span{color:#333;}#mermaid-svg-rvIzFho6oPfXPteW .cluster-label span p{background-color:transparent;}#mermaid-svg-rvIzFho6oPfXPteW .label text,#mermaid-svg-rvIzFho6oPfXPteW span{fill:#333;color:#333;}#mermaid-svg-rvIzFho6oPfXPteW .node rect,#mermaid-svg-rvIzFho6oPfXPteW .node circle,#mermaid-svg-rvIzFho6oPfXPteW .node ellipse,#mermaid-svg-rvIzFho6oPfXPteW .node polygon,#mermaid-svg-rvIzFho6oPfXPteW .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-rvIzFho6oPfXPteW .rough-node .label text,#mermaid-svg-rvIzFho6oPfXPteW .node .label text,#mermaid-svg-rvIzFho6oPfXPteW .image-shape .label,#mermaid-svg-rvIzFho6oPfXPteW .icon-shape .label{text-anchor:middle;}#mermaid-svg-rvIzFho6oPfXPteW .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-rvIzFho6oPfXPteW .rough-node .label,#mermaid-svg-rvIzFho6oPfXPteW .node .label,#mermaid-svg-rvIzFho6oPfXPteW .image-shape .label,#mermaid-svg-rvIzFho6oPfXPteW .icon-shape .label{text-align:center;}#mermaid-svg-rvIzFho6oPfXPteW .node.clickable{cursor:pointer;}#mermaid-svg-rvIzFho6oPfXPteW .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-rvIzFho6oPfXPteW .arrowheadPath{fill:#333333;}#mermaid-svg-rvIzFho6oPfXPteW .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-rvIzFho6oPfXPteW .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-rvIzFho6oPfXPteW .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-rvIzFho6oPfXPteW .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-rvIzFho6oPfXPteW .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-rvIzFho6oPfXPteW .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-rvIzFho6oPfXPteW .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-rvIzFho6oPfXPteW .cluster text{fill:#333;}#mermaid-svg-rvIzFho6oPfXPteW .cluster span{color:#333;}#mermaid-svg-rvIzFho6oPfXPteW div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-rvIzFho6oPfXPteW .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-rvIzFho6oPfXPteW rect.text{fill:none;stroke-width:0;}#mermaid-svg-rvIzFho6oPfXPteW .icon-shape,#mermaid-svg-rvIzFho6oPfXPteW .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-rvIzFho6oPfXPteW .icon-shape p,#mermaid-svg-rvIzFho6oPfXPteW .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-rvIzFho6oPfXPteW .icon-shape .label rect,#mermaid-svg-rvIzFho6oPfXPteW .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-rvIzFho6oPfXPteW .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-rvIzFho6oPfXPteW .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-rvIzFho6oPfXPteW :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Sentinel 高可用
监控层
判断层
执行层
PING 心跳(每秒)
INFO 拓扑发现(每10秒)
Pub/Sub 互相发现(每2秒)
SDOWN → 个人判断
ODOWN → 多数派共识(quorum)
Leader 选举 → Raft 简化版(majority)
选主:replica-priority > offset > runid
切换:SLAVEOF NO ONE
通知:+switch-master

三个必须记住的数字

参数 作用 常见错误
quorum ODOWN 判定所需票数 设太小→误判,设太大→判不了
down-after-milliseconds 心跳超时 设太小→网络抖动就触发故障转移
parallel-syncs 故障转移后并发同步数 设太大→新主库 OOM

如有疑问或指正,欢迎在评论区交流。

相关推荐
是小王同学啊~1 小时前
Redis 面试通关笔记:高频八股 + 生产实战 + 追问链路(下)
redis·面试题
唔661 小时前
(二)补充完整的数据库、中间件、MQTT、JAR后台和Web前端的部署脚本,全部一键自动化。
数据库·中间件·jar
六月雨滴1 小时前
Oracle 内存优化
数据库·oracle
学代码的真由酱1 小时前
MySQL数据库进阶-数据库设计实践-Java
数据库·mysql·数据库设计
遇事不決洛必達1 小时前
【数据库系列】本地映射云服务器Mysql的方法
服务器·数据库·mysql·定时任务
海鸥-w2 小时前
用python (fastapi)做项目第一天创建项目结构,数据建表,ORM配置安装,写第一个接口
数据库·python·fastapi
zfoo-framework2 小时前
通过redis-cli+lua脚本查询redis数据
数据库·redis·lua
岳读2 小时前
Redis Windows 7.2.3 使用教程解压启动与测试步骤
redis
牛栓柱2 小时前
【后端实战】用 Supabase + React/TS 零成本构建高并发 Multi-Agent 服务
前端·数据库·人工智能·后端·react.js·前端框架