【面试突击】Redis 哨兵(Sentinel)完全指南:从原理到实战

文章目录

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个作用:

  1. 监控:持续监控主从节点和其他哨兵的运行状态
  2. 通知:当监控的实例出现问题时发送通知
  3. 自动故障转移:主节点故障时,自动将从节点提升为主节点
  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:如何选择新的主节点?

答案:

选择流程(按优先级):

  1. 排除不健康的从节点(下线、断线、长时间未响应)
  2. 比较优先级:replica-priority 数字越小越优先
  3. 比较复制偏移量:offset 越大数据越新
  4. 比较 runid:字典序最小的
Q5:什么是 quorum?如何设置?

答案:

  • 定义:判定主节点客观下线所需的最少哨兵同意数量
  • 配置sentinel monitor mymaster 192.168.1.100 6379 2(最后的2是quorum)
  • 设置原则quorum = 哨兵总数 / 2 + 1

注意事项:

  • quorum 只影响客观下线判定
  • 故障转移需要超过半数哨兵存活(Raft 选举要求)
Q6:哨兵之间如何自动发现?

答案:

通过 Redis 的发布订阅机制

  1. 每个哨兵向主节点的 __sentinel__:hello 频道发布消息(包含自己的 IP、端口、runid)
  2. 每个哨兵订阅主节点的 __sentinel__:hello 频道
  3. 通过接收其他哨兵发布的消息,互相发现并建立连接
  4. 通过 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();

工作流程:

  1. 客户端连接哨兵获取当前主节点地址
  2. 连接主节点进行读写
  3. 订阅哨兵的 +switch-master 事件
  4. 主节点故障转移后,接收通知并自动重连

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个知识点:

  1. 至少3个哨兵,推荐奇数个
  2. quorum = 哨兵数 / 2 + 1
  3. 主观下线(SDOWN) → 单个哨兵认为挂了
  4. 客观下线(ODOWN) → 多数哨兵认为挂了
  5. Raft 选举 → 选 Leader 执行故障转移
  6. 客户端连接哨兵,不直接连主节点
  7. 防脑裂配置min-replicas-to-write
  8. 选主规则:优先级 → 偏移量 → runid

10.2 快速记忆口诀

text 复制代码
三个哨兵来监控(至少3个哨兵)
主观客观两下线(SDOWN、ODOWN)
法定人数定生死(quorum机制)
选举领导做转移(Raft选举)
客户哨兵来连接(客户端接入方式)
脑裂配置防丢失(防脑裂参数)
相关推荐
Lee川11 小时前
优雅进化的JavaScript:从ES6+新特性看现代前端开发范式
javascript·面试
Lee川14 小时前
从异步迷雾到优雅流程:JavaScript异步编程与内存管理的现代化之旅
javascript·面试
晴殇i16 小时前
揭秘JavaScript中那些“不冒泡”的DOM事件
前端·javascript·面试
绝无仅有16 小时前
Redis过期删除与内存淘汰策略详解
后端·面试·架构
绝无仅有16 小时前
Redis大Key问题排查与解决方案全解析
后端·面试·架构
AAA梅狸猫17 小时前
Looper.loop() 循环机制
面试
AAA梅狸猫17 小时前
Handler基本概念
面试
Wect18 小时前
浏览器缓存机制
前端·面试·浏览器
掘金安东尼19 小时前
Fun with TypeScript Generics:玩转 TS 泛型
前端·javascript·面试
掘金安东尼19 小时前
Next.js 企业级落地
前端·javascript·面试