Skip to content
清晨的一缕阳光
返回

Redis 哨兵模式详解

Redis 哨兵模式详解

哨兵(Sentinel)是 Redis 的高可用解决方案,提供监控、通知、自动故障转移等功能。本文将深入哨兵机制,帮助你构建高可用的 Redis 架构。

一、哨兵模式概述

1.1 什么是哨兵

哨兵是 Redis 的独立进程,用于:

┌─────────────┐
│   Master    │
│  192.168.1.1│
└──────┬──────┘

   ┌───┴───┐
   ▼       ▼
┌─────┐ ┌─────┐
│S1   │ │S2   │  ← 哨兵集群
│Sentinel│ │Sentinel│
└─────┘ └─────┘

1.2 哨兵特性

特性说明
分布式多个哨兵进程独立运行
去中心化无主节点,平等协作
最终一致性通过 Gossip 协议同步状态
高可用容忍部分哨兵故障

二、哨兵部署

2.1 部署架构

推荐配置:
- 至少 3 个哨兵节点
- 哨兵部署在不同机器
- 哨兵与 Redis 节点分离

┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│  Master     │     │  Slave 1    │     │  Slave 2    │
│ 192.168.1.1 │     │ 192.168.1.2 │     │ 192.168.1.3 │
└─────────────┘     └─────────────┘     └─────────────┘
       ▲                   ▲                   ▲
       │                   │                   │
   ┌───┴──────┬────────────┴──────┬────────────┴───┐
   │          │                   │                │
┌─────┐  ┌─────┐            ┌─────┐         ┌─────┐
│ S1  │  │ S2  │            │ S3  │         │ S4  │
│192.168│ │192.168│            │192.168│         │192.168│
│.2.1  │  │.2.2  │            │.2.3  │         │.2.4  │
└─────┘  └─────┘            └─────┘         └─────┘

2.2 哨兵配置

# sentinel.conf
port 26379
daemonize yes
pidfile /var/run/redis-sentinel.pid
logfile /var/log/redis/sentinel.log

# 监控配置
sentinel monitor mymaster 192.168.1.1 6379 2

# 密码配置
sentinel auth-pass mymaster your_password

# 超时配置
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 60000
sentinel parallel-syncs mymaster 1

# 通知脚本
sentinel notification-script mymaster /var/redis/notify.sh

2.3 配置参数详解

# 监控主节点
# sentinel monitor <master-name> <ip> <port> <quorum>
sentinel monitor mymaster 192.168.1.1 6379 2
# quorum = 2 表示至少 2 个哨兵认为主节点故障才触发故障转移

# 主观下线时间
sentinel down-after-milliseconds mymaster 5000
# 5000ms 无响应则标记为主观下线

# 故障转移超时
sentinel failover-timeout mymaster 60000
# 故障转移超时时间 60 秒

# 并行同步数
sentinel parallel-syncs mymaster 1
# 故障转移后,同时同步数据的从节点数

三、哨兵工作原理

3.1 监控机制

哨兵                     主节点
  │                          │
  │────── PING ───────────▶│  每秒发送
  │                          │
  │◀───── PONG ────────────│  回复
  │                          │
  │──── INFO ─────────────▶│  获取信息
  │◀──── 主从信息 ─────────│
  │                          │
  │──── SLAVEOF ──────────▶│  获取从节点列表

监控内容

// 哨兵监控信息
typedef struct sentinelRedisInstance {
    char *name;              // 实例名称
    char *ip;                // IP 地址
    int port;                // 端口
    int flags;               // 标志
    long long down_after_period;  // 下线时间
    
    // 主从关系
    struct sentinelRedisInstance *master;
    dict *slaves;
    
    // 哨兵信息
    dict *sentinels;
} sentinelRedisInstance;

3.2 主观下线与客观下线

主观下线(SDOWN)

单个哨兵认为实例故障

触发条件:
- down-after-milliseconds 时间内无响应
- 仅表示该哨兵的观点

客观下线(ODOWN)

多个哨兵达成共识认为实例故障

触发条件:
- 达到 quorum 数量的哨兵标记 SDOWN
- 通过 Gossip 协议同步状态
- 标记为 ODOWN 后触发故障转移

流程

哨兵 A                    哨兵 B                    哨兵 C
  │                        │                        │
  │ 检测主节点无响应        │                        │
  │                        │                        │
  │ 标记 SDOWN             │                        │
  │                        │                        │
  │──── IS-MASTER-DOWN ──▶│                        │
  │                        │                        │
  │◀──── +SDOWN ──────────│                        │
  │                        │                        │
  │──────── IS-MASTER-DOWN ──────────────────────▶│
  │                        │                        │
  │◀──────── +SDOWN ──────────────────────────────│
  │                        │                        │
  │ 达到 quorum=2          │                        │
  │ 标记 ODOWN             │                        │
  │                        │                        │
  │ 触发故障转移           │                        │

3.3 故障转移流程

1. 主节点故障

2. 哨兵检测到 SDOWN

3. 哨兵间通信确认 ODOWN

4. 选举领头哨兵

5. 领头哨兵执行故障转移

6. 选择新主节点

7. 通知其他从节点切换

8. 更新配置

详细流程

┌─────────┐  ┌─────────┐  ┌─────────┐
│ Sentinel│  │ Sentinel│  │ Sentinel│
│    A    │  │    B    │  │    C    │
└────┬────┘  └────┬────┘  └────┬────┘
     │            │            │
     │ 1. 检测故障 │            │
     │            │            │
     │──── 2. 发送 IS-MASTER-DOWN ────▶
     │            │            │
     │◀─── 3. 确认 SDOWN ────────│
     │            │            │
     │ 4. 达到 quorum           │
     │ 标记 ODOWN               │
     │            │            │
     │──── 5. 选举领头哨兵 ────▶
     │            │            │
     │ Sentinel A 当选          │
     │            │            │
     │ 6. 选择新主节点          │
     │ (优先级最高、offset 最大)  │
     │            │            │
     │ 7. SLAVEOF NO ONE        │
     │ 新主节点                 │
     │            │            │
     │ 8. 通知其他从节点        │
     │ SLAVEOF new_master       │
     │            │            │
     │ 9. 更新配置              │

3.4 领头哨兵选举

// 选举算法
void sentinelLeaderElection() {
    // 1. 每个哨兵生成随机运行 ID
    char *my_runid = server.runid;
    
    // 2. 发送选举请求
    SENTINEL VOTE <master_name> <candidate_runid>;
    
    // 3. 收集投票
    // 4. 获得半数以上票数的哨兵成为领头哨兵
    
    // 5. 领头哨兵执行故障转移
    if (I am the leader) {
        sentinelFailover(master);
    }
}

选举规则

  1. 每个哨兵只能投一票
  2. 先收到投票请求的优先
  3. 获得半数以上票数当选
  4. 同一任期只选一次

四、从节点选择

4.1 选择标准

// 从节点筛选
sentinelRedisInstance *sentinelSelectSlave() {
    // 1. 过滤不符合条件的从节点
    for each slave {
        if (slave->flags & S_DOWN) continue;      // 主观下线
        if (slave->flags & O_DOWN) continue;      // 客观下线
        if (slave->disconnected) continue;        // 连接断开
        if (now - slave->last_ping_time > timeout) continue;
    }
    
    // 2. 按优先级排序
    sort by slave->priority (越小优先级越高);
    
    // 3. 优先级相同,按 offset 排序
    sort by slave->repl_offset (越大越好);
    
    // 4. 都相同,按 runid 排序
    sort by slave->runid (字典序);
    
    return best_slave;
}

4.2 优先级配置

# 从节点配置
replica-priority 100

# 优先级规则
# 值越小优先级越高
# 0 表示永远不会被选为主节点

五、客户端连接

5.1 获取主节点

# 哨兵命令
SENTINEL masters                    # 获取所有主节点
SENTINEL master mymaster            # 获取指定主节点
SENTINEL slaves mymaster            # 获取从节点列表
SENTINEL sentinels mymaster         # 获取哨兵列表
SENTINEL get-master-addr-by-name mymaster  # 获取主节点地址

5.2 客户端集成

Java(Jedis)

Set<String> sentinels = new HashSet<>();
sentinels.add("192.168.2.1:26379");
sentinels.add("192.168.2.2:26379");
sentinels.add("192.168.2.3:26379");

JedisSentinelPool pool = new JedisSentinelPool(
    "mymaster",
    sentinels,
    poolConfig,
    password
);

// 自动获取主节点
Jedis jedis = pool.getResource();
jedis.set("key", "value");

Python(redis-py)

from redis.sentinel import Sentinel

sentinel = Sentinel(
    [('192.168.2.1', 26379),
     ('192.168.2.2', 26379)],
    socket_timeout=0.1
)

# 获取主节点
master = sentinel.master_for('mymaster', password='your_password')
master.set('key', 'value')

# 获取从节点
slave = sentinel.slave_for('mymaster', password='your_password')
value = slave.get('key')

六、配置管理

6.1 配置持久化

# 哨兵配置会自动更新
# 故障转移后,sentinel.conf 会自动修改

# 示例(故障转移后)
sentinel monitor mymaster 192.168.1.2 6379 2
# 主节点 IP 从 1.1 变为 1.2

# 配置重写
SENTINEL FLUSHCONFIG

6.2 动态配置

# 在线修改配置
SENTINEL SET mymaster down-after-milliseconds 10000
SENTINEL SET mymaster failover-timeout 120000
SENTINEL SET mymaster parallel-syncs 2

# 查看配置
SENTINEL GET-MASTER-ADDR-BY-NAME mymaster
SENTINEL MASTER mymaster

七、监控与告警

7.1 监控指标

# 哨兵状态
INFO sentinel

# 输出示例
# sentinel
# sentinel_masters:1
# sentinel_tilt:0
# sentinel_running_scripts:0
# sentinel_scripts_queue_length:0
# sentinel_simulate_failure_flags:0
# master0:name=mymaster,status=ok,address=192.168.1.1:6379,slaves=2,sentinels=3

7.2 告警通知

# 配置通知脚本
sentinel notification-script mymaster /var/redis/notify.sh

# 通知脚本示例
#!/bin/bash
echo "Alert: $1 $2 $3 $4 $5 $6 $7" >> /var/log/redis/sentinel-alerts.log

# 参数
# $1: master-name
# $2: subject (down, sdown, odown, failover)
# $3: type (master, slave, sentinel)
# $4: address
# $5: port
# $6: extra info

八、最佳实践

8.1 部署建议

推荐配置:
- 3 或 5 个哨兵节点(奇数)
- 哨兵部署在不同机器
- 哨兵与 Redis 节点分离
- 跨机房部署(容灾)

8.2 配置优化

# 生产环境配置
sentinel monitor mymaster 192.168.1.1 6379 2

# 主观下线时间
sentinel down-after-milliseconds mymaster 5000

# 故障转移超时
sentinel failover-timeout mymaster 60000

# 并行同步数
sentinel parallel-syncs mymaster 1

# 通知脚本
sentinel notification-script mymaster /opt/scripts/notify.sh

8.3 故障转移测试

# 手动触发故障转移
SENTINEL FAILOVER mymaster

# 模拟主节点故障
redis-cli -h 192.168.1.1 SHUTDOWN

# 观察故障转移过程
tail -f /var/log/redis/sentinel.log

九、常见问题

Q1: 哨兵数量多少合适?

推荐:3 或 5 个
- 奇数个,避免脑裂
- 容忍 (N-1)/2 个哨兵故障
- 3 个哨兵可容忍 1 个故障
- 5 个哨兵可容忍 2 个故障

Q2: 脑裂问题?

网络分区导致:
- 部分哨兵认为主节点故障
- 部分哨兵认为主节点正常

解决方案:
- 配置合理的 quorum
- 设置 min-slaves-to-write
- 使用网络分区检测

Q3: 故障转移失败?

# 检查日志
tail -f /var/log/redis/sentinel.log

# 常见原因
# 1. quorum 设置过高
# 2. 网络不通
# 3. 从节点不可用
# 4. 配置权限问题

总结

Redis 哨兵核心要点:

机制说明
监控每秒 PING 检测
SDOWN主观下线
ODOWN客观下线
选举领头哨兵选举
故障转移自动切换主节点
通知脚本告警

最佳实践

  1. 部署 3 或 5 个哨兵
  2. 哨兵与 Redis 分离部署
  3. 配置合理的超时时间
  4. 配置告警通知
  5. 定期测试故障转移

掌握哨兵模式,构建高可用 Redis 架构!

参考资料


分享这篇文章到:

上一篇文章
RocketMQ 消息设计最佳实践与案例
下一篇文章
以日为鉴:少子化浪潮下的教师过剩危机与破局之道