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);
}
}
选举规则:
- 每个哨兵只能投一票
- 先收到投票请求的优先
- 获得半数以上票数当选
- 同一任期只选一次
四、从节点选择
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 | 客观下线 |
| 选举 | 领头哨兵选举 |
| 故障转移 | 自动切换主节点 |
| 通知 | 脚本告警 |
最佳实践:
- 部署 3 或 5 个哨兵
- 哨兵与 Redis 分离部署
- 配置合理的超时时间
- 配置告警通知
- 定期测试故障转移
掌握哨兵模式,构建高可用 Redis 架构!