第八章:哨兵模式
深入了解 Redis Sentinel 哨兵系统,实现 Redis 高可用。
最后更新: 2024-01-15
页面目录
Redis Sentinel 哨兵模式
Sentinel(哨兵)是 Redis 的高可用解决方案,用于监控主从架构、自动故障转移和通知。
Sentinel 概述
┌─────────────────────────────────────────────────────────────────┐
│ Redis Sentinel 架构 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Sentinel 集群 │ │
│ │ │ │
│ │ (Sentinel 1) (Sentinel 2) (Sentinel 3) │ │
│ │ :26379 :26379 :26380 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ │ 监控 │
│ ┌──────────────────────────┼────────────────────────────┐ │
│ │ │ │ │
│ │ ┌────┴────┐ │ │
│ │ │ Master │ │ │
│ │ │ 6379 │ │ │
│ │ └────┬────┘ │ │
│ │ │ │ │
│ │ ┌───────────────┼───────────────┐ │ │
│ │ │ │ │ │ │
│ │ ┌─────┴─────┐ ┌─────┴─────┐ ┌─────┴─────┐ │ │
│ │ │ Replica1 │ │ Replica2 │ │ Replica3 │ │ │
│ │ │ 6380 │ │ 6381 │ │ 6382 │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
Sentinel 功能
| 功能 | 说明 |
|---|---|
| 监控 | 持续监控主从节点健康状态 |
| 通知 | 故障时发送告警通知 |
| 自动故障转移 | 主节点故障时自动提升从节点 |
| 配置提供者 | 客户端连接哨兵获取主节点地址 |
Sentinel 选举机制
选举流程
1. Sentinel 定时检测主节点
2. 发现主节点不可达(主观下线)
3. 多个 Sentinel 确认(客观下线)
4. Sentinel 竞争成为 leader
5. Leader 执行故障转移
6. 选举新主节点
7. 更新配置,通知客户端
Leader 选举算法
# 使用 Raft 算法选举 leader
# 1. 获得其他 Sentinel 确认
# 2. 优先级排序
# 3. 票数过半成为 leader
# 4. 执行故障转移
配置 Sentinel
主节点配置
# /etc/redis/redis.conf
bind 0.0.0.0
port 6379
requirepass master_password
daemonize yes
Sentinel 配置
# /etc/redis/sentinel.conf
# Sentinel 端口
port 26379
# 守护进程
daemonize yes
pidfile /var/run/redis-sentinel.pid
# 日志
logfile /var/log/redis-sentinel.log
# 工作目录
dir /tmp
# 监控的主节点配置
# sentinel monitor <master-name> <ip> <port> <quorum>
sentinel monitor mymaster 127.0.0.1 6379 2
# 主节点密码
sentinel auth-pass mymaster master_password
# 主观下线时间(毫秒)
sentinel down-after-milliseconds mymaster 30000
# 故障转移超时
sentinel failover-timeout mymaster 180000
# 并行同步数量
sentinel parallel-syncs mymaster 1
# 客户端配置更新
sentinel notification-script mymaster /etc/redis/notify.sh
sentinel client-reconfig-script mymaster /etc/redis/reconfig.sh
最小配置
# 最简 Sentinel 配置
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel auth-pass mymaster password
启动 Sentinel
启动命令
# 直接启动
redis-sentinel /etc/redis/sentinel.conf
# 或使用 redis-server
redis-server /etc/redis/sentinel.conf --sentinel
Docker 启动
# docker-compose.yml
version: '3.8'
services:
sentinel1:
image: redis:7-alpine
container_name: redis-sentinel1
ports:
- "26379:26379"
command: |
redis-sentinel /usr/local/etc/redis/sentinel.conf
volumes:
- ./sentinel1.conf:/usr/local/etc/redis/sentinel.conf
sentinel2:
image: redis:7-alpine
container_name: redis-sentinel2
ports:
- "26380:26379"
command: |
redis-sentinel /usr/local/etc/redis/sentinel.conf
volumes:
- ./sentinel2.conf:/usr/local/etc/redis/sentinel.conf
sentinel3:
image: redis:7-alpine
container_name: redis-sentinel3
ports:
- "26381:26379"
command: |
redis-sentinel /usr/local/etc/redis/sentinel.conf
volumes:
- ./sentinel3.conf:/usr/local/etc/redis/sentinel.conf
Sentinel 命令
监控命令
# 查看 Sentinel 信息
redis-cli -p 26379 INFO
# 查看主节点
redis-cli -p 26379 SENTINEL master mymaster
# 查看所有从节点
redis-cli -p 26379 SENTINEL slaves mymaster
# 查看 Sentinel 列表
redis-cli -p 26379 SENTINEL sentinels mymaster
# 获取主节点地址
redis-cli -p 26379 SENTINEL get-master-addr-by-name mymaster
管理命令
# 手动故障转移
redis-cli -p 26379 SENTINEL failover mymaster
# 强制重新加载配置
redis-cli -p 26379 SENTINEL flushconfig
# 检查主节点状态
redis-cli -p 26379 SENTINEL is-master-down-by-addr 127.0.0.1 6379
客户端连接
Python 连接
from redis.sentinel import Sentinel
# 创建 Sentinel 连接
sentinel = Sentinel([
('localhost', 26379),
('localhost', 26380),
('localhost', 26381),
], socket_timeout=0.1)
# 获取主节点
master = sentinel.master_for('mymaster')
# 获取从节点
slave = sentinel.slave_for('mymaster')
# 使用主节点写
master.set('key', 'value')
# 使用从节点读
value = slave.get('key')
Java 连接
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisSentinelPool;
import redis.clients.jedis.exceptions.JedisException;
public class SentinelDemo {
public static void main(String[] args) {
Set<String> sentinels = new HashSet<>();
sentinels.add("localhost:26379");
sentinels.add("localhost:26380");
sentinels.add("localhost:26381");
JedisSentinelPool pool = new JedisSentinelPool(
"mymaster",
sentinels,
new JedisPoolConfig(),
2000,
"password"
);
// 获取连接
Jedis master = pool.getResource();
master.set("key", "value");
master.close();
// 归还连接
pool.close();
}
}
故障转移流程
详细步骤
┌─────────────────────────────────────────────────────────────────┐
│ 故障转移流程 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 1. 主观下线 │
│ ┌─────────┐ │
│ │Sentinel │──► ping master (超时) │
│ └─────────┘──► 主观下线 S_DOWN │
│ │
│ 2. 客观下线 │
│ ┌─────────┐ │
│ │Sentinel1│──► 通知其他 Sentinel │
│ └─────────┘ │
│ │ │
│ 超过 quorum 个确认 → 客观下线 O_DOWN │
│ │
│ 3. 选举 Leader │
│ ┌─────────┐ │
│ │Sentinel1│──► 发送 SENTINEL is-master-down │
│ │Sentinel2│──► 投票 │
│ │Sentinel3│──► 票数过半成为 Leader │
│ └─────────┘ │
│ │
│ 4. 故障转移 │
│ ┌─────────┐ │
│ │ Leader │──► 选择最优先的从节点 │
│ │ │──► SLAVEOF NO ONE │
│ │ │──► 更新其他从节点指向新主 │
│ │ │──► 更新 Sentinel 配置文件 │
│ └─────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
从节点选择标准
| 优先级 | 标准 |
|---|---|
| 1 | 优先级(replica-priority) |
| 2 | 复制偏移量(offset) |
| 3 | Run ID(字典序最小) |
# 从节点优先级配置(redis.conf)
replica-priority 100
通知脚本
故障通知
#!/bin/bash
# /etc/redis/notify.sh
case $1 in
# 主节点或从节点下线
+sdown)
echo "节点下线: $2"
;;
# 重新上线
-sdown)
echo "节点重新上线: $2"
;;
# 客观下线
+odown)
echo "客观下线: $2"
;;
# 故障转移开始
+switch-master)
echo "故障转移: $3 -> $4"
;;
# 故障转移完成
+slave-reconf-sent)
echo "从节点重新配置: $2"
;;
esac
# 发送告警(邮件、钉钉等)
# curl -X POST "https://oapi.dingtalk.com/robot/send?access_token=xxx" \
# -H "Content-Type: application/json" \
# -d '{"msgtype":"text","text":{"content":"Redis 故障告警"}}'
权限配置
sentinel notification-script mymaster /etc/redis/notify.sh
chmod +x /etc/redis/notify.sh
常见问题
1. Sentinel 无法发现节点
# 检查网络连通性
redis-cli -h master ping
# 检查端口
nc -zv master 6379
# 检查配置
redis-cli -p 26379 SENTINEL master mymaster
2. 故障转移失败
# 检查从节点状态
redis-cli INFO replication
# 检查 Sentinel 日志
tail -f /var/log/redis-sentinel.log
# 手动故障转移
redis-cli -p 26379 SENTINEL failover mymaster
3. 客户端未感知故障转移
# 确保使用正确的 Sentinel 配置
from redis.sentinel import Sentinel
sentinel = Sentinel([
('host1', 26379),
('host2', 26379),
('host3', 26379),
], socket_timeout=0.1)
# 每次操作获取最新主节点
master = sentinel.master_for('mymaster')
高可用部署建议
部署拓扑
| 环境 | Sentinel 数量 | Quorum | 说明 |
|---|---|---|---|
| 开发/测试 | 1 | 1 | 开发测试使用 |
| 生产环境 | 3 | 2 | 最低高可用配置 |
| 生产环境 | 5 | 2 | 推荐配置 |
| 生产环境 | 7 | 3 | 强一致性配置 |
网络架构
┌─────────────────────────────────────────────────────────────────┐
│ 推荐部署架构 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ DataCenter1│ │ DataCenter2│ │ DataCenter3│ │
│ │ │ │ │ │ │ │
│ │ ┌─────────┐ │ │ ┌─────────┐ │ │ ┌─────────┐ │ │
│ │ │ Master │ │◄──►│ │ Sentinel│ │ │ │ Sentinel│ │ │
│ │ └─────────┘ │ │ └─────────┘ │ │ └─────────┘ │ │
│ │ ┌─────────┐ │ │ │ │ │ │
│ │ │ Sentinel│ │ │ ┌─────────┐ │ │ ┌─────────┐ │ │
│ │ └─────────┘ │ │ │ Sentinel│ │ │ │ Sentinel│ │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
配置参数详解
| 参数 | 默认值 | 说明 |
|---|---|---|
| sentinel monitor | - | 监控配置 |
| sentinel auth-pass | - | 认证密码 |
| sentinel down-after-ms | 30000 | 主观下线时间 |
| sentinel failover-timeout | 180000 | 故障转移超时 |
| sentinel parallel-syncs | 1 | 并行同步数 |
| sentinel notification-script | - | 通知脚本 |
| sentinel client-reconfig-script | - | 重新配置脚本 |
下一步
接下来让我们学习 Redis 安全配置。
👉 安全配置