当前位置: 首页 > news >正文

Redis 从入门到精通:Redis Sentinel 哨兵

IT策士 10余年一线大厂经验,专注 IT 思维、架构、职场进阶。我会在各个平台持续发布最新文章,助你少走弯路。

上一篇我们搭建了 Redis 主从架构,实现了数据冗余和读写分离。但有个致命问题:当主节点宕机时,必须手动将某个从节点提升为主节点,再通知其他从节点和所有应用修改连接地址。这个过程少则几分钟,多则半小时,对于核心业务来说完全不可接受。

Redis Sentinel(哨兵)就是为解决这个问题而生的。它能自动监控主从节点、自动发现故障、自动完成故障转移,让 Redis 真正具备生产级高可用能力。本文带你从原理到实战,用 Docker 搭建完整哨兵集群,并用 Python 客户端对接,感受自动故障转移的丝滑体验。

1. Sentinel 是什么?解决了什么问题?

Sentinel 是一套独立的分布式监控系统,它由多个哨兵进程组成,共同协作完成三个核心任务:

  • 监控(Monitoring):持续检查主节点和从节点是否正常运行。

  • 通知(Notification):当节点状态发生变化时,通过 API 或脚本通知管理员和其他应用。

  • 自动故障转移(Automatic Failover):当主节点不可用时,自动选举一个从节点升级为新主节点,并让其他从节点和新主节点同步。

架构图:

┌──────────────┬──────────────┬──────────────┐ │ Sentinel1│ Sentinel2│ Sentinel3│ ← 哨兵集群(至少3个) └──────┬───────┴──────┬───────┴──────┬───────┘ │ │ │ ▼ ▼ ▼ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ Master │───>│ Slave1│ │ Slave2│ └──────────┘ └──────────┘ └──────────┘

💡 Sentinel 本身不存储数据,它是独立于 Redis 数据节点的控制平面。即使没有 Sentinel,Redis 也能正常工作;加上 Sentinel,就拥有了自动化的“守护神”。

2. 核心原理:主观下线与客观下线

哨兵判断一个节点是否故障,用了两层机制。

2.1 主观下线(SDOWN)

每个哨兵进程每隔 1 秒向主节点、从节点、其他哨兵发送PING命令。如果某个节点在down-after-milliseconds(配置项,默认 30 秒)时间内没有有效回复,这个哨兵自身就会标记该节点为主观下线(Subjectively Down)。

但单个哨兵的判断可能失误——比如自己的网络出了故障。所以不能仅凭 SDOWN 就发起故障转移。

2.2 客观下线(ODOWN)

当某个哨兵发现主节点进入 SDOWN 后,它会询问其他哨兵:“你们也觉得主节点挂了吗?” 当达到quorum(法定人数,通常为哨兵数量的半数以上)个哨兵都认为主节点下线,该节点就被标记为客观下线(Objectively Down)。

此时,哨兵集群会通过Raft 算法选举出一个哨兵领导者,由它来执行故障转移。

2.3 故障转移流程

  1. 哨兵领导者从所有健康的从节点中,按优先级、复制偏移量、runid 等规则选出最合适的一个。

  2. 向被选中的从节点发送SLAVEOF NO ONE,将其提升为主节点。

  3. 向其他从节点发送SLAVEOF 新主IP 新主端口,让它们同步新主。

  4. 将旧主节点的地址更新到哨兵配置中,一旦旧主恢复,它将成为新主的从节点。

整个过程通常在10~30 秒内完成。

⚠️ Sentinel 只对主节点做客观下线判断。从节点和哨兵节点只需要主观下线即可做出反应。

3. 关键配置参数详解

在开始实战之前,先理解 Sentinel 的核心配置项:

# sentinel.conf 核心配置# 监控的主节点:名称、IP、端口、quorum(法定人数)sentinel monitor mymaster127.0.0.163792# 主观下线判定超时(毫秒),超过此时间无响应则 SDOWNsentinel down-after-milliseconds mymaster5000# 故障转移最大超时(毫秒),超过此时间未完成则重新选举领导者sentinel failover-timeout mymaster10000# 新主节点同步时,允许同时同步的从节点数量(越小同步越慢,但主节点压力小)sentinel parallel-syncs mymaster1# 哨兵端口port26379

📌quorum 建议:3 个哨兵设quorum=2;5 个哨兵设quorum=3。这保证即使少数哨兵故障,也能正常完成故障转移。

4. 实战:Docker 搭建 Sentinel 集群

我们搭建一个“1 主 2 从 + 3 哨兵”的完整环境。

4.1 整体架构

Sentinel 集群(26379/26380/26381) │ ▼ 主节点 Master(6379) │ ┌───┴───┐ ▼ ▼ Slave1(6380) Slave2(6381)

4.2 创建网络和主从节点

# 创建网络dockernetwork create sentinel-net# 启动主节点dockerrun-d--nameredis-master--networksentinel-net\-p6379:6379 redis:7.2 redis-server--appendonlyyes# 启动从节点 1dockerrun-d--nameredis-slave1--networksentinel-net\-p6380:6379 redis:7.2 redis-server\--appendonlyyes--slaveofredis-master6379# 启动从节点 2dockerrun-d--nameredis-slave2--networksentinel-net\-p6381:6379 redis:7.2 redis-server\--appendonlyyes--slaveofredis-master6379

验证主从状态:

dockerexecredis-master redis-cli INFO replication|grepconnected_slaves# connected_slaves:2

4.3 配置并启动 3 个哨兵

首先在宿主机创建哨兵配置文件(3 份):

# 创建目录mkdir-p~/sentinel/conf# 哨兵配置文件cat>~/sentinel/conf/sentinel.conf<<'EOF' port 26379 dir /data sentinel monitor mymaster redis-master 6379 2 sentinel down-after-milliseconds mymaster 5000 sentinel failover-timeout mymaster 10000 sentinel parallel-syncs mymaster 1 EOF# 复制为 3 份(端口不同)cp~/sentinel/conf/sentinel.conf ~/sentinel/conf/sentinel1.confcp~/sentinel/conf/sentinel.conf ~/sentinel/conf/sentinel2.confcp~/sentinel/conf/sentinel.conf ~/sentinel/conf/sentinel3.conf

⚠️ 实际生产中 3 个哨兵最好部署在不同物理机上,这里仅演示原理,都放在同一台机器。

启动哨兵容器:

# 哨兵 1 (端口 26379)dockerrun-d--namesentinel1--networksentinel-net\-p26379:26379\-v~/sentinel/conf/sentinel1.conf:/etc/redis/sentinel.conf\redis:7.2 redis-sentinel /etc/redis/sentinel.conf# 哨兵 2 (端口 26380)dockerrun-d--namesentinel2--networksentinel-net\-p26380:26379\-v~/sentinel/conf/sentinel2.conf:/etc/redis/sentinel.conf\redis:7.2 redis-sentinel /etc/redis/sentinel.conf# 哨兵 3 (端口 26381)dockerrun-d--namesentinel3--networksentinel-net\-p26381:26379\-v~/sentinel/conf/sentinel3.conf:/etc/redis/sentinel.conf\redis:7.2 redis-sentinel /etc/redis/sentinel.conf

4.4 验证哨兵状态

连接到任意哨兵查看信息:

dockerexec-itsentinel1 redis-cli-p26379127.0.0.1:26379>SENTINEL masters1)1)"name"2)"mymaster"3)"ip"4)"redis-master"5)"port"6)"6379"7)"flags"8)"master"9)"num-slaves"10)"2"...127.0.0.1:26379>SENTINEL slaves mymaster1)1)"name"2)"172.18.0.3:6379"3)"ip"4)"172.18.0.3"...127.0.0.1:26379>SENTINEL sentinels mymaster(列出所有监控 mymaster 的哨兵)

看到这些信息,说明 3 个哨兵已经成功发现主节点和从节点,系统处于健康监控状态。

5. 模拟故障转移:亲眼见证自动切换

5.1 停止主节点

等待约 5~10 秒(down-after-milliseconds为 5000ms),观察哨兵日志:

dockerlogs sentinel1--tail20

关键日志行:

+sdown master mymaster redis-master6379+odown master mymaster redis-master6379#quorum 2/2+try-failover master mymaster redis-master6379+vote-for-leader... +elected-leader master mymaster redis-master6379+failover-state-select-slave master mymaster redis-master6379+selected-slave slave172.18.0.3:6379... redis-slave16379+failover-state-send-slaveof-noone slave redis-slave1:6379 +failover-state-wait-promotion slave redis-slave1:6379 +promoted-slave slave redis-slave1:6379 +failover-state-reconf-slaves master mymaster... +slave-reconf-sent slave redis-slave2:6379 +failover-end master mymaster redis-master6379+switch-master mymaster redis-master6379172.18.0.36379

日志清晰地展示了完整过程:检测主观下线 → 达成客观下线共识 → 选举哨兵领导 → 选择最佳从节点 → 提升为新主 → 重配置其他从节点。

5.2 验证新主节点

# 查看新主状态dockerexecredis-slave1 redis-cli INFO replication|greprole# role:master# 查看原从节点 2 是否指向新主dockerexecredis-slave2 redis-cli INFO replication|grepmaster_host# master_host:172.18.0.3 (即 redis-slave1)

原主节点恢复后会自动变成从节点:

dockerstart redis-master# 几秒后dockerexecredis-master redis-cli INFO replication|greprole# role:slave

6. Python 客户端对接 Sentinel

最关键的环节来了:应用如何自动感知主节点切换?答案是使用 Sentinel 感知的客户端。

6.1 安装 redis-py

6.2 Sentinel 连接方式

redis-py提供了Sentinel类来管理主从发现:

from redis.sentinelimportSentinelimporttime# 配置所有哨兵地址sentinel_hosts=[('localhost',26379),('localhost',26380),('localhost',26381),]# 创建 Sentinel 客户端sentinel=Sentinel(sentinel_hosts,socket_timeout=0.5)# 从哨兵获取当前主节点和从节点master=sentinel.master_for('mymaster',socket_timeout=0.5,decode_responses=True)slave=sentinel.slave_for('mymaster',socket_timeout=0.5,decode_responses=True)# 写操作:走主节点master.set('username','IT策士')master.set('counter',100)print(f"写入完成: username={master.get('username')}, counter={master.get('counter')}")# 读操作:走从节点print(f"从节点读取: username={slave.get('username')}, counter={slave.get('counter')}")# 获取当前主节点的实际地址master_addr=sentinel.discover_master('mymaster')print(f"当前主节点地址: {master_addr}")slaves=sentinel.discover_slaves('mymaster')print(f"当前从节点列表: {slaves}")

输出示例:

写入完成:username=IT策士,counter=100从节点读取:username=IT策士,counter=100当前主节点地址:('172.18.0.3',6379)当前从节点列表:[('172.18.0.4',6379),('172.18.0.2',6379)]

6.3 验证故障转移对客户端透明

编写一个持续读写的脚本来测试:

from redis.sentinelimportSentinelimporttimesentinel=Sentinel([('localhost',26379),('localhost',26380),('localhost',26381),],socket_timeout=0.5)master=sentinel.master_for('mymaster',socket_timeout=0.5,decode_responses=True,retry_on_timeout=True)print("开始持续写入...")foriinrange(1000): try: key=f'key:{i % 10}'value=f'value:{i}'master.set(key, value)result=master.get(key)print(f"[{i:03d}] SET/GET {key} = {result}")time.sleep(0.1)except Exception as e: print(f"[{i:03d}] 错误: {e}")time.sleep(0.5)# 等待故障转移完成print("测试结束")

在脚本运行期间,用docker stop redis-slave1停掉当前主节点,观察输出:

[042]SET/GET key:2=value:42[043]SET/GET key:3=value:43[044]SET/GET key:4=value:44[045]错误: Connection refused[046]错误: Connection refused[047]SET/GET key:7=value:47 ← 恢复!自动连上新主[048]SET/GET key:8=value:48

客户端在短暂报错后自动恢复,无需任何人工干预。这就是 Sentinel 的核心价值。

6.4 封装高可用客户端类

from redis.sentinelimportSentinel from typingimportList, Tuple class RedisHA:"""Sentinel 高可用 Redis 客户端""" def __init__(self, sentinel_hosts: List[Tuple[str, int]], service_name: str='mymaster', socket_timeout: float=0.5, decode_responses: bool=True): self.sentinel=Sentinel(sentinel_hosts,socket_timeout=socket_timeout)self.service_name=service_name self.decode_responses=decode_responses self._master=None self._slave=None @property def master(self):"""获取主节点连接(自动发现)"""returnself.sentinel.master_for(self.service_name,socket_timeout=0.5,decode_responses=self.decode_responses,retry_on_timeout=True)@property def slave(self):"""获取从节点连接(轮询)"""returnself.sentinel.slave_for(self.service_name,socket_timeout=0.5,decode_responses=self.decode_responses,retry_on_timeout=True)def get_master_addr(self):"""获取当前主节点地址"""returnself.sentinel.discover_master(self.service_name)def get_slaves(self):"""获取所有从节点"""returnself.sentinel.discover_slaves(self.service_name)def set(self, key, value, **kwargs):returnself.master.set(key, value, **kwargs)def get(self, key):returnself.slave.get(key)def delete(self, key):returnself.master.delete(key)# 使用cache=RedisHA(sentinel_hosts=[('localhost',26379),('localhost',26380),('localhost',26381)])cache.set('app:version','3.0')print(cache.get('app:version'))print(f'当前主节点: {cache.get_master_addr()}')

7. 生产环境最佳实践

  • 哨兵数量:至少 3 个,且部署在不同物理机或可用区。偶数个哨兵会增加投票平局的概率。

  • quorum 设置sentinel_num / 2 + 1,例如 3 哨兵设 2,5 哨兵设 3。

  • 客户端超时和重试:设置socket_timeoutretry_on_timeout,避免因网络抖动抛异常。

  • 监控哨兵本身:哨兵也可能挂,需要监控哨兵进程的存活和SENTINEL masters的正确性。

  • 避免在故障转移期间操作:故障转移一般 10~30 秒,此期间短暂不可写入是正常现象。

  • 不要将哨兵和数据节点混布在同一台机器:否则机器宕机时数据节点和哨兵一起挂。

8. 动手试试

  1. 模拟主节点宕机:在 Python 持续写入时,docker stop主节点,观察恢复时间。

  2. 模拟哨兵宕机:停掉 1 个哨兵,观察剩下 2 个是否仍能正常完成故障转移(quorum=2)。

  3. 查看故障转移后配置:检查哨兵配置文件,看sentinel monitor行是否自动更新为新主地址。

  4. 网络分区模拟:通过 Docker 网络隔离主节点,观察哨兵的行为和客户端恢复。

预期效果:主节点宕机后 10~20 秒自动恢复写入;单哨兵宕机不影响集群可用性;配置文件自动更新;网络分区恢复后旧主变为从。

9. 总结

Sentinel 让 Redis 从“单点可用”跃升为“真正的自动高可用”。但它无法解决写的横向扩展——所有写入仍然只能走一个主节点。下一篇,我们将迎来 Redis 的终极形态:Redis Cluster,用哈希槽实现数据分片,彻底突破单机瓶颈。

想了解更多还可以去各个平台搜索「IT策士」,一起升级 IT 思维 !

http://www.cnnetsun.cn/news/2909813.html

相关文章:

  • 星露谷物语SMAPI终极指南:5分钟学会安全安装模组框架
  • 英雄联盟自动化工具终极指南:如何通过智能助手提升60%游戏效率
  • 5分钟快速上手:使用Scrapy-pinduoduo构建专业级拼多多数据采集系统
  • 别再手动删点了!用Python的RDP算法5分钟搞定轨迹/轮廓简化(附Shapely避坑指南)
  • 从地图App的流畅缩放,到游戏模型的轻量加载:聊聊Ramer-Douglas-Peucker算法背后的工程智慧
  • MC68341芯片选与RTC配置实战:从寄存器原理到嵌入式系统稳定设计
  • 别被坑了!2026实测好用的AI论文写作工具|实测必入避坑版
  • 别再手动维护字典了!用Python装饰器实现一个自己的Registry注册器(附完整代码)
  • 抖音内容下载终极指南:从零搭建自动采集系统的完整方案
  • 深入解析NXP KE1x系列PCC外设时钟控制器:原理、配置与低功耗实践
  • 实战指南:用Python的巴特沃斯滤波器,给你的传感器数据(比如Arduino或树莓派采集的)降降噪
  • 从你家墙上的220V到手机充电器:RMS电压到底是怎么影响我们日常用电的?
  • 终端与IDE形态的vibe coding实测:两款AI编程工具迭代能力对比
  • 从“表面相似“到“语义匹配“:BERTScore如何重塑你的文本评估体验?
  • 中国大模型价格战背后的AI基础设施重构
  • 高层次综合设计乒乓buffer(double-buffer/pingpong-buffer)
  • MC68349串口驱动与JTAG边界扫描实战:嵌入式通信与硬件调试核心技术解析
  • NSK双滑块定位承载装置技术手册
  • APK Installer:在Windows电脑上运行安卓应用的终极指南
  • 手把手复现:用Python仿真验证电容容抗公式1/(j*2*pi*f*C),附代码与波形分析
  • 豆包暴跌610万用户的真相:AI产品免费模式的死亡螺旋与破局路径
  • “泄露了windows12“
  • 从PCL/VTK迁移到C#/Halcon?手把手教你用ActiViz.NET实现三维点云可视化(避坑指南)
  • DSGE模型终极指南:如何从零开始掌握宏观经济建模的40个经典案例
  • FUXA工业可视化平台实战指南:快速构建专业级SCADA监控系统
  • Cursor Free VIP:破解AI编程助手限制的技术实现与深度应用指南
  • 别再只记结论了!通过5个PyTorch代码实验,亲手验证model.eval()与torch.no_grad()的真实影响
  • CAN FD协议升级?手把手教你用FPGA实现更高带宽的车载通信节点
  • 从审核员视角看漏洞:拆解CNVD收录标准,理解安全风险的‘轻重缓急’
  • JESD204B协议仿真全流程:从Vivado IP核配置到波形调试(含代码解读)