哨兵模式概述
Redis Sentinel 是 Redis 的高可用性解决方案,它提供了监控、通知、自动故障转移和配置提供者等功能。当主服务器出现故障时,Sentinel 能够自动将某个从服务器升级为新的主服务器,并让其他从服务器改为复制新的主服务器。
哨兵模式架构
📱 Client
应用客户端
↓
🛡️ Sentinel 1
哨兵节点
🛡️ Sentinel 2
哨兵节点
🛡️ Sentinel 3
哨兵节点
↓
🔴 Master
主服务器
🟢 Slave 1
从服务器
🟢 Slave 2
从服务器
特点:自动监控、故障检测、自动故障转移、配置管理
🔧 哨兵配置
配置 Redis Sentinel 的详细步骤:
Redis 服务器配置:
主服务器配置 (redis-master.conf):
# 基本配置 port 6379 bind 0.0.0.0 # 设置密码 requirepass mypassword # 持久化配置 save 900 1 save 300 10 save 60 10000 appendonly yes # 复制配置 repl-backlog-size 1mb repl-backlog-ttl 3600 # 日志配置 logfile "/var/log/redis/redis-master.log" loglevel notice
从服务器配置 (redis-slave.conf):
# 基本配置 port 6379 bind 0.0.0.0 # 指定主服务器 replicaof 192.168.1.100 6379 masterauth mypassword # 从服务器密码 requirepass mypassword # 只读模式 replica-read-only yes # 持久化配置 appendonly yes # 日志配置 logfile "/var/log/redis/redis-slave.log" loglevel notice
Sentinel 配置:
哨兵配置文件 (sentinel.conf):
# 哨兵端口 port 26379 # 绑定地址 bind 0.0.0.0 # 监控主服务器 # sentinel monitorsentinel monitor mymaster 192.168.1.100 6379 2 # 主服务器密码 sentinel auth-pass mymaster mypassword # 故障转移超时时间(毫秒) sentinel down-after-milliseconds mymaster 30000 # 故障转移期间允许的并行同步从服务器数量 sentinel parallel-syncs mymaster 1 # 故障转移超时时间 sentinel failover-timeout mymaster 180000 # 通知脚本(可选) # sentinel notification-script mymaster /var/redis/notify.sh # 客户端重新配置脚本(可选) # sentinel client-reconfig-script mymaster /var/redis/reconfig.sh # 日志配置 logfile "/var/log/redis/sentinel.log" loglevel notice # 工作目录 dir /var/lib/redis # 拒绝危险命令 sentinel deny-scripts-reconfig yes
启动服务:
# 启动 Redis 主服务器 redis-server /etc/redis/redis-master.conf # 启动 Redis 从服务器 redis-server /etc/redis/redis-slave1.conf redis-server /etc/redis/redis-slave2.conf # 启动 Sentinel(至少3个节点) redis-sentinel /etc/redis/sentinel1.conf redis-sentinel /etc/redis/sentinel2.conf redis-sentinel /etc/redis/sentinel3.conf # 或者使用 redis-server 启动 redis-server /etc/redis/sentinel1.conf --sentinel redis-server /etc/redis/sentinel2.conf --sentinel redis-server /etc/redis/sentinel3.conf --sentinel
🔍 哨兵监控
监控哨兵系统的状态和健康度:
| 监控命令 | 描述 | 示例 |
|---|---|---|
SENTINEL masters |
显示所有被监控的主服务器 | redis-cli -p 26379 SENTINEL masters |
SENTINEL slaves <master-name> |
显示指定主服务器的从服务器 | SENTINEL slaves mymaster |
SENTINEL sentinels <master-name> |
显示监控指定主服务器的哨兵 | SENTINEL sentinels mymaster |
SENTINEL get-master-addr-by-name <master-name> |
获取主服务器地址 | SENTINEL get-master-addr-by-name mymaster |
SENTINEL failover <master-name> |
手动触发故障转移 | SENTINEL failover mymaster |
SENTINEL reset <pattern> |
重置哨兵状态 | SENTINEL reset mymaster |
监控脚本示例:
#!/bin/bash # Redis Sentinel 监控脚本 SENTINEL_HOST="127.0.0.1" SENTINEL_PORT="26379" MASTER_NAME="mymaster" LOG_FILE="/var/log/redis-sentinel-monitor.log" # 检查哨兵状态 sentinel_info=$(redis-cli -h $SENTINEL_HOST -p $SENTINEL_PORT INFO sentinel) echo "$(date): Sentinel info: $sentinel_info" >> $LOG_FILE # 获取主服务器信息 master_info=$(redis-cli -h $SENTINEL_HOST -p $SENTINEL_PORT SENTINEL masters) echo "$(date): Master info: $master_info" >> $LOG_FILE # 获取当前主服务器地址 master_addr=$(redis-cli -h $SENTINEL_HOST -p $SENTINEL_PORT SENTINEL get-master-addr-by-name $MASTER_NAME) echo "$(date): Current master: $master_addr" >> $LOG_FILE # 检查从服务器状态 slaves_info=$(redis-cli -h $SENTINEL_HOST -p $SENTINEL_PORT SENTINEL slaves $MASTER_NAME) echo "$(date): Slaves info: $slaves_info" >> $LOG_FILE # 检查哨兵节点 sentinels_info=$(redis-cli -h $SENTINEL_HOST -p $SENTINEL_PORT SENTINEL sentinels $MASTER_NAME) echo "$(date): Sentinels info: $sentinels_info" >> $LOG_FILE
🔄 故障转移流程
Sentinel 自动故障转移的详细流程:
1. 故障检测
主观下线检测
→
2. 客观下线
多数哨兵确认
→
3. 选举领导
选择领导哨兵
→
4. 选择新主
从从服务器中选择
→
5. 故障转移
执行主从切换
→
6. 通知客户端
更新配置信息
故障检测机制:
# 主观下线(Subjectively Down, SDOWN) # 单个哨兵认为主服务器已下线 # 条件:在指定时间内没有收到有效回复 # 客观下线(Objectively Down, ODOWN) # 多个哨兵都认为主服务器已下线 # 条件:达到 quorum 数量的哨兵确认主服务器下线 # 配置示例 sentinel down-after-milliseconds mymaster 30000 # 30秒无响应判定为主观下线 sentinel monitor mymaster 192.168.1.100 6379 2 # 需要2个哨兵确认客观下线
新主服务器选择规则:
💡 选择优先级:
- 排除故障节点:排除已下线、断线的从服务器
- 优先级排序:按
replica-priority值排序(值越小优先级越高) - 复制偏移量:选择复制偏移量最大的(数据最新)
- 运行ID:如果前面都相同,选择运行ID最小的
故障转移配置:
# 从服务器优先级配置 replica-priority 100 # 值越小优先级越高,0表示永不提升为主服务器 # 故障转移超时配置 sentinel failover-timeout mymaster 180000 # 3分钟 # 并行同步数量 sentinel parallel-syncs mymaster 1 # 故障转移时同时同步的从服务器数量
📱 客户端连接
应用程序如何连接到 Sentinel 管理的 Redis 集群:
Java 客户端示例(Jedis):
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisSentinelPool;
import java.util.HashSet;
import java.util.Set;
public class SentinelExample {
public static void main(String[] args) {
// 配置哨兵节点
Set sentinels = new HashSet<>();
sentinels.add("192.168.1.101:26379");
sentinels.add("192.168.1.102:26379");
sentinels.add("192.168.1.103:26379");
// 创建哨兵连接池
JedisSentinelPool sentinelPool = new JedisSentinelPool(
"mymaster", // 主服务器名称
sentinels, // 哨兵节点集合
"mypassword" // Redis密码
);
// 使用连接
try (Jedis jedis = sentinelPool.getResource()) {
jedis.set("key1", "value1");
String value = jedis.get("key1");
System.out.println("Value: " + value);
}
// 关闭连接池
sentinelPool.close();
}
}
Python 客户端示例(redis-py):
import redis.sentinel
# 配置哨兵
sentinel_list = [
('192.168.1.101', 26379),
('192.168.1.102', 26379),
('192.168.1.103', 26379)
]
# 创建哨兵对象
sentinel = redis.sentinel.Sentinel(
sentinel_list,
socket_timeout=0.1
)
# 获取主服务器连接
master = sentinel.master_for(
'mymaster',
socket_timeout=0.1,
password='mypassword',
decode_responses=True
)
# 获取从服务器连接(只读)
slave = sentinel.slave_for(
'mymaster',
socket_timeout=0.1,
password='mypassword',
decode_responses=True
)
# 使用连接
master.set('key1', 'value1')
value = slave.get('key1')
print(f'Value: {value}')
Spring Boot 配置:
# application.yml
spring:
redis:
password: mypassword
timeout: 2000ms
sentinel:
master: mymaster
nodes:
- 192.168.1.101:26379
- 192.168.1.102:26379
- 192.168.1.103:26379
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0
哨兵模式优缺点
| 方面 | 优点 | 缺点 |
|---|---|---|
| 高可用性 | 自动故障转移,无需人工干预 | 故障转移有短暂中断 |
| 监控能力 | 实时监控,及时发现问题 | 需要额外的哨兵节点 |
| 配置管理 | 自动更新客户端配置 | 配置相对复杂 |
| 扩展性 | 支持读写分离 | 写能力仍受限于单主 |
| 一致性 | 保证数据一致性 | 可能有短暂的数据丢失 |
🚨 故障排查
常见问题的诊断和解决方法:
常见问题:
⚠️ 脑裂问题:
现象:网络分区导致出现多个主服务器
解决:配置 min-slaves-to-write 和 min-slaves-max-lag
# 防止脑裂配置 min-slaves-to-write 1 min-slaves-max-lag 10
⚠️ 哨兵无法达成一致:
现象:哨兵节点数量不足或网络问题
解决:确保至少3个哨兵节点,且网络连通
⚠️ 故障转移失败:
现象:主服务器下线但未触发故障转移
排查:检查 quorum 配置、哨兵日志、网络连接
故障排查脚本:
#!/bin/bash
# Redis Sentinel 故障排查脚本
SENTINEL_HOSTS=("192.168.1.101" "192.168.1.102" "192.168.1.103")
SENTINEL_PORT="26379"
MASTER_NAME="mymaster"
echo "=== Redis Sentinel 健康检查 ==="
# 检查哨兵节点状态
for host in "${SENTINEL_HOSTS[@]}"; do
echo "检查哨兵节点: $host:$SENTINEL_PORT"
# 检查连接
if redis-cli -h $host -p $SENTINEL_PORT ping > /dev/null 2>&1; then
echo "✓ 连接正常"
# 检查主服务器信息
master_addr=$(redis-cli -h $host -p $SENTINEL_PORT SENTINEL get-master-addr-by-name $MASTER_NAME 2>/dev/null)
if [ $? -eq 0 ]; then
echo "✓ 主服务器地址: $master_addr"
else
echo "✗ 无法获取主服务器地址"
fi
# 检查哨兵数量
sentinel_count=$(redis-cli -h $host -p $SENTINEL_PORT SENTINEL sentinels $MASTER_NAME | grep -c "name")
echo "✓ 哨兵节点数量: $((sentinel_count + 1))"
else
echo "✗ 连接失败"
fi
echo "---"
done
# 检查主服务器状态
echo "检查主服务器状态:"
master_info=$(redis-cli -h ${SENTINEL_HOSTS[0]} -p $SENTINEL_PORT SENTINEL get-master-addr-by-name $MASTER_NAME 2>/dev/null)
if [ $? -eq 0 ]; then
master_host=$(echo $master_info | cut -d' ' -f1)
master_port=$(echo $master_info | cut -d' ' -f2)
if redis-cli -h $master_host -p $master_port ping > /dev/null 2>&1; then
echo "✓ 主服务器 $master_host:$master_port 正常"
else
echo "✗ 主服务器 $master_host:$master_port 异常"
fi
else
echo "✗ 无法获取主服务器信息"
fi
💡 最佳实践:
- 奇数个哨兵:部署奇数个哨兵节点(推荐3个或5个)
- 分布式部署:哨兵节点分布在不同的物理机器上
- 合理配置超时:根据网络环境调整超时参数
- 监控告警:监控哨兵状态,设置告警机制
- 定期演练:定期进行故障转移演练
- 日志分析:定期分析哨兵和Redis日志
- 网络优化:确保哨兵之间网络稳定