🛡️ Redis 哨兵模式

实现 Redis 高可用性的自动化解决方案

哨兵模式概述

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 monitor    
sentinel 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个哨兵确认客观下线

新主服务器选择规则:

💡 选择优先级:

  1. 排除故障节点:排除已下线、断线的从服务器
  2. 优先级排序:replica-priority 值排序(值越小优先级越高)
  3. 复制偏移量:选择复制偏移量最大的(数据最新)
  4. 运行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-writemin-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日志
  • 网络优化:确保哨兵之间网络稳定