02 哨兵 Sentinel
主从复制解决了备份和读扩展,但"主挂了要人工切换"是硬伤。哨兵就是那个 7×24 小时替你盯着、并自动完成切换的值班员。 本章以理解概念为主。
1. 哨兵是什么
哨兵(Sentinel)是独立运行的特殊 Redis 进程,它不存业务数据,专职做四件事:
- 监控:不停地 ping 主节点和从节点,检查是否存活
- 自动故障转移:确认主节点挂了后,自动从从节点中选一个提升为新主,并让其他从节点改认新主
- 通知客户端:客户端连接哨兵询问"现在谁是主节点",切换后自动感知
- 配置提供者:充当客户端的服务发现入口
架构长这样(生产标准配置:3 个哨兵 + 1 主 2 从):
哨兵1 哨兵2 哨兵3 ← 互相通信,共同监控
\ | /
↘ ↓ ↙
┌─────────┐
│ 主节点 │
└─────────┘
↙ ↘
┌──────┐ ┌──────┐
│ 从节点1 │ │ 从节点2 │
└──────┘ └──────┘
2. 为什么哨兵要部署 3 个(奇数个)?
如果只有 1 个哨兵,它自己的网络抖一下,就会误判"主节点挂了"而乱切换。所以需要多个哨兵投票:
- 主观下线(SDOWN):某一个哨兵 ping 不通主节点,它"个人认为"主挂了
- 客观下线(ODOWN):达到
quorum(法定人数,通常配 2)个哨兵都认为主挂了,才正式认定故障 - 随后哨兵之间选举出一个领头哨兵(Raft 算法思想),由它执行故障转移
部署奇数个(至少 3 个)是为了投票能形成多数派、避免平票。
3. 故障转移的完整流程
- 主节点宕机
- 各哨兵陆续发现 ping 不通 → 主观下线
- 达到 quorum 个哨兵同意 → 客观下线
- 哨兵选举出领头哨兵
- 领头哨兵挑选新主节点,优先级规则大致为:
- 配置的优先级(
replica-priority)高的优先 - 复制偏移量最大的优先(数据最全的)
- runid 最小的兜底
- 配置的优先级(
- 对选中的从节点执行
replicaof no one,升级为主 - 通知其余从节点
replicaof 新主 - 旧主恢复后,自动降级为新主的从节点
整个过程通常几秒到几十秒,期间写请求会失败,客户端要有重试机制。
4. 最小配置示例(了解即可)
每个哨兵一份 sentinel.conf:
port 26379
# 监控名为 mymaster 的主节点,quorum = 2
sentinel monitor mymaster 127.0.0.1 6379 2
# ping 不通 30 秒判定主观下线
sentinel down-after-milliseconds mymaster 30000
# 故障转移超时
sentinel failover-timeout mymaster 180000
启动:
redis-sentinel sentinel.conf
客户端连接方式也变了——连哨兵而不是直连主节点(以 Python 为例):
from redis.sentinel import Sentinel
sentinel = Sentinel([("h1", 26379), ("h2", 26379), ("h3", 26379)])
master = sentinel.master_for("mymaster") # 自动发现当前主节点
replica = sentinel.slave_for("mymaster") # 自动发现从节点
master.set("k", "v") # 写走主
replica.get("k") # 读走从
# 主从切换后,sentinel 客户端会自动连上新主,业务代码无感
5. 哨兵模式的局限
哨兵解决了高可用(自动切换),但没解决:
- 写性能瓶颈:还是只有一个主节点在写
- 容量瓶颈:数据总量不能超过单机内存
数据量大到一台机器装不下时,就需要最后一块拼图——集群(Cluster)。
6. 小结
- 哨兵 = 监控 + 自动故障转移 + 客户端服务发现
- 至少部署 3 个哨兵,靠**投票(quorum)**避免误判:主观下线 → 客观下线 → 选领头 → 切换
- 新主的选择偏向数据最全的从节点
- 客户端连哨兵,切换无感知
- 哨兵不解决写性能和容量问题 → 引出集群
下一章:数据分片,突破单机极限 👉 03 集群 Cluster