文章目录
- [Redis 哨兵(Sentinel)完全指南:从原理到实战](#Redis 哨兵(Sentinel)完全指南:从原理到实战)
-
- [一、什么是 Redis 哨兵](#一、什么是 Redis 哨兵)
- 二、哨兵的核心功能
-
- [2.1 监控(Monitoring)](#2.1 监控(Monitoring))
- [2.2 通知(Notification)](#2.2 通知(Notification))
- [2.3 自动故障转移(Automatic Failover)](#2.3 自动故障转移(Automatic Failover))
- [2.4 配置中心(Configuration Provider)](#2.4 配置中心(Configuration Provider))
- 三、哨兵工作流程详解
-
- [3.1 流程总览](#3.1 流程总览)
- [3.2 第一步:监控阶段](#3.2 第一步:监控阶段)
- [3.3 第二步:主观下线(SDOWN)](#3.3 第二步:主观下线(SDOWN))
- [3.4 第三步:客观下线(ODOWN)](#3.4 第三步:客观下线(ODOWN))
- [3.5 第四步:选举 Leader 哨兵](#3.5 第四步:选举 Leader 哨兵)
- [3.6 第五步:故障转移](#3.6 第五步:故障转移)
- 四、核心概念深入理解
- 五、客户端接入方案
-
- [5.1 传统方式的问题](#5.1 传统方式的问题)
- [5.2 哨兵模式接入(推荐)](#5.2 哨兵模式接入(推荐))
-
- [Java 示例(Jedis)](#Java 示例(Jedis))
- [Spring Boot 配置](#Spring Boot 配置)
- [5.3 客户端工作流程](#5.3 客户端工作流程)
- [5.4 读写分离实现](#5.4 读写分离实现)
- 六、生产环境配置
-
- [6.1 哨兵配置文件(sentinel.conf)](#6.1 哨兵配置文件(sentinel.conf))
- [6.2 主节点配置(redis.conf)](#6.2 主节点配置(redis.conf))
- [6.3 从节点配置(redis.conf)](#6.3 从节点配置(redis.conf))
- [6.4 部署架构示例](#6.4 部署架构示例)
- [6.5 启动步骤](#6.5 启动步骤)
- 七、常见问题与解决方案
-
- [7.1 脑裂问题](#7.1 脑裂问题)
- [7.2 数据丢失问题](#7.2 数据丢失问题)
- [7.3 哨兵配置错误](#7.3 哨兵配置错误)
-
- [错误1:quorum 设置不当](#错误1:quorum 设置不当)
- [错误2:哨兵与 Redis 部署在同一台服务器](#错误2:哨兵与 Redis 部署在同一台服务器)
- [错误3:down-after-milliseconds 设置过短](#错误3:down-after-milliseconds 设置过短)
- [7.4 性能问题](#7.4 性能问题)
- 八、面试高频问题
-
- [8.1 基础问题](#8.1 基础问题)
-
- [Q1:什么是 Redis 哨兵?作用是什么?](#Q1:什么是 Redis 哨兵?作用是什么?)
- Q2:主观下线和客观下线的区别?
- Q3:哨兵集群需要多少个节点?为什么要奇数个?
- [8.2 进阶问题](#8.2 进阶问题)
-
- Q4:如何选择新的主节点?
- [Q5:什么是 quorum?如何设置?](#Q5:什么是 quorum?如何设置?)
- Q6:哨兵之间如何自动发现?
- [Q7:客户端如何连接哨兵模式的 Redis?](#Q7:客户端如何连接哨兵模式的 Redis?)
- [8.3 高级问题](#8.3 高级问题)
-
- Q8:什么是脑裂?如何解决?
- Q9:哨兵模式的缺点是什么?
- [Q10:哨兵和 Redis Cluster 的区别?](#Q10:哨兵和 Redis Cluster 的区别?)
- 九、实战项目中的应用
- 十、总结
-
- [10.1 核心要点](#10.1 核心要点)
- [10.2 快速记忆口诀](#10.2 快速记忆口诀)
Redis 哨兵(Sentinel)完全指南:从原理到实战
一、什么是 Redis 哨兵
1.1 定义
Redis Sentinel 是 Redis 官方提供的高可用解决方案,用于监控 Redis 主从架构,实现自动故障转移。
1.2 为什么需要哨兵?
传统主从架构的问题
正常情况:
text
客户端
↓
主节点(Master) → 处理写操作 ✅
↓
从节点(Slave) → 处理读操作
主节点故障:
text
客户端
↓
主节点(宕机) → 无法处理 ❌
痛点:
- 主节点宕机后,需要手动将从节点提升为主节点
- 需要手动修改客户端配置,指向新主节点
- 服务中断时间长,影响业务
哨兵模式的优势
text
主节点故障:
哨兵1:主节点无响应!
哨兵2:确认主节点下线!
哨兵3:同意故障转移!
自动故障转移:
从节点A → 自动提升为新主节点
从节点B → 自动切换为新主节点的从节点
客户端 → 自动连接新主节点
✅ 全程自动化,业务几乎无感知
二、哨兵的核心功能
2.1 监控(Monitoring)
text
哨兵每隔 1 秒:
- 向主节点发送 PING 命令
- 向从节点发送 PING 命令
- 向其他哨兵发送 PING 命令
- 检查各节点的运行状态
2.2 通知(Notification)
text
当监控的 Redis 实例出现问题:
- 通过 API 发送通知
- 发送告警消息给管理员
- 记录日志便于排查
2.3 自动故障转移(Automatic Failover)
text
主节点故障时:
1. 选择一个从节点提升为主节点
2. 让其他从节点复制新主节点
3. 通知客户端主节点已更换
4. 将旧主节点标记为从节点
2.4 配置中心(Configuration Provider)
text
客户端连接流程:
客户端 → 哨兵:"谁是当前主节点?"
哨兵 → 客户端:"192.168.1.100:6379"
客户端 → 连接主节点进行读写操作
三、哨兵工作流程详解
3.1 流程总览
text
监控阶段
↓
主节点无响应(超时)
↓
主观下线(SDOWN)
↓
询问其他哨兵
↓
达到 quorum?
↓
客观下线(ODOWN)
↓
选举 Leader 哨兵
↓
执行故障转移
↓
通知客户端
3.2 第一步:监控阶段
text
时间线:每 1 秒执行一次
哨兵1:PING 主节点 → 收到 PONG ✅
哨兵2:PING 主节点 → 收到 PONG ✅
哨兵3:PING 主节点 → 收到 PONG ✅
结论:主节点健康,继续监控
监控内容:
- 主节点状态
- 从节点状态
- 其他哨兵状态
- 主从复制偏移量
3.3 第二步:主观下线(SDOWN)
text
某一时刻,主节点网络故障...
哨兵1 的判断过程:
第 1 秒:PING 主节点 → 无响应
第 2 秒:PING 主节点 → 无响应
第 3 秒:PING 主节点 → 无响应
...
第 30 秒:PING 主节点 → 仍无响应
判断:超过 down-after-milliseconds(30秒)
结论:主节点进入主观下线状态(SDOWN)
关键配置:
bash
# 30秒无响应判定为主观下线
sentinel down-after-milliseconds mymaster 30000
为什么叫"主观"?
- 仅代表单个哨兵的判断
- 可能是哨兵自身网络问题导致的误判
- 需要其他哨兵确认
3.4 第三步:客观下线(ODOWN)
text
哨兵1 发现主观下线后,开始询问其他哨兵:
哨兵1 → 哨兵2:"你能连上主节点吗?"
哨兵2 → 哨兵1:"不能,我也认为它下线了"
哨兵1 → 哨兵3:"你能连上主节点吗?"
哨兵3 → 哨兵1:"不能,我也认为它下线了"
统计结果:
- 哨兵总数:3
- 认为下线的哨兵数:3
- quorum 配置:2
判断:3 >= 2,达到法定人数
结论:主节点客观下线(ODOWN)
关键配置:
bash
# quorum = 2,至少2个哨兵同意才算客观下线
sentinel monitor mymaster 192.168.1.100 6379 2
3.5 第四步:选举 Leader 哨兵
text
问题:3个哨兵都知道主节点挂了,但谁来执行故障转移?
答案:使用 Raft 算法选举一个 Leader
选举过程:
哨兵1:我要当Leader!请投票给我!
哨兵2:我投给哨兵1
哨兵3:我投给哨兵1
结果:哨兵1 获得多数票 → 成为 Leader
选举原则:
- 需要获得超过半数哨兵的投票
- 先到先得(先发起投票的优先)
- 每个哨兵在一轮选举中只能投一票
3.6 第五步:故障转移
text
Leader 哨兵(哨兵1)执行故障转移:
步骤1:从从节点中选择新主节点
候选者:
- 从节点A:优先级100,偏移量1000,ID=aaa
- 从节点B:优先级100,偏移量999,ID=bbb
选择:从节点A(偏移量最大,数据最新)
步骤2:将从节点A提升为主节点
哨兵1 → 从节点A:"SLAVEOF NO ONE"
从节点A:"我现在是主节点了!"
步骤3:让其他从节点复制新主节点
哨兵1 → 从节点B:"SLAVEOF 从节点A的IP端口"
从节点B:"开始复制新主节点"
步骤4:通知客户端
哨兵1 → 发布消息:"+switch-master mymaster 新IP 新端口"
客户端 → 接收通知并重新连接
步骤5:处理旧主节点
旧主节点恢复时 → 自动降级为从节点
开始复制新主节点的数据
四、核心概念深入理解
4.1 quorum(法定人数)
定义
判定主节点客观下线所需的最少哨兵同意数量。
配置示例
bash
sentinel monitor mymaster 192.168.1.100 6379 2
# ↑主节点名称 ↑IP地址 ↑端口 ↑quorum
设置原则
| 哨兵总数 | 推荐 quorum | 容错能力 |
|---|---|---|
| 3 | 2 | 允许1个哨兵故障 |
| 5 | 3 | 允许2个哨兵故障 |
| 7 | 4 | 允许3个哨兵故障 |
计算公式:
text
quorum = 哨兵总数 / 2 + 1
场景分析
quorum 设置过小(= 1):
text
风险:误判率高
哨兵1 网络抖动 → 误以为主节点挂了
立即触发故障转移 → 不必要的切换 ❌
quorum 设置过大(= 3):
text
风险:可用性降低
3个哨兵,quorum=3
如果哨兵3故障 → 只剩2个哨兵
永远无法达到quorum → 无法故障转移 ❌
quorum 合理设置(= 2):
text
平衡误判和可用性
3个哨兵,quorum=2
哨兵1误判 → 询问哨兵2、3 → 不触发转移 ✅
主节点真挂 → 2个哨兵确认 → 正常转移 ✅
哨兵3故障 → 剩余2个哨兵仍可工作 ✅
4.2 为什么要奇数个哨兵?
容错能力对比
text
3个哨兵(quorum=2):
正常运行:3个哨兵
挂1个后:剩2个 → 2 >= 2 → 可以故障转移 ✅
挂2个后:剩1个 → 1 < 2 → 无法故障转移 ❌
容错能力:最多允许1个故障
4个哨兵(quorum=3):
正常运行:4个哨兵
挂1个后:剩3个 → 3 >= 3 → 可以故障转移 ✅
挂2个后:剩2个 → 2 < 3 → 无法故障转移 ❌
容错能力:最多允许1个故障
结论:4个哨兵比3个哨兵多1台服务器,但容错能力相同!
成本对比表
| 哨兵数量 | 服务器成本 | 容错能力 | 性价比 |
|---|---|---|---|
| 2 | 低 | 0(不推荐) | ❌ |
| 3 | 中 | 1 | ✅ 推荐 |
| 4 | 中高 | 1 | ❌ 浪费 |
| 5 | 高 | 2 | ✅ 推荐 |
| 6 | 高 | 2 | ❌ 浪费 |
| 7 | 很高 | 3 | ✅ 大规模 |
最佳实践:
- ✅ 小型项目:3个哨兵
- ✅ 中型项目:5个哨兵
- ✅ 大型项目:7个哨兵
- ❌ 避免使用偶数个哨兵
4.3 如何选择新主节点?
选择流程
text
假设主节点故障,有3个从节点候选:
从节点A:
- 优先级(replica-priority):50
- 复制偏移量(offset):1000
- runid:aaa-bbb-ccc
从节点B:
- 优先级:100
- 复制偏移量:999
- runid:ddd-eee-fff
从节点C:
- 优先级:100
- 复制偏移量:999
- runid:ggg-hhh-iii
选择规则(按优先级)
第1步:排除不健康的从节点
text
排除以下从节点:
- 主观下线状态的
- 断线状态的
- 最近5秒内没有回复INFO命令的
- 与主节点断开连接超过 down-after-milliseconds * 10 的
第2步:比较优先级(replica-priority)
text
从节点A:50 ← 优先级最高(数字越小优先级越高)✅
从节点B:100
从节点C:100
选择:从节点A
第3步:比较复制偏移量(如果优先级相同)
text
从节点B:999
从节点C:999 ← 偏移量相同,继续下一步
复制偏移量越大 = 数据越新 = 优先选择
第4步:比较 runid(如果偏移量也相同)
text
从节点B:ddd-eee-fff ← 字典序更小 ✅
从节点C:ggg-hhh-iii
选择:从节点B
配置优先级
bash
# 在从节点的 redis.conf 中配置
replica-priority 100 # 默认值,数字越小优先级越高
# 示例:设置某个从节点永不提升为主节点
replica-priority 0 # 0 = 永不提升
4.4 哨兵之间如何发现彼此?
发现机制:发布订阅
text
步骤1:哨兵向主节点发布自己的信息
哨兵1 → 主节点的频道 __sentinel__:hello:
"192.168.1.10,26379,哨兵1的runid,当前epoch,..."
步骤2:其他哨兵订阅该频道
哨兵2、哨兵3 → 订阅 __sentinel__:hello 频道
步骤3:接收其他哨兵的信息
哨兵2 收到:"192.168.1.10,26379,哨兵1的runid,..."
哨兵2:"发现了哨兵1,建立连接"
步骤4:互相建立连接
哨兵1 ←→ 哨兵2
哨兵1 ←→ 哨兵3
哨兵2 ←→ 哨兵3
发现从节点
text
哨兵通过 INFO 命令获取主节点信息:
哨兵1 → 主节点:"INFO replication"
主节点 → 哨兵1:
"slave0: ip=192.168.1.101,port=6379,..."
"slave1:ip=192.168.1.102,port=6379,..."
哨兵1:"发现了2个从节点,开始监控"
五、客户端接入方案
5.1 传统方式的问题
java
// 直接连接主节点 IP
Jedis jedis = new Jedis("192.168.1.100", 6379);
jedis.set("name", "张三");
// 问题:主节点故障后,IP地址失效 ❌
// 需要手动修改代码,重新部署
5.2 哨兵模式接入(推荐)
Java 示例(Jedis)
java
import redis.clients.jedis.JedisSentinelPool;
import redis.clients.jedis.Jedis;
import java.util.HashSet;
import java.util. Set;
public class RedisSentinelExample {
public static void main(String[] args) {
// 配置哨兵地址(不是主节点地址!)
Set<String> sentinels = new HashSet<>();
sentinels.add("192.168.1.10:26379"); // 哨兵1
sentinels.add("192.168.1.11:26379"); // 哨兵2
sentinels.add("192.168.1.12:26379"); // 哨兵3
// 创建哨兵连接池
JedisSentinelPool pool = new JedisSentinelPool(
"mymaster", // 主节点名称(在哨兵配置中定义)
sentinels, // 哨兵地址集合
poolConfig, // 连接池配置
2000, // 连接超时(毫秒)
"password" // Redis密码(如果有)
);
// 使用连接
try (Jedis jedis = pool.getResource()) {
jedis.set("name", "张三");
String name = jedis.get("name");
System.out.println(name);
}
// 关闭连接池
pool.close();
}
}
Spring Boot 配置
yaml
spring:
redis:
sentinel:
master: mymaster
nodes:
- 192.168.1.10:26379
- 192.168.1.11:26379
- 192.168.1.12:26379
password: your_password
database: 0
timeout: 2000
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0
max-wait: -1ms
java
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(
RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
// 设置序列化器
Jackson2JsonRedisSerializer<Object> serializer =
new Jackson2JsonRedisSerializer<>(Object.class);
template.setValueSerializer(serializer);
template.setKeySerializer(new StringRedisSerializer());
return template;
}
}
5.3 客户端工作流程
text
1. 客户端启动
↓
2. 连接哨兵集群(任意一个哨兵)
客户端 → 哨兵1:"SENTINEL get-master-addr-by-name mymaster"
↓
3. 获取主节点地址
哨兵1 → 客户端:"192.168.1.100,6379"
↓
4. 连接主节点
客户端 → 主节点(192.168.1.100:6379)
↓
5. 订阅哨兵的频道(监听主节点切换)
客户端 → 哨兵:订阅 "+switch-master" 事件
↓
6. 正常读写操作
客户端 ←→ 主节点
↓
7. 【某一时刻,主节点故障,哨兵执行故障转移】
↓
8. 接收切换通知
哨兵 → 客户端:"+switch-master mymaster 192.168.1.101 6379"
↓
9. 自动重新连接
客户端 → 新主节点(192.168.1.101:6379)
↓
10. 业务无感知,继续正常读写
5.4 读写分离实现
java
import redis.clients.jedis.JedisSentinelPool;
import redis.clients.jedis. Jedis;
public class ReadWriteSplitExample {
private JedisSentinelPool masterPool; // 主节点池(写)
private JedisSentinelPool slavePool; // 从节点池(读)
public void write(String key, String value) {
try (Jedis master = masterPool.getResource()) {
master.set(key, value); // 写操作在主节点
}
}
public String read(String key) {
try (Jedis slave = slavePool. getResource()) {
return slave.get(key); // 读操作在从节点
}
}
}
六、生产环境配置
6.1 哨兵配置文件(sentinel.conf)
bash
# ========== 基础配置 ==========
# 哨兵端口
port 26379
# 后台运行
daemonize yes
# PID 文件
pidfile /var/run/redis-sentinel.pid
# 日志文件
logfile /var/log/redis/sentinel.log
# 工作目录
dir /var/lib/redis
# ========== 监控配置 ==========
# 监控主节点
# 格式:sentinel monitor <主节点名称> <IP> <端口> <quorum>
sentinel monitor mymaster 192.168.1.100 6379 2
# 主节点密码(如果有)
sentinel auth-pass mymaster your_password
# ========== 故障判定配置 ==========
# 多少毫秒无响应判定为主观下线(默认30秒)
sentinel down-after-milliseconds mymaster 30000
# 故障转移超时时间(默认3分钟)
sentinel failover-timeout mymaster 180000
# ========== 故障转移配置 ==========
# 故障转移时,同时同步新主节点的从节点数量
# 数字越小,转移时间越长,但对主节点影响越小
sentinel parallel-syncs mymaster 1
# ========== 通知脚本配置 ==========
# 故障转移时执行的脚本(可选)
# sentinel notification-script mymaster /path/to/notify.sh
# 故障转移后执行的脚本(可选)
# sentinel client-reconfig-script mymaster /path/to/reconfig.sh
# ========== 安全配置 ==========
# 保护模式(生产环境建议关闭,配合防火墙使用)
protected-mode no
# 绑定IP(可选,不配置则监听所有网络接口)
# bind 192.168.1.10
# 哨兵密码(可选)
# requirepass your_sentinel_password
6.2 主节点配置(redis.conf)
bash
# ========== 基础配置 ==========
bind 0.0.0.0
port 6379
daemonize yes
pidfile /var/run/redis_6379.pid
logfile /var/log/redis/redis. log
dir /var/lib/redis
# ========== 密码配置 ==========
requirepass your_password
# ========== 主从复制配置 ==========
# 防止脑裂:至少1个从节点在线才允许写入
min-replicas-to-write 1
# 从节点延迟不超过10秒
min-replicas-max-lag 10
# ========== 持久化配置 ==========
# RDB
save 900 1
save 300 10
save 60 10000
# AOF
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec
6.3 从节点配置(redis.conf)
bash
# ========== 基础配置 ==========
bind 0.0.0.0
port 6379
daemonize yes
pidfile /var/run/redis_6379.pid
logfile /var/log/redis/redis.log
dir /var/lib/redis
# ========== 主从复制配置 ==========
# 指定主节点(初始配置,哨兵会自动修改)
replicaof 192.168.1.100 6379
# 主节点密码
masterauth your_password
# 从节点密码
requirepass your_password
# 从节点优先级(数字越小优先级越高)
replica-priority 100
# 从节点只读
replica-read-only yes
6.4 部署架构示例
三节点部署(推荐最小配置)
text
服务器1 (192.168.1.10):
- Redis 主节点 (6379)
- Sentinel (26379)
服务器2 (192.168.1.11):
- Redis 从节点 (6379)
- Sentinel (26379)
服务器3 (192.168.1.12):
- Redis 从节点 (6379)
- Sentinel (26379)
配置:quorum = 2
五节点部署(推荐生产配置)
text
服务器1 (192.168.1.10):
- Redis 主节点 (6379)
- Sentinel (26379)
服务器2 (192.168.1.11):
- Redis 从节点 (6379)
- Sentinel (26379)
服务器3 (192.168.1.12):
- Redis 从节点 (6379)
- Sentinel (26379)
服务器4 (192.168.1.13):
- Sentinel (26379)
服务器5 (192.168.1.14):
- Sentinel (26379)
配置:quorum = 3
6.5 启动步骤
bash
# 1. 启动主节点
redis-server /etc/redis/redis-master.conf
# 2. 启动从节点
redis-server /etc/redis/redis-slave-1.conf
redis-server /etc/redis/redis-slave-2.conf
# 3. 验证主从复制
redis-cli -h 192.168.1.100 -a your_password INFO replication
# 4. 启动哨兵(3个哨兵节点)
redis-sentinel /etc/redis/sentinel-1.conf
redis-sentinel /etc/redis/sentinel-2.conf
redis-sentinel /etc/redis/sentinel-3.conf
# 5. 验证哨兵状态
redis-cli -h 192.168.1.10 -p 26379
> SENTINEL masters
> SENTINEL slaves mymaster
> SENTINEL sentinels mymaster
七、常见问题与解决方案
7.1 脑裂问题
什么是脑裂?
text
正常架构:
客户端 → 主节点 ← → 从节点
↑
哨兵监控
网络分区(主节点被隔离):
区域A:
客户端1 → 旧主节点(以为自己还是主节点)
区域B:
客户端2 → 新主节点(从节点被提升)
哨兵集群
问题:
- 客户端1 写入旧主节点:name=张三
- 客户端2 写入新主节点:name=李四
网络恢复后:
- 旧主节点降级为从节点
- 从新主节点同步数据:name=李四
- 张三的数据丢失了!❌
解决方案
配置防脑裂参数:
bash
# 在主节点的 redis.conf 中配置
# 至少1个从节点在线
min-replicas-to-write 1
# 从节点延迟不超过10秒
min-replicas-max-lag 10
工作原理:
text
旧主节点被隔离后:
- 检查连接的从节点数量:0个
- 0 < 1(min-replicas-to-write)
- 拒绝写入操作!❌
- 客户端1 写入失败
这样就不会有数据丢失了!✅
7.2 数据丢失问题
异步复制导致的数据丢失
text
场景:
1. 客户端写入主节点:SET name 张三
2. 主节点返回:OK
3. 主节点异步复制给从节点(还未完成)
4. 主节点突然宕机!
5. 从节点被提升为新主节点
6. 从节点没有 name=张三 的数据
结果:数据丢失 ❌
解决方案:
bash
# 方案1:配置 min-replicas(部分缓解)
min-replicas-to-write 1
min-replicas-max-lag 10
# 方案2:使用 WAIT 命令(强一致性)
# 确保至少1个从节点完成复制后再返回
SET name 张三
WAIT 1 1000 # 等待1个从节点,超时1000ms
java
// Java 代码示例
jedis.set("name", "张三");
jedis.wait(1, 1000); // 等待至少1个从节点确认
AOF持久化数据丢失
text
场景:
1. appendfsync everysec(每秒同步一次)
2. 写入数据后1秒内服务器断电
3. 最后1秒的数据未持久化
结果:最多丢失1秒数据 ❌
解决方案:
bash
# redis. conf 配置
# 方案1:每次写入都同步(最安全,性能最差)
appendfsync always
# 方案2:每秒同步(推荐,平衡性能和安全)
appendfsync everysec
# 方案3:由操作系统决定(性能最好,安全性最差)
appendfsync no
7.3 哨兵配置错误
错误1:quorum 设置不当
bash
# 错误配置:3个哨兵,quorum=1
sentinel monitor mymaster 192.168.1.100 6379 1
# 问题:任何一个哨兵误判都会触发故障转移 ❌
# 正确配置:
sentinel monitor mymaster 192.168.1.100 6379 2
错误2:哨兵与 Redis 部署在同一台服务器
text
错误部署:
服务器1:主节点 + 哨兵1
服务器2:从节点 + 哨兵2
服务器3:从节点 + 哨兵3
问题:
- 服务器1宕机 → 主节点和哨兵1都挂了
- 只剩2个哨兵,但主节点已经挂了
- 虽然能故障转移,但失去了一个哨兵 ❌
更好的部署:
服务器1:主节点
服务器2:从节点
服务器3:从节点
服务器4:哨兵1
服务器5:哨兵2
服务器6:哨兵3
错误3:down-after-milliseconds 设置过短
bash
# 错误配置:
sentinel down-after-milliseconds mymaster 5000 # 5秒
# 问题:网络抖动就会触发故障转移 ❌
# 推荐配置:
sentinel down-after-milliseconds mymaster 30000 # 30秒
7.4 性能问题
问题:故障转移时间过长
原因分析:
text
故障转移耗时 = 主观下线判定时间 + 客观下线确认时间 + 选举时间 + 转移执行时间
默认配置:
- down-after-milliseconds: 30秒
- 询问其他哨兵: 1-2秒
- 选举Leader: 1-2秒
- 执行转移: 5-10秒
总计:约40秒左右
优化方案:
bash
# 适当缩短判定时间(需权衡误判风险)
sentinel down-after-milliseconds mymaster 10000 # 10秒
# 缩短故障转移超时时间
sentinel failover-timeout mymaster 60000 # 1分钟
# 增加并行同步数量(小心对主节点影响)
sentinel parallel-syncs mymaster 2
八、面试高频问题
8.1 基础问题
Q1:什么是 Redis 哨兵?作用是什么?
答案:
Redis Sentinel 是 Redis 官方的高可用解决方案,主要有4个作用:
- 监控:持续监控主从节点和其他哨兵的运行状态
- 通知:当监控的实例出现问题时发送通知
- 自动故障转移:主节点故障时,自动将从节点提升为主节点
- 配置中心:客户端通过哨兵获取当前主节点地址
Q2:主观下线和客观下线的区别?
答案:
| 类型 | 主观下线(SDOWN) | 客观下线(ODOWN) |
|---|---|---|
| 判断者 | 单个哨兵 | 多个哨兵(>=quorum) |
| 可靠性 | 可能误判 | 几乎确定 |
| 触发条件 | 超过 down-after-milliseconds 无响应 | 超过 quorum 个哨兵同意 |
| 后续动作 | 询问其他哨兵 | 触发故障转移 |
Q3:哨兵集群需要多少个节点?为什么要奇数个?
答案:
- 推荐至少3个哨兵,推荐奇数个(3、5、7)
原因:
text
3个哨兵(quorum=2):容错1个
4个哨兵(quorum=3):容错1个
5个哨兵(quorum=3):容错2个
6个哨兵(quorum=4):容错2个
结论:偶数个浪费资源,容错能力不变
8.2 进阶问题
Q4:如何选择新的主节点?
答案:
选择流程(按优先级):
- 排除不健康的从节点(下线、断线、长时间未响应)
- 比较优先级:replica-priority 数字越小越优先
- 比较复制偏移量:offset 越大数据越新
- 比较 runid:字典序最小的
Q5:什么是 quorum?如何设置?
答案:
- 定义:判定主节点客观下线所需的最少哨兵同意数量
- 配置 :
sentinel monitor mymaster 192.168.1.100 6379 2(最后的2是quorum) - 设置原则 :
quorum = 哨兵总数 / 2 + 1
注意事项:
- quorum 只影响客观下线判定
- 故障转移需要超过半数哨兵存活(Raft 选举要求)
Q6:哨兵之间如何自动发现?
答案:
通过 Redis 的发布订阅机制:
- 每个哨兵向主节点的
__sentinel__:hello频道发布消息(包含自己的 IP、端口、runid) - 每个哨兵订阅主节点的
__sentinel__:hello频道 - 通过接收其他哨兵发布的消息,互相发现并建立连接
- 通过
INFO命令从主节点获取从节点列表
Q7:客户端如何连接哨兵模式的 Redis?
答案:
java
// 配置哨兵地址(不是主节点地址!)
Set<String> sentinels = new HashSet<>();
sentinels.add("192.168.1.10:26379");
sentinels.add("192.168.1.11:26379");
sentinels.add("192.168.1.12:26379");
// 创建哨兵连接池
JedisSentinelPool pool = new JedisSentinelPool("mymaster", sentinels);
// 使用
Jedis jedis = pool.getResource();
工作流程:
- 客户端连接哨兵获取当前主节点地址
- 连接主节点进行读写
- 订阅哨兵的
+switch-master事件 - 主节点故障转移后,接收通知并自动重连
8.3 高级问题
Q8:什么是脑裂?如何解决?
答案:
脑裂定义:
网络分区导致旧主节点未感知到自己已被降级,继续接受写入,网络恢复后数据被覆盖,造成数据丢失。
解决方案:
bash
# 在主节点配置
min-replicas-to-write 1 # 至少1个从节点在线
min-replicas-max-lag 10 # 从节点延迟不超过10秒
原理:
当主节点被隔离后,发现没有从节点连接,拒绝写入操作,避免数据丢失。
Q9:哨兵模式的缺点是什么?
答案:
| 缺点 | 说明 | 解决方案 |
|---|---|---|
| 不支持数据分片 | 所有数据在一个主节点,存储容量受限 | 使用 Redis Cluster |
| 写操作无法扩展 | 只有一个主节点可写 | 使用 Redis Cluster |
| 故障转移有短暂不可用 | 选举和切换需要时间(通常几秒到几十秒) | 客户端重试机制 |
| 主从复制延迟 | 异步复制可能导致数据丢失 | 配置 min-replicas-to-write |
Q10:哨兵和 Redis Cluster 的区别?
答案:
| 特性 | 哨兵模式 | Cluster 模式 |
|---|---|---|
| 数据分片 | ❌ 不支持 | ✅ 支持(16384个槽) |
| 写扩展 | ❌ 单主节点 | ✅ 多主节点 |
| 高可用 | ✅ 自动故障转移 | ✅ 自动故障转移 |
| 客户端复杂度 | 低 | 中(需要支持集群协议) |
| 适用场景 | 数据量小,高可用需求 | 数据量大,需要水平扩展 |
九、实战项目中的应用
9.1 项目描述话术
面试回答示例:
"在之前的项目中,我们使用 Redis 哨兵实现高可用。部署了3个 Sentinel 节点 ,quorum 设置为 2。当主节点故障时,哨兵会自动进行故障转移,将从节点提升为主节点。客户端通过 Jedis 的
JedisSentinelPool连接哨兵,能够自动感知主节点切换。为了防止脑裂导致数据丢失,我们还配置了min-replicas-to-write参数,确保主节点至少有 1 个从节点在线才允许写入。整体故障转移时间在 30-40 秒左右,对业务影响较小。"
9.2 常见运维操作
手动故障转移
bash
# 连接任意哨兵
redis-cli -h 192.168.1.10 -p 26379
# 手动触发故障转移(用于主动切换主节点)
SENTINEL failover mymaster
# 查看故障转移状态
SENTINEL masters
查看哨兵信息
bash
# 查看监控的主节点
SENTINEL masters
# 查看从节点
SENTINEL slaves mymaster
# 查看其他哨兵
SENTINEL sentinels mymaster
# 查看主节点地址
SENTINEL get-master-addr-by-name mymaster
动态修改配置
bash
# 修改 down-after-milliseconds
SENTINEL set mymaster down-after-milliseconds 20000
# 修改 parallel-syncs
SENTINEL set mymaster parallel-syncs 2
# 修改 quorum
SENTINEL set mymaster quorum 3
十、总结
10.1 核心要点
一句话总结:
Redis Sentinel 是官方的高可用方案,通过多个哨兵节点监控主从架构,当主节点故障时自动选举新主节点并通知客户端,实现自动故障转移。
必须掌握的8个知识点:
- ✅ 至少3个哨兵,推荐奇数个
- ✅ quorum = 哨兵数 / 2 + 1
- ✅ 主观下线(SDOWN) → 单个哨兵认为挂了
- ✅ 客观下线(ODOWN) → 多数哨兵认为挂了
- ✅ Raft 选举 → 选 Leader 执行故障转移
- ✅ 客户端连接哨兵,不直接连主节点
- ✅ 防脑裂配置 :
min-replicas-to-write - ✅ 选主规则:优先级 → 偏移量 → runid
10.2 快速记忆口诀
text
三个哨兵来监控(至少3个哨兵)
主观客观两下线(SDOWN、ODOWN)
法定人数定生死(quorum机制)
选举领导做转移(Raft选举)
客户哨兵来连接(客户端接入方式)
脑裂配置防丢失(防脑裂参数)