Redis 脑裂深度解析:Sentinel 与 Cluster 机制、流程及对比

前言

在 Redis 的主从架构 + 高可用 部署中,网络分区是引发脑裂 (集群中出现多个主节点,导致双主写入、数据不一致)的常见元凶。

Redis Sentinel(哨兵)和 Redis Cluster(集群)为解决这一问题,都借鉴了 Raft 多数派共识思想,设计出「主观下线 → 客观下线 → 故障转移」的标准流程。但两者在节点角色、通信协议、投票逻辑等层面存在本质区别。

本文将结合流程图、原理分析、典型脑裂场景及防护方案,系统对比这两种高可用架构。


一、核心概念速览

  • 主观下线(PFAIL / SDOWN)

    单个节点因探测不到目标主节点,单方面将其标记为下线。该标记仅本地有效,不具备全局权威性。

  • 客观下线(FAIL / ODOWN)

    集群中超过 Quorum(多数派) 节点一致认为某主节点已下线,正式触发故障转移流程。

  • 脑裂

    网络分区导致集群分裂成多个子网,各自选出主节点,形成双主/多主局面。客户端向多个主节点写入数据,最终造成数据永久分裂。

  • Quorum(多数派)

    分布式系统中防止误判和脑裂的核心机制------只有半数以上节点达成一致,判定结果才生效。


二、Redis Sentinel 哨兵模式:下线与故障转移

2.1 架构角色

  • Sentinel 节点:独立的监控节点,不存储业务数据,负责探测、投票、选主和执行故障转移。
  • 数据节点 :Master + Slave,负责读写和数据复制,不参与任何投票

2.2 流程时序图

所有节点 Leader 从节点 原Master Sentinel-3 Sentinel-2 Sentinel-1 所有节点 Leader 从节点 原Master Sentinel-3 Sentinel-2 Sentinel-1 #mermaid-svg-gXIFhFRSt8OZbKMp{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-gXIFhFRSt8OZbKMp .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-gXIFhFRSt8OZbKMp .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-gXIFhFRSt8OZbKMp .error-icon{fill:#552222;}#mermaid-svg-gXIFhFRSt8OZbKMp .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-gXIFhFRSt8OZbKMp .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-gXIFhFRSt8OZbKMp .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-gXIFhFRSt8OZbKMp .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-gXIFhFRSt8OZbKMp .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-gXIFhFRSt8OZbKMp .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-gXIFhFRSt8OZbKMp .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-gXIFhFRSt8OZbKMp .marker{fill:#333333;stroke:#333333;}#mermaid-svg-gXIFhFRSt8OZbKMp .marker.cross{stroke:#333333;}#mermaid-svg-gXIFhFRSt8OZbKMp svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-gXIFhFRSt8OZbKMp p{margin:0;}#mermaid-svg-gXIFhFRSt8OZbKMp .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-gXIFhFRSt8OZbKMp text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-gXIFhFRSt8OZbKMp .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-gXIFhFRSt8OZbKMp .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-gXIFhFRSt8OZbKMp .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-gXIFhFRSt8OZbKMp .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-gXIFhFRSt8OZbKMp #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-gXIFhFRSt8OZbKMp .sequenceNumber{fill:white;}#mermaid-svg-gXIFhFRSt8OZbKMp #sequencenumber{fill:#333;}#mermaid-svg-gXIFhFRSt8OZbKMp #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-gXIFhFRSt8OZbKMp .messageText{fill:#333;stroke:none;}#mermaid-svg-gXIFhFRSt8OZbKMp .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-gXIFhFRSt8OZbKMp .labelText,#mermaid-svg-gXIFhFRSt8OZbKMp .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-gXIFhFRSt8OZbKMp .loopText,#mermaid-svg-gXIFhFRSt8OZbKMp .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-gXIFhFRSt8OZbKMp .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-gXIFhFRSt8OZbKMp .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-gXIFhFRSt8OZbKMp .noteText,#mermaid-svg-gXIFhFRSt8OZbKMp .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-gXIFhFRSt8OZbKMp .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-gXIFhFRSt8OZbKMp .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-gXIFhFRSt8OZbKMp .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-gXIFhFRSt8OZbKMp .actorPopupMenu{position:absolute;}#mermaid-svg-gXIFhFRSt8OZbKMp .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-gXIFhFRSt8OZbKMp .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-gXIFhFRSt8OZbKMp .actor-man circle,#mermaid-svg-gXIFhFRSt8OZbKMp line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-gXIFhFRSt8OZbKMp :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 网络分区:Sentinel集群与原Master断连 满足Quorum(多数派),达成共识 假设Sentinel-1成为Leader 心跳探测(Ping)心跳探测(Ping)心跳探测(Ping)标记原Master【主观下线 SDOWN】标记原Master【主观下线 SDOWN】同步下线状态同步下线状态同步下线状态同步下线状态选举Sentinel Leader选举Sentinel Leader选举Sentinel Leader全局标记【客观下线 ODOWN】全局标记【客观下线 ODOWN】全局标记【客观下线 ODOWN】发起从节点选举选出新Master执行故障转移,切换主从关系

2.3 详细流程拆解

  1. 心跳探测

    所有 Sentinel 节点持续向 Master 发送 Ping,检测其存活状态。

  2. 主观下线(SDOWN)

    若某个 Sentinel 超时未收到响应,就在本地将该 Master 标记为 SDOWN(仅自身认可)。

  3. 状态同步 + 多数派校验

    Sentinel 之间互相交换状态信息。当超过半数 Sentinel 都标记了 SDOWN,集群判定该 Master 为全局下线。

  4. 选举 Sentinel Leader

    从 Sentinel 集群中选出一个 Leader,全权负责后续的故障转移操作。

  5. 客观下线(ODOWN)

    Leader 正式将原 Master 标记为 ODOWN,该状态全网生效。

  6. 故障转移

    Leader 从所有从节点中选举新 Master,更新集群拓扑,其余从节点切换复制源至新 Master。

2.4 Sentinel 脑裂场景与防护

典型脑裂场景(以 5 个 Sentinel 为例)
  • 分区1 :3 个 Sentinel 与原 Master 断开 → 形成多数派,选举出新 Master
  • 分区2 :2 个 Sentinel + 原 Master 网络正常 → 原 Master 仍接受客户端写入。
    结果:集群出现双主,脑裂发生。
防护方案
  1. min-replicas-to-write + min-replicas-max-lag

    原 Master 在正常从节点数量不足或从节点复制延迟超时的情况下,直接拒绝所有写请求,从源头阻断脏数据写入。

  2. 纪元(Epoch)版本机制

    每次故障转移后全局纪元自增,新 Master 拥有更高纪元。网络恢复后,低纪元的旧 Master 会被自动降级为从节点。


三、Redis Cluster 集群模式:下线与故障转移

3.1 架构角色

  • 主节点:同时承担数据存储、投票、监控职责,参与状态判定与选举。
  • 从节点 :仅负责数据复制,无投票权
  • 通信协议 :基于 Gossip 协议 ,通过 Ping / Pong 在全网同步节点状态。

3.2 流程时序图

所有节点 全网节点 故障节点的从节点 故障主节点 主节点M3 主节点M2 主节点M1 所有节点 全网节点 故障节点的从节点 故障主节点 主节点M3 主节点M2 主节点M1 #mermaid-svg-AvuuSPYBi10jK5wz{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-AvuuSPYBi10jK5wz .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-AvuuSPYBi10jK5wz .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-AvuuSPYBi10jK5wz .error-icon{fill:#552222;}#mermaid-svg-AvuuSPYBi10jK5wz .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-AvuuSPYBi10jK5wz .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-AvuuSPYBi10jK5wz .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-AvuuSPYBi10jK5wz .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-AvuuSPYBi10jK5wz .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-AvuuSPYBi10jK5wz .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-AvuuSPYBi10jK5wz .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-AvuuSPYBi10jK5wz .marker{fill:#333333;stroke:#333333;}#mermaid-svg-AvuuSPYBi10jK5wz .marker.cross{stroke:#333333;}#mermaid-svg-AvuuSPYBi10jK5wz svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-AvuuSPYBi10jK5wz p{margin:0;}#mermaid-svg-AvuuSPYBi10jK5wz .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-AvuuSPYBi10jK5wz text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-AvuuSPYBi10jK5wz .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-AvuuSPYBi10jK5wz .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-AvuuSPYBi10jK5wz .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-AvuuSPYBi10jK5wz .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-AvuuSPYBi10jK5wz #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-AvuuSPYBi10jK5wz .sequenceNumber{fill:white;}#mermaid-svg-AvuuSPYBi10jK5wz #sequencenumber{fill:#333;}#mermaid-svg-AvuuSPYBi10jK5wz #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-AvuuSPYBi10jK5wz .messageText{fill:#333;stroke:none;}#mermaid-svg-AvuuSPYBi10jK5wz .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-AvuuSPYBi10jK5wz .labelText,#mermaid-svg-AvuuSPYBi10jK5wz .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-AvuuSPYBi10jK5wz .loopText,#mermaid-svg-AvuuSPYBi10jK5wz .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-AvuuSPYBi10jK5wz .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-AvuuSPYBi10jK5wz .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-AvuuSPYBi10jK5wz .noteText,#mermaid-svg-AvuuSPYBi10jK5wz .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-AvuuSPYBi10jK5wz .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-AvuuSPYBi10jK5wz .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-AvuuSPYBi10jK5wz .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-AvuuSPYBi10jK5wz .actorPopupMenu{position:absolute;}#mermaid-svg-AvuuSPYBi10jK5wz .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-AvuuSPYBi10jK5wz .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-AvuuSPYBi10jK5wz .actor-man circle,#mermaid-svg-AvuuSPYBi10jK5wz line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-AvuuSPYBi10jK5wz :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 网络中断,探测超时 半数以上主节点标记PFAIL,满足Quorum 其他从节点切换复制至新Master Gossip Ping探测Gossip Ping探测Gossip Ping探测标记故障节点【主观下线 PFAIL】标记故障节点【主观下线 PFAIL】Ping/Pong 夹带节点状态(Gossip同步)Ping/Pong 夹带节点状态(Gossip同步)Ping/Pong 夹带节点状态(Gossip同步)Ping/Pong 夹带节点状态(Gossip同步)广播FAIL消息,标记【客观下线 FAIL】广播FAIL消息,标记【客观下线 FAIL】广播FAIL消息,标记【客观下线 FAIL】从节点发起领导者选举升级为新Master,接管哈希槽

3.3 详细流程拆解

  1. Gossip 心跳探测

    主节点之间通过 Ping/Pong 持续通信,数据包中携带节点状态、槽位信息等。

  2. 主观下线(PFAIL)

    当某个主节点探测目标节点超时,本地标记为 PFAIL

  3. Gossip 状态扩散

    节点通过 Ping/Pong 将 PFAIL 状态传播到整个集群。

  4. 客观下线(FAIL)

    若集群中半数以上主节点 都标记了 PFAIL,则全网广播 FAIL 消息,该节点正式下线。

  5. 从节点选举与故障转移

    故障节点的从节点内部选举出新 Master,接管对应的哈希槽,其余从节点切换复制关系。

3.4 Cluster 脑裂场景与防护

脑裂场景

网络分区将集群拆成两个子网,两边的主节点各自满足「多数派」条件,分别完成故障转移,导致多个主节点并存,引发脑裂。

防护方案

与 Sentinel 通用:

  • 使用 min-replicas-to-write 限制主节点写入,在网络分区期间禁止旧主接收写命令。
  • 依赖集群版本 / 纪元校验,分区恢复后自动将旧主降级为从节点。

四、Sentinel 与 Cluster 核心异同对比

4.1 相同点(底层思想一致)

  • 都采用 「主观下线 → 客观下线」 二级判定,避免单点误判。
  • 核心依赖 Raft 多数派 Quorum 实现分布式共识,是防误判、防脑裂的基础。
  • 故障转移逻辑相同:下线原主 → 选举新主 → 切换拓扑。
  • 脑裂防护方案一致:从节点数量限制禁写 + 纪元版本降级

4.2 核心差异(架构与实现独立)

对比维度 Redis Sentinel 哨兵 Redis Cluster 集群
节点角色 哨兵节点(监控/投票) + 数据节点(读写),角色分离 主节点兼具「数据存储 + 投票监控」,无独立监控节点
投票主体 由 Sentinel 节点投票 仅集群主节点参与投票,从节点无投票权
通信协议 哨兵私有协议 Gossip 协议(Ping/Pong 同步状态)
下线标识 主观 SDOWN / 客观 ODOWN 主观 PFAIL / 客观 FAIL
故障影响范围 整套一主多从全部不可用 仅故障节点负责的哈希槽不可用,集群其余节点正常服务
适用架构 传统主从架构(一主多从) 分布式分片集群(数据按槽位分片)

五、总结

  1. 共识思想同源,实现不同

    Redis Sentinel 和 Cluster 底层都依赖多数派、二级下线机制实现高可用,脑裂成因与防护方案也高度一致。最大区别在于架构角色与通信模型:Sentinel 是独立监控集群,而 Cluster 是数据节点自监管控。

  2. 生产环境必配防护参数

    无论使用哪种架构,都务必开启 min-replicas-to-write 配置,从源头规避网络分区带来的脑裂数据错乱问题。

  3. 选型建议

    • 简单的主从复制 + 高可用场景,优先 Sentinel
    • 大规模数据分片、需要水平扩展的场景,使用 Cluster
相关推荐
woniu_buhui_fei12 小时前
Sentinel实现限流
微服务·sentinel
努力攻坚操作系统12 小时前
MySQL 原理解析
数据库·mysql
weixin_4074438712 小时前
基于Sentinel-1/2数据特征优选的冬小麦识别
人工智能·算法·随机森林·机器学习·sentinel
数据库小学妹12 小时前
MySQL 字符集深度解析:utf8 vs utf8mb4 的底层差异与索引失效根因
数据库·经验分享·mysql
Daydream.V12 小时前
深入拆解 MySQL 锁机制:全局锁、表级锁、行级锁实战全解析
数据库·mysql·oracle·
小辰记事本12 小时前
从零读懂RDMA硬件排障:读数、看码、查计数器
运维·网络·数据库
JZC_xiaozhong13 小时前
企业微信集成OA、ERP与第三方应用:从“数据孤岛”到“流程闭环”
大数据·数据库·企业微信·etl工程师·持续集成·企业数据安全·数据集成与应用集成
一 乐13 小时前
个人博客系统|基于Springboot的个人博客系统设计与实现(源码+数据库+文档)
java·数据库·spring boot·后端·论文·毕设·个人博客系统
光泽雨13 小时前
SqlDataAdapter.Fill(dt) 和SqlDataReader + dt.Load()的差异
数据库