【面试突击】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选举)
客户哨兵来连接(客户端接入方式)
脑裂配置防丢失(防脑裂参数)
相关推荐
踏浪无痕2 小时前
JobFlow:固定分片如何解决分布式扫描的边界抖动
后端·面试·架构
NAGNIP3 小时前
Transformer 中为什么用LayerNorm而不用BatchNorm?
人工智能·面试
踏浪无痕3 小时前
JobFlow调度的难题:超时、补偿与漏调
后端·面试·架构
白帽黑客-晨哥3 小时前
Web安全方向的面试通常会重点考察哪些漏洞和防御方案?
安全·web安全·面试·职场和发展·渗透测试
开心比对错重要4 小时前
进程、线程、虚拟线程详解及线程个数设置
java·jvm·算法·面试
9号达人6 小时前
支付配置时好时坏?异步方法里的对象引用坑
java·后端·面试
韭菜炒大葱6 小时前
TailwindCSS:从“样式民工”到“UI乐高大师”的逆袭
前端·面试·编程语言
Kiyra6 小时前
Spring Boot Starter 自定义开发:封装中间件配置
spring boot·redis·后端·缓存·中间件·性能优化·rocketmq
_OP_CHEN7 小时前
【C++数据结构进阶】从 Redis 底层到手写实现!跳表(Skiplist)全解析:手把手带你吃透 O (logN) 查找的神级结构!
数据结构·数据库·c++·redis·面试·力扣·跳表