--- tags: [redis, cluster-sentinel, sharding, gossip, failover] create time: 2026-08-08 18:43 update time: 2026-08-08 18:43 --- # 集群与哨兵机制 ## 概述 Redis 单实例架构天然面临内存上限和单点故障问题。Redis Cluster(原生分片集群)解决了水平扩展,Sentinel(哨兵)解决了高可用自动故障转移。理解它们的架构、通信协议和选举机制,是构建生产级 Redis 基础设施的前提。 ## 核心原理 ### Sentinel — 哨兵架构 哨兵不是对数据进行分片的,而是在主从复制的基础上增加自动化故障检测和恢复能力。 **哨兵节点组成:** ```mermaid graph TB subgraph RedisCluster["Redis 主从集群"] M["Master
192.168.1.10:6379"] S1["Slave1
192.168.1.11:6379"] S2["Slave2
192.168.1.12:6379"] end subgraph Sentinels["哨兵集群"] SEN1["Sentinel-1
192.168.1.10:26379"] SEN2["Sentinel-2
192.168.1.11:26379"] SEN3["Sentinel-3
192.168.1.12:26379"] end subgraph Clients["客户端"] APP1["应用服务A"] APP2["应用服务B"] end M -->|replication| S1 M -->|replication| S2 SEN1 -->|监控| M SEN2 -->|监控| M SEN3 -->|监控| M SEN1 <-->|Gossip通信| SEN2 SEN2 <-->|Gossip通信| SEN3 SEN3 <-->|Gossip通信| SEN1 APP1 -->|连接| M APP2 -->|连接| M ``` **四大职责:** 1. **监控(Monitoring)**:定期检查 Master、Slave 和其他 Sentinel 是否可达(通过 PING)。 2. **提醒(Notification)**:当某个节点发现异常时,通知其他 Sentinel。 3. **自动故障转移(Failover)**:当 master 被标记为客观下线(ODOWN)时,选择一个 slave 升为主节点。 4. **配置提供者(Configuration Provider)**:客户端连接 sentinel 来获取当前 master 地址。 **故障转移步骤:** ```mermaid sequenceDiagram participant S1 as Sentinel-1 participant Quorum as Quorum(n/2+1) participant Master as 原Master participant Slave as Slave节点 S1->>Master: PING (超时判定主观下线) S1->>S1: 标记为 Subjectively Down (SDOWN) S1->>Quorum: 询问是否也认为 Master SDOWN Quorum-->>S1: n/2+1 确认 S1->>S1: 标记为 Objectively Down (ODOWN) S1->>S1: 选举 leader Sentinel Note over S1: RAFT-like 选举
先到先得 S1->>Quorum: 投票选出 Failover Owner S1->>Slave: 选出最合适的 slave (复制偏移量最大) Slave->>Slave: SLAVEOF NO ONE Slave->>Slave: 提升为 Master S1->>其他 Slave: SLAVEOF new-master IP PORT S1->>Clients: 更新 master 地址 ``` **关键参数:** | 参数 | 默认值 | 含义 | |------|-------|------| | down-after-milliseconds | 30000 | 主观下线的判断时间(ms) | | failover-timeout | 180000 | 故障转移超时(ms) | | parallel-syncs | 1 | 故障转移后同时同步的新 master 数量 | > [!NOTE] > quorum = n/2 + 1 中的 n 是配置的 Sentinel 节点总数,不是存活节点数。如果配置了 3 个 Sentinel,需要至少 2 个认为 master SDOWN 才会触发 ODOWN。这意味着少数派宕机不影响多数派的故障检测。 ### Redis Cluster — 槽位分配 Redis Cluster 是无中心架构,每个节点都知道完整的拓扑结构。数据分片基于 **哈希槽(hash slot)**。 **16384 个槽位:** - Redis Cluster 将 16384 个 hash slot 分布在多个节点上 - Key 的 slot 计算:`CRC16(key) % 16384` - 客户端可以通过 `ASK` 和 `MOVED` 重定向消息定位到正确节点 **槽位迁移流程(在线迁移):** ```mermaid sequenceDiagram participant Admin as 管理员 participant NodeA as Source Node participant NodeB as Target Node participant Client as 客户端 Admin->>NodeA: CLUSTER ADDSLOTS 0-5460 NodeA->>NodeB: 开始迁移 slots Note over NodeA,NodeB: 阶段1: IMPORTING NodeA->>NodeA: setslot IMPORTING NodeB->>NodeB: setslot MIGRATING loop 逐个 key 迁移 NodeA->>NodeB: MIGRATE host port key timeout Note over NodeA: client 查 key → ASK redirect →
再查到目标 node end NodeA->>NodeA: setslot NODE (迁移完成) Client->>NodeB: 直接查找 (no redirect needed) ``` ### Gossip 协议 哨兵之间使用简单的主子 Gossip 协议进行信息交换: - **PUBLISH/SUBSCRIBE**:哨兵可以发布订阅频道获取全局事件 - **HEARTBEAT**:每秒钟互相发送 PING,包含自身的版本号和当前 master 状态 - **INFO 传播**:每个哨兵维护整个集群的视图,定期与其他哨兵同步 - **领导选举**:基于 Raft 思想的简化版——先到先得,获得 quorum 票数成为 owner > [!WARNING] > Gossip 协议存在最终一致性延迟。在哨兵刚刚选出新 leader 的瞬间,未收到通知的哨兵可能仍认为旧状态是正确的,此时如果再次发生故障转移请求可能出现混乱。实际生产中要合理设置 timeouts。 ### CP vs AP 权衡 Redis Cluster 在设计上做出了明确的取舍: ```mermaid graph LR A["CAP 定理"] --> B{"一致性 or 可用性?"} B -->|Redis Cluster 选择| C["AP 倾向
(可用性优先)"] B -->|对比方案| D["Redis Sentinel CP 倾向"] C --> E["分片节点不可用时
部分操作返回 -CLUSTERDOWN"] D --> F["单主从切换期间
短暂不可用但最终一致"] E --> G["适合:缓存/排行榜等可接受短暂不一致场景"] F --> H["适合:会话存储/限流计数等要求强一致场景"] ``` | 维度 | Redis Sentinel | Redis Cluster | |------|---------------|---------------| | 一致性模型 | CP(主从切换期间短暂不可用但保证一致) | AP(分片后允许不同分区有不同视角) | | 数据分片 | 否(全量复制到每个从节点) | 是(16384 槽位分散) | | 多 DB 支持 | 支持(DB0 ~ DB15) | 不支持(仅 DB0) | | 扩容方式 | 手动迁移或重新搭建 | 在线增量迁移 | | 适用场景 | 中小规模、强一致性要求 | 大规模、水平扩展需求 | ## 代码示例 Go 中使用 Redigo 连接 Sentinel: ```go import "github.com/gomodule/redigo/redis" sentinelAddrs := []string{"192.168.1.10:26379", "192.168.1.11:26379"} pool := &redis.Pool{ MaxIdle: 10, DialContext: func(ctx context.Context) (conn redis.Conn, err error) { // 自动发现 master,无需硬编码地址 conn, err = redis.DialSentinel( "mymaster", // sentinel 网络名 sentinelAddrs, // sentinel 地址列表 "", "", // username/password ) return }, } defer pool.Close() ``` ```bash # Sentinel CLI 查看当前 master 信息 127.0.0.1:26379> SENTINEL get-master-addr-by-name mymaster 1) "192.168.1.10" 2) "6379" # 如果返回空数组,说明正在故障转移中 ``` ## 实践场景 1. **Sentinel 部署最佳实践**:至少 3 个(奇数),跨机房部署避免单机房断电导致全部失联。quorum 设为 n/2 + 1。 2. **客户端感知的 Sentinel 连接**:Java 的 Jedis / Lettuce 和 Go 的 redigo 都内置了 Sentinel 发现逻辑,配置好 master name 即可自动路由。不要把 master IP 写死在配置里。 3. **Cluster 扩缩容**:新增节点时先 `CLUSTER MEET` 加入集群,再通过 `CLUSTER ADDSLOT` 分配槽位,最后用 `redis-cli --cluster rebalance` 自动均衡。整个过程业务零停机。 4. **脑裂风险**:当网络和物理隔离导致两个"主"共存时,会产生数据分裂。可通过 `min-replicas-to-write` 和 `min-replicas-max-lag` 降低风险。 ## 关联笔记 - [[03.Redis/core/Redis 五大核心数据结构]] - [[03.Redis/core/RDB 与 AOF 持久化]] - [[03.Redis/strategies/多级缓存架构设计]]