Appearance
哨兵模式
Redis Sentinel 是主从架构外面的一层"观察者和调度者"——一组 Sentinel 进程持续监控主库和从库,发现主库不可用后投票确认故障,再从从库中选一个提升为新主库,通知其他从库改挂新主。
Sentinel 解决的是主从的自动切换问题,不解决分片扩容问题。数据容量和写入压力仍然由单个主库承担。需要多主分片时看 Redis Cluster。
一、Sentinel 的部署结构
Sentinel 进程和 Redis 进程是分开的——它监听独立的端口(默认 26379),使用独立的配置文件和 systemd unit。Sentinel 可以和 Redis 部署在同一台机器上,也可以分开部署。
至少需要 3 个 Sentinel 节点。原因不是"3 个比较好看",而是 Sentinel 需要投票达成共识(quorum)才能确认故障——1 个节点自身故障就被误判,2 个节点出现网络分区时无法区分"对方挂了"和"网络断了"。3 个节点用奇数,投票不出现平局。
示例拓扑:
| 角色 | 地址 | Redis 端口 | Sentinel 端口 |
|---|---|---|---|
| Redis master | 192.168.10.11 | 6379 | 26379 |
| Redis replica | 192.168.10.129 | 6379 | 26379 |
| Redis replica | 192.168.10.130 | 6379 | 26379 |
先配好 Redis 主从,再部署 Sentinel。Sentinel 的配置对象是"已经存在的主从拓扑"。
二、Sentinel 配置文件
sentinel.conf:
conf
port 26379
daemonize no
supervised systemd
dir /data/redis-sentinel
logfile /var/log/redis/sentinel.log
sentinel monitor mymaster 192.168.10.11 6379 2
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 60000
sentinel parallel-syncs mymaster 1关键配置的含义:
sentinel monitor mymaster 192.168.10.11 6379 2:告诉 Sentinel 监控主库192.168.10.11:6379,命名为mymaster(这个名字会被客户端用来查询主库地址),最后的2是 quorum——至少 2 个 Sentinel 同意才算客观下线。down-after-milliseconds 5000:单个 Sentinel 在 5 秒内没有收到主库的正常响应,就把它标记为 SDOWN(主观下线)。failover-timeout 60000:故障转移各阶段的总超时时间。如果 60 秒内切换没完成,就放弃本次尝试。parallel-syncs 1:切换完成后,允许多少个从库同时并行同步新主。设为 1 表示一次只同步一个,避免新主同时给所有从库发 RDB 压力太大。
如果 Redis 有密码:
conf
sentinel auth-pass mymaster strong_password_hereSentinel 在运行过程中会自动重写 sentinel.conf,把发现的从库、其他 Sentinel 节点、当前主库状态写进去。所以 sentinel.conf 文件需要对 Redis 运行用户有写权限,且不要设置为只读。自动重写的内容是 Sentinel 的"运行时记忆"——重启后靠这些记录恢复对拓扑的认知。
三、systemd 管理 Sentinel
ini
# /etc/systemd/system/redis-sentinel.service
[Unit]
Description=Redis Sentinel
After=network.target
[Service]
Type=notify
User=redis
Group=redis
ExecStart=/usr/local/redis/bin/redis-sentinel /etc/redis/sentinel.conf --supervised systemd
Restart=on-failure
RestartSec=5
LimitNOFILE=100000
[Install]
WantedBy=multi-user.target启动:
bash
systemctl daemon-reload
systemctl enable --now redis-sentinel四、查看 Sentinel 状态
Sentinel 用 redis-cli 连接到它的端口(26379),用 SENTINEL 系列子命令管理:
bash
redis-cli -p 26379 PING
# 查看当前监控的主库
redis-cli -p 26379 SENTINEL master mymaster
# 查看从库列表
redis-cli -p 26379 SENTINEL replicas mymaster
# 查看其他 Sentinel 节点
redis-cli -p 26379 SENTINEL sentinels mymaster
# 获取当前主库的地址(客户端最常用的查询)
redis-cli -p 26379 SENTINEL get-master-addr-by-name mymasterSENTINEL get-master-addr-by-name 是客户端发现主库的核心命令。应用不写死主库 IP,而是通过 Sentinel 查询当前主库地址——切换后查询结果自动指向新主库。但前提是客户端的连接库(如 Jedis、Lettuce、redigo)支持 Sentinel 模式并正确配置了 Sentinel 地址列表。
五、主观下线(SDOWN)和客观下线(ODOWN)
Sentinel 判断故障分两层:
| 状态 | 触发条件 | 含义 |
|---|---|---|
| SDOWN(主观下线) | 单个 Sentinel 在 down-after-milliseconds 时间内没收到主库正常响应 | "我认为它挂了" |
| ODOWN(客观下线) | 达到 quorum 数量的 Sentinel 都报告 SDOWN | "大家投票确认它挂了" |
只有进入 ODOWN 后,Sentinel 才会启动故障转移。这两个阶段的设计是为了防止单个 Sentinel 因为自身网络问题而误判主库故障。
quorum 值的选择:
- 3 个 Sentinel,quorum = 2:任意 1 个出问题(宕机或网络分区),还有 2 个可以正常投票
- 5 个 Sentinel,quorum = 3:容忍 2 个出问题
quorum 太低容易误判——比如网络抖动导致 2 个 Sentinel 暂时连不上主库;quorum 太高可能真的故障时凑不够票,切换迟迟不触发。
六、故障转移的过程中做了什么
主库被确认为 ODOWN 后:
text
1. Sentinel 之间选举一个 leader(由这个 leader 主导本次故障转移)
2. Leader 从可用从库中选一个作为新主库
3. Leader 向新主库发送 REPLICAOF NO ONE,将其提升为主库
4. Leader 通知其他从库改为复制新主库
5. Leader 更新自己的配置
6. 客户端通过 SENTINEL get-master-addr-by-name 查询到新地址选主(第二步)时的权衡因素:
| 因素 | 优先 |
|---|---|
| 从库是否在线 | 下线的直接排除 |
| 复制偏移量 | offset 越大越好,数据越新 |
replica-priority | 值越小优先级越高,0 表示永不提升 |
| 运行 ID | 以上都相同时按运行 ID 排序 |
如果有一台从库只用于备份、不希望被提升为主库:
conf
replica-priority 0 # 这个从库永远不会被 Sentinel 提升为主库七、手动触发切换
维护窗口里(比如要重启主库所在机器),可以先手动触发一次平滑切换:
bash
redis-cli -p 26379 SENTINEL failover mymaster这会让 Sentinel 主动执行一次故障转移——原主库变成从库,选一个从库提升。和被动故障切换的区别是:旧主库在线的情况下可以平滑交接,不会有数据丢失。
手动切换后验证:
bash
# 确认新主库地址
redis-cli -p 26379 SENTINEL get-master-addr-by-name mymaster
# 在新主库上做写入测试
redis-cli -h <新主库IP> -p 6379 SET test:failover "$(date +%s)"
redis-cli -h <新主库IP> -p 6379 GET test:failover八、客户端如何适配 Sentinel
客户端不能写死主库 IP——写死的话 Sentinel 切换了,应用还连着旧地址。正确的接入方式:
text
客户端配置:
- Sentinel 地址列表:192.168.10.11:26379, 192.168.10.129:26379, 192.168.10.130:26379
- master 名称:mymaster
客户端内部流程:
Sentinel 列表 → 任意一个 Sentinel 响应 → 查询 mymaster 的主库地址 → 连接该地址
→ 订阅 Sentinel 的切换事件 → 主库地址变更时重连新主库客户端要能处理"查到的地址突然连不上"的情况——更新主库地址后立即重连新主库,而不是一直重试旧地址。
九、切换后常见的"看起来切换了但业务没好"
Sentinel 报告切换成功,不等于业务恢复了。几个经常出问题的环节:
| 问题 | 原因 | 怎么确认 |
|---|---|---|
| 应用还连着旧主库 | 客户端直接写死了 IP | 检查客户端配置是否使用 Sentinel |
| 连接池没刷新 | 连接池里的旧连接还保留着 | 检查连接池是否在收到切换事件后重建连接 |
| 旧主库恢复后又写入 | 旧主库重启后没有加 replicaof,以独立主库身份运行 | 检查旧主库的 INFO replication 角色 |
| 新主库不可写 | replica-read-only 没取消,或 REPLICAOF NO ONE 没成功 | 在新主库上执行写入测试 |
旧主库恢复后的处理是容易出问题的环节——如果直接启动但没有配置 replicaof,它就成为一个独立主库。此时系统中存在两个"主库",各自接受写入,数据出现分叉。恢复旧主库的正确方式是让它以从库身份加入,挂到新主库下面。
十、通知脚本
Sentinel 可以在发生关键事件时执行外部脚本:
conf
sentinel notification-script mymaster /usr/local/bin/redis-sentinel-notify.sh脚本示例:
bash
#!/bin/bash
set -euo pipefail
event="$1"
master="$2"
logger -t redis-sentinel "event=$event master=$master args=$*"脚本里不适合写太重的逻辑——调外部 API 失败或执行时间过长会影响 Sentinel 自身工作。更稳妥的做法是脚本只记录和投递事件(发一条消息到监控系统),复杂处理交给外部系统。
十一、常见故障排查
主库故障但没有切换:
bash
# 看有多少 Sentinel 在线
redis-cli -p 26379 SENTINEL sentinels mymaster
# 看主库在 Sentinel 眼中的状态
redis-cli -p 26379 SENTINEL master mymaster常见原因:Sentinel 节点之间不通(无法形成 ODOWN)、quorum 设置太高凑不够票、所有从库的 replica-priority 都是 0、或者 Sentinel 和主库之间的网络没断但主库和客户端之间断了(Sentinel 角度看主库还活着)。
Sentinel 看不到从库:从库的 replicaof 配置是否正确、从库和 Sentinel 之间的网络可达性、sentinel auth-pass 密码是否正确。
切换后应用报错:十有八九是客户端没接 Sentinel,或者连接池没刷新。
Sentinel 是主从架构上的自动切换组件,不是完整高可用方案的全部。应用连接、网络隔离、监控告警、旧主库恢复流程,这些缺一个都会让自动切换的效果大打折扣。