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
通知管理员/客户端
节点状态变化
- 监控(Monitoring)
定期 PING 主库和从库
检查存活状态
Sentinel 集群
Sentinel-1
Sentinel-2
Sentinel-3
1.3 Sentinel 本身的特点
| 特性 | 说明 |
|---|---|
| 独立进程 | Sentinel 是独立于 Redis 的进程(redis-sentinel 或 redis-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 节点发现机制
- 配置文件指定主库地址
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开始执行故障转移
选举规则:
- 每个 Sentinel 发现 ODOWN 后,会等待一段时间(随机优先级 + 延迟),然后向其他 Sentinel 请求投票
- 每个 Sentinel 在每个纪元(epoch)中只能投一票,先到先得
- 获得 ≥ majority(N/2 + 1) 票的 Sentinel 成为 Leader
- 如果没有人获得多数票,等待 2 倍故障转移超时后重新选举
注意 :这里的 majority 和 quorum 可能不同。假设 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 |
如有疑问或指正,欢迎在评论区交流。