很多团队第一次听说Redis哨兵模式,往往是被线上事故逼的。某天凌晨主库宕机,业务写入全部报错,值班同学爬起来手动执行slaveof no one,再把从库改主库,折腾半个多小时才恢复。这种操作一次两次还能忍,但每一次都在挑战团队的极限。
哨兵(Sentinel)这套机制存在的意义,就是为了干掉这种“人肉故障转移”。它做什么呢?三件事:监控主从状态、自动切换故障主库、通知客户端新的主库地址。你不需要写脚本轮询,不需要提前守在电脑前,它能在主库挂掉之后自动从从库中挑一个升级,把集群拉回可用状态。
今天这篇不会讲太虚的概念,重点放在哨兵到底怎么选主、怎么判定宕机、怎么配置防脑裂,以及真实部署时要面对的那些坑。配置文件和命令都有,照着敲就能跑起来。
1. 哨兵模式到底解决了什么问题
1.1 主从复制是基础,但不是高可用
主从架构本身解决的是读压力和数据备份问题。一个主库负责写,多个从库同步数据并承担读请求,确实比单机稳不少。但这里有一个要命的假设:主库永远活着。
一旦主库进程崩溃、服务器断电或者网络分区,主从架构就露馅了。从库们还在傻乎乎地执行复制,然而主库已经不在了,所有写操作直接失败。更麻烦的是,从库不会自动上位,它们只是被动等待命令。业务方如果连接的是从库,数据已经开始滞后,如果连接的是主库,直接看到连接拒绝。
这就是为什么我们需要哨兵模式。它不替代主从复制,而是在主从复制之上加了一个“监工”,确保主库挂掉时有人站出来处理后续流程。
1.2 哨兵的四个核心职责
官方文档把哨兵的功能归纳为四点:监控、通知、自动故障转移、配置提供。
- 监控:Sentinel实例会定时向所有主节点、从节点以及其他Sentinel发送心跳命令,确认它们是否存活。
- 通知:某个被监控的Redis实例出现异常时,哨兵通过脚本或API通知管理员或上层系统。
- 自动故障转移:主库被判“真死”后,哨兵会从从库中选一个提升为主库,把其他从库的复制目标改成这个新主库,同时把旧主库降级为从库。
- 配置提供:客户端启动时可以向哨兵询问当前主库地址,切换后客户端也能拿到新地址,避免了手动改配置的尴尬。
这四个职责合在一起,实际效果就是:一个Redis主从集群可以做到无人值守的故障恢复。
1.3 什么场景适合哨兵,什么场景不适合
哨兵模式适合需要高可用但数据量不庞大的场景,比如单副本支撑的业务缓存、某些后台系统的状态存储。注意,我特意提到“数据量不庞大”,因为哨兵模式本质上仍是“一主多从”,单主库存在内存上限和写瓶颈。
当数据量到了几十GB甚至上百GB,写并发也压到单机上很吃力时,哨兵就撑不住了。这时候该上Redis Cluster集群模式,它把数据分片到多个主节点,每个主节点各自带从节点。同样的高可用能力,但写能力可以横向扩展。
选型的时候不要一刀切,我见过有人单机才2GB数据就喊着要上Cluster,完全没必要。哨兵模式部署简单、运维成本低、客户端支持成熟,在中小规模下反而更香。
2. 哨兵架构与核心术语
2.1 Sentinel节点与数据节点的区别
哨兵本身也是一个Redis服务实例,但它的用途特殊,不存储业务数据,只保存监控信息。运行的进程叫redis-sentinel,也可以redis-server --port 26379 --sentinel启动。默认端口是26379。
一个标准的高可用部署至少需要三个Sentinel实例,后面我会解释为什么一定是三个而不是两个。数据节点则是正常的Redis主从结构,一主两从是常见的起步配置。
哨兵之间会互相通信,共享对Redis数据节点存活状态的看法。这种“多哨兵协同”机制可以避免单一哨兵因为网络抖动而误判主库宕机。
2.2 主观下线与客观下线:两个关键状态
这是理解哨兵故障转移原理最重要的两个概念。
主观下线(Subjectively Down,SDOWN):单个哨兵发现主库在配置的down-after-milliseconds时间内没有响应,就会把主库标记为主观下线。注意“主观”二字——它只是“我看到的”状态,可能与真实情况不符。比如哨兵和主库之间网络短暂拥堵,这一个哨兵就会误以为主库挂了。
客观下线(Objectively Down,ODOWN):当足够多的哨兵都认为主库主观下线时,这个状态才会被确认。这个“足够多”不是固定值,而是由quorum参数控制。quorum设置为2,则至少2个哨兵认为主库挂了,才会触发客观下线,进而启动故障转移流程。
这里有一个很多人理解错的地方:quorum并不决定由谁来做故障转移,只决定“是否需要触发”故障转移。真正执行切换的哨兵,是所有在线哨兵中通过选举产生的一个“领头哨兵”。
2.3 脑裂场景与哨兵如何规避
脑裂这个词在网络领域很常见。在Redis哨兵场景里,它指的是主库并未真正宕机,但哨兵误判后触发切换,导致两个主库同时存活。
举个例子:主库所在机器负载过高,或网络链路异常,Sentinel A无法连接主库,把主库标记为主观下线。随后Sentinel B和C也陆续报告无法连接,quorum满足,故障转移被触发,从库被提升为新主库。但旧主库其实还活着,只是网络分区把它隔离了。此时有业务写入旧主库,数据就产生了分叉。
Redis哨兵应对脑裂的思路比较务实:如果真的发生分区,宁可让少数区域不可写,也不能让两边同时写。实际配置中通过两个核心点控制:
min-replicas-to-write:主库写数据时至少要有N个从库连接。如果可用从库低于这个值,主库拒绝写入。这能降低分区后旧主库大量接收写入的概率。- 合理的
down-after-milliseconds:把判定时间设得宽松一点,避免因为瞬时抖动就触发主从切换。
我也见过有些团队把哨兵的下线判定时间调到1秒甚至几百毫秒,初衷是想“切换更快”,结果就是频繁误切换。这个参数我建议至少10秒起步,业务敏感的可设为30秒。
2.4 为什么需要奇数个哨兵节点
这是哨兵部署最容易被忽略的点。哨兵要选一个“领头哨兵”来执行故障转移,选举机制需要超过一半的哨兵同意。如果哨兵节点数为2,A同意B,B同意B,都是1:1,永远选不出来。如果有3个,即使一个哨兵失联,剩下2个还能互相确认,选举可以进行。
奇数节点还有一个好处:为了容忍故障又不破坏多数派,3节点最多允许挂1个,5节点最多允许挂2个。偶数节点看似安全,比如4节点允许挂1个吗?4节点多数派需要3个,挂1个还剩3个,确实能工作,但挂2个就只剩2个。而3节点挂1个还剩2个。4节点和3节点容错能力一样,却多花钱多维护,不划算。
所以生产环境最少3个哨兵,一般3个或5个即可。再多意义不大,因为哨兵不是数据副本,不需要无限增加。
3. 哨兵的工作流程与故障转移细节
3.1 哨兵之间的通信机制
每个哨兵启动后,会连接到自己监控的主库,并通过主库的pub/sub频道获知从库列表及新增的哨兵信息。具体来说,Redis有多个内部频道,比如__sentinel__:hello,哨兵周期性地在这个频道里发布自己的信息(IP、端口、运行ID、配置版本),同时订阅所有哨兵的消息。
通过这种发布订阅机制,哨兵之间自动发现对方,不需要提前硬编码对端地址。你只需要在配置里指定要监控的主库信息,其他哨兵会自动找上门来。
哨兵还会与其他哨兵建立长连接,用于安全通信和选举投票。这些连接是长期维持的,断线会重连。
3.2 故障转移内部流程,拆开看一共六步
我梳理一下整个故障转移的完整流程,每一步里哨兵都在做什么。理解这六步,比背一堆配置参数有用得多。
第一步,标记Sdown。每个哨兵通过sentinel is-master-down-by-addr命令检查主库状态。如果主库在down-after-milliseconds内没有有效响应,哨兵本地标记为主观下线。
第二步,确认Odown。标记为Sdown的哨兵会向其他哨兵发送请求,请它们也确认主库状态。如果收到包括自己在内的、数量达到quorum的确认,这个哨兵把主库标记为客观下线。
第三步,选举领头的哨兵。所有认为主库客观下线的哨兵参与选举,目标是产生一个执行者。选举算法是raft风格的:哨兵会给自己投票,也接受其他哨兵的投票请求。拿到半数以上投票的哨兵成为领头者。
领头哨兵产生后,真正执行故障转移的角色确定。其他哨兵进入等待状态,观察领头者的动作。
第四步,筛选候选从库。领头哨兵会遍历自己掌握的所有从库,过滤掉断线的、最近断连时间太久的、优先级太低的。Redis会从健康从库中选出候选。
第五步,选主并执行切换。候选从库按照priority值排序,值越小优先级越高。如果优先级一样,则比较复制偏移量,偏移量越大说明数据越接近旧主库。再相同的话,比较runid字典序,小的优先。选出的从库会收到replicaof no one命令,晋升为新主库。
第六步,修改其余从库配置。旧从库们收到replicaof 新主库IP 新主库端口,重新指向新的主库。另外,哨兵还会持续监控旧主库,如果它恢复了,会把它也降级为新主库的从库。
3.3 为什么投票机制重要
如果没有选举,每个哨兵都尝试执行故障转移,就会出现多个哨兵同时向不同从库发指令,数据状态混乱。
投票机制把故障转移的执行权收拢到某一个哨兵手里,由它统一对从库发号施令。其他哨兵不会插手,而是等待结果,然后定期刷新自己的配置视图。
选举过程中如果发起选举的哨兵没有拿到足够票数,它会退让,等待新一轮选举。所以即使网络分区中的两个哨兵同时发起竞选,最终也会收敛到一个胜出者。
3.4 配置文件版本号和防环机制
故障转移完成后,领头哨兵会把新的主库信息通过__sentinel__:hello频道广播,同时递增配置版本config-epoch。每个哨兵收到新配置后,会比较版本号,只接受更高版本。低版本信息不会覆盖高版本信息。
这样设计保证了:即使有哨兵因为分区而落后于最新配置,重新连接后也会同步最新主库状态,不会因为旧信息把新主库又切回旧主库。
在实际操作中,我确实见过有人手动改了哨兵配置文件造成版本混乱,后续触发了奇怪的切换行为。解决办法很简单:生产环境的哨兵配置文件不要手动编辑,改配置用sentinel set命令或者从配置管理平台下发。
4. 哨兵配置逐项解析与实战部署
4.1 sentinel.conf 关键配置项
哨兵的配置文件通常叫sentinel.conf,内容比redis.conf简洁很多。但每个参数在故障转移时都可能是胜负手,逐个过一遍。
port 26379 sentinel monitor mymaster 127.0.0.1 6379 2 sentinel down-after-milliseconds mymaster 30000 sentinel failover-timeout mymaster 180000 sentinel parallel-syncs mymaster 1 sentinel auth-pass mymaster 你的密码sentinel monitor这行是整个配置的入口。格式是:监控组名、主库IP、主库端口、quorum。组名可以自定义,一套哨兵可以监控多组主从,但生产环境不建议一组哨兵监控太多Redis主从,隔离性差。
down-after-milliseconds:主库无响应多久后标记为Sdown。单位毫秒,30000就是30秒。不要设太短,网络抖动和GC都容易触发误判。
failover-timeout:故障转移超时时间,默认180秒。这个值同时控制多个场景:与旧主库断开连接的时间上限、从库被提升后向新主库同步数据的上限。如果超过这个时间没有完成切换,哨兵可能重新发起流程。
parallel-syncs:故障转移后,允许同时有多少个从库向新主库同步数据。1意味着一个一个来,避免新主库瞬间同步压力过大,硬件资源紧张的可以保持1。
auth-pass和masterauth要注意区分。哨兵连接Redis实例做监控需要密码,用auth-pass。同时,从库连主库做复制也需要密码,要用masterauth。很多新人只配置了requirepass,结果哨兵能连通但复制建立不起来。
4.2 部署拓扑与启动命令
以最常用的“一主两从三哨兵”为例,拓扑如下:
- 主库:172.16.0.10:6379
- 从库1:172.16.0.11:6379
- 从库2:172.16.0.12:6379
- 哨兵1:172.16.0.10:26379
- 哨兵2:172.16.0.11:26379
- 哨兵3:172.16.0.12:26379
注意,哨兵和数据节点可以部署在同一台机器上,这是可行的,但生产环境有条件的话尽量分开。原因很简单:如果一台机器挂掉,部署在上面的数据节点和哨兵同时失效,其他哨兵需要注意容错数量是否仍然满足quorum。
启动数据节点:
redis-server /etc/redis/redis.conf启动哨兵:
redis-sentinel /etc/redis/sentinel.conf也可以用redis-server的--sentinel参数:
redis-server /etc/redis/sentinel.conf --sentinel启动完成后,通过redis-cli -p 26379 sentinel master mymaster可以看到监控状态,通过sentinel slaves mymaster可以看到从库列表。
4.3 从零搭建一主两从三哨兵的步骤
我假设三台机器都装好了Redis,从零开始操作一次。先准备主从配置,再部署哨兵。
第一步,主库的redis.conf写上:
bind 0.0.0.0 port 6379 daemonize yes requirepass redis123 masterauth redis123masterauth必须在从库配置里同样写上,否则从库同步主库数据时无法通过密码验证。
第二步,两个从库的redis.conf在以上基础上增加一行:
replicaof 172.16.0.10 6379如果Redis版本在5.0之前,这个指令叫slaveof。旧名称已被废弃,新的统一用replicaof。启动后执行redis-cli -p 6379 info replication,在从库上能看到role:slave,并且master_link_status:up。
第三步,三个Sentinel服务各自准备sentinel.conf。内容如下:
port 26379 daemonize yes dir /tmp sentinel monitor mymaster 172.16.0.10 6379 2 sentinel auth-pass mymaster redis123 sentinel down-after-milliseconds mymaster 30000 sentinel failover-timeout mymaster 180000 sentinel parallel-syncs mymaster 1注意sentinel monitor里填的是主库地址。三个哨兵都填一样的主库地址。quorum设为2。
启动三个哨兵后,观察日志或直接往主库写入数据,再通过任意哨兵查询主库地址:
redis-cli -p 26379 sentinel get-master-addr-by-name mymaster能正确返回主库IP和端口,说明监控链路正常。
4.4 客户端如何感知主库切换
故障转移后,主库地址变了,客户端如果还连接旧地址,自然无法写入。我们有两种方式应对:
一种是在业务层用哨兵地址获取当前主库。比如Java生态中的Lettuce和Jedis都支持从哨兵获取主库信息。每次启动时先从哨兵询问主库地址,切换后会通过pub/sub监听配置变更事件。
另一种是部署一层代理,比如早期的Twemproxy或者后续的各类Proxy方案,代理自动感知主库切换,应用只需要连接代理。但这种方案会引入额外延迟和运维负担,如果不是历史遗留,我更倾向于客户端直连哨兵的方式。
Spring Boot项目里配置哨兵模式很简单:
spring: redis: sentinel: master: mymaster nodes: 172.16.0.10:26379,172.16.0.11:26379,172.16.0.12:26379注意这里的nodes是哨兵地址,不是Redis数据节点地址。框架会通过哨兵动态获取真实主库地址。切换过程中客户端可能短暂报错,但如果配置了合适的超时时间和重试机制,对业务影响可以控制在秒级。
5. 故障演练:手动杀掉主库,看哨兵如何接管
5.1 演练环境准备与预期行为
理论终归是理论,不上手演练一遍,出了问题你都不敢确信哨兵真的会切换。我强烈建议每个团队在测试环境至少做一次完整的故障演练。
演练前,先确认几件事:
- 三个哨兵都在运行。
- 主从数据同步正常。
- 客户端连接的是哨兵配置,而不是硬编码的主库地址。
演练预期行为:杀掉主库进程后,10到30秒内(依据配置)哨兵标记Sdown,满足quorum后切换,新的主库产生,其余从库重新指向新主库。整个过程不需要人工干预。
5.2 操作过程与观察点
第一步,模拟故障。在主库机器上执行:
kill -9 $(cat /var/run/redis.pid)或者更粗暴一点:redis-cli -p 6379 debug sleep 60,让主库进入长时阻塞。用debug sleep的方式更贴近真实世界里的“假死”状态,网络连接仍存在,但响应超时。
第二步,观察哨兵日志。哨兵日志路径可以配置,没有配置就在stdout输出。日志中最关键的是这几行:
+sdown master mymaster 172.16.0.10 6379 +odown master mymaster 172.16.0.10 6379 #quorum 2/2 +try-failover master mymaster 172.16.0.10 6379 +switch-master mymaster 172.16.0.10 6379 172.16.0.11 6379sdown表示某个哨兵发现了问题,odown表示quorum达成,try-failover表示开始尝试切换,switch-master是切换成功的标志。如果顺利走通,整个流程应该不超过failover-timeout。
第三步,验证新主库。用哨兵查询当前主库地址:
redis-cli -p 26379 sentinel get-master-addr-by-name mymaster返回应该是新主库的IP和端口。再连上新主库执行info replication,可以看到它身份是master,其他从库都指向它。
第四步,恢复旧主库。把旧主库重新启动。理论上它会以从库身份加入到新主库的复制集群。执行info replication应看到role:slave,数据追平后进入online状态。
5.3 演练中容易发现的隐藏问题
真实演练过几次后,你就会发现一些配置文档里根本不会写的问题。
down-after-milliseconds太短导致切换极其频繁。在测试环境没有真实流量,网络质量很好,1秒也能正常工作。但到了生产环境,GC暂停、虚拟化宿主机IO抖动、网卡丢包都会造成误判。稳妥起见,生产环境至少设置成30秒。- 从库没有配置
masterauth,导致新主库选出来之后,从库无法认证复制。故障转移成功后,数据持续无法同步,直到人工发现。这种问题在演练时最容易暴露。 - 客户端连接池缓存了旧地址。即使哨兵已经切换,客户端持有的连接池还在往旧主库上发命令。需要有机制让连接池重建,或者在客户端配置里开启地址刷新。
- 哨兵配置文件在故障转移后会被自动重写。你原本写的主库地址会被更新为新主库地址,这是正常行为。下次监控的就不再是旧主库了。
6. 哨兵模式的高可用参数调优与经验值
6.1 关键参数推荐值以及背后逻辑
我这里给一套经过多轮压测和生产验证的推荐参考值。不同业务特点会有差异,但作为起点已经够用。
| 参数 | 推荐值 | 参考说明 |
|---|---|---|
| down-after-milliseconds | 30000 | 低于10秒易误判,30秒在故障与误判之间较平衡 |
| failover-timeout | 180000 | 与运维经验相关,切换失败后再诊断的时间容忍 |
| parallel-syncs | 1 | 从库较多时不要设2以上,同步流量和fork可能压垮新主库 |
| quorum | 2(3哨兵) | 别设3,单哨兵故障就无法触发切换 |
| min-replicas-to-write | 1 | 放弃可用性也要防脑裂,数据一致性优先 |
parallel-syncs这个参数值得单独说。3个或更多从库同时连接到新主库执行全量同步时,新主库的内存、CPU、磁盘IO都会被打满。如果新主库硬件资源本身比较紧张,到时候新主库也可能被拖垮。所以保持1是安全做法,代价是所有从库数据追平的耗时变长。
6.2 min-replicas-to-write 和 min-replicas-max-lag 组合使用
这两个参数是Redis从5.0开始提供的保护机制,用于减少脑裂风险。简单来说,主库会检查自己的在线从库数量,如果低于设定值,就停止接受写入。
举个例子:
min-replicas-to-write 1 min-replicas-max-lag 10含义是:主库至少要有1个从库连接正常,并且数据延迟不能超过10秒。如果主库在分区中,没有从库能连上它,写请求将报错,数据不会在旧主库上产生新分叉。
这样的配置牺牲了一部分可用性,但换来了“旧主库不产生新数据”的一致性保证。对资金类、订单类系统非常推荐。对一致性要求不那么高的业务,可以设置为0,完全关闭这个限制。
6.3 为什么我不建议把切换时间调得过短
很多业务方提出要求:“Redis挂了必须在5秒内切换”。这个愿望可以理解,但确实不容易实现。
故障转移涉及多个流程:哨兵确认Sdown、哨兵间确认Odown、选举领头哨兵、筛选从库、发送切换命令、从库提升、其他从库重新指向。每一环都需要时间,而且是网络往返的时间。如果down-after-milliseconds设置成5秒,从库数量多、网络质量一般的情况下,整个过程可能需要15到20秒。
当然,如果业务真的能容忍较短不可用时间,可以考虑10秒左右的配置。但小于5秒的坏处更多:只要某台机器发生一次长停顿,比如磁盘RAID卡偶发卡顿、JVM GC停顿,就可能引发误切换。切换本身附带数据同步开销和短暂访问抖动,误切换比不切换更痛苦。
7. 常见问题排查与踩坑实录
7.1 哨兵没有触发自动切换,可能卡在哪里
主库挂了,哨兵却没有切换,这是反馈最多的问题。排查顺序我建议按下面几步走:
先看哨兵日志。如果压根没有+sdown日志,说明哨兵甚至没发现主库异常。这时检查down-after-milliseconds是否过短,或者主库与哨兵之间网络是否真的断了。
如果有+sdown但没有+odown,说明quorum未满足。一个常见场景是三个哨兵部署在三台机器上,其中一台挂了,恰好剩下的某个哨兵和主库之间网络不通。三个哨兵里两个在线,但只有1个认为主库挂了,另一个因为网络正常而认为主库在线,quorum为2时就无法确认。
如果有+odown但没有+try-failover,问题在选举阶段。可能是哨兵配置版本不一致,或者某个哨兵认为自己是更高版本的配置源,不参与选举。
另外还见过一个极端情况:哨兵虽然监控主库,但配置里写的是主库的内网IP,而业务通过外网IP访问。主库宕机后哨兵能切换成功,但新主库向外广播的内网IP客户端根本连不上。这种问题在云环境下特别常见,配置时务必确认IP地址类别清楚。
7.2 为什么我从库数据一直追不上
故障转移后从库数据追不上,优先看复制连接状态。执行info replication查看master_link_status,如果为down,说明从库连不上新主库。
排查步骤:先确认新主库的requirepass是否配置了密码,再确认从库的masterauth是否一致。特别要注意,哨兵切换主库后,如果从库只配置了requirepass而忘了masterauth,复制认证必然失败。
另一个可能是从库配置了replica-read-only yes,但旧主库恢复后作为从库加入集群,它的配置里有slave-read-only no或者历史遗留配置覆盖,导致写入了脏数据。建议在从库配置中明确replica-read-only yes,并在角色切换后重新检查。
7.3 哨兵日志提示 instance is down 但实际还能访问
这种情况通常是网络分区导致的:哨兵能访问某个从库的IP,但访问主库的IP超时。这里要先思考一个问题:哨兵监控用的IP是不是和客户端访问的IP一致。
如果哨兵用内网IP监控,客户端用公网IP访问,网络分区发生时内网通信正常但公网链路异常,就会出现哨兵没触发切换但客户端已经连不上的现象。反过来说,哨兵通过公网IP监控,内网抖动时也可能误判。
最佳实践:哨兵和数据节点之间的通信与应用访问通道分开评估。如果应用容器到Redis的链路和哨兵到Redis的链路不同,网络故障的表现也会不同。
7.4 故障转移后旧主库重新加入,出现双主现象
双主现象多半是旧主库没有正确降级。旧主库恢复后,哨兵会通过pub/sub告知它当前的新主库信息,但旧主库如果因为网络分区没有成功执行replicaof,它将保持在master状态,业务如果此时仍然写旧主库,就产生两条数据线。
排查方式:检查旧主库的info replication,如果role是master,且没有任何从库,手动执行:
redis-cli -p 6379 replicaof 新主库IP 新主库端口如果旧主库配置了密码,记得先设置好masterauth再执行replicaof,否则连接认证失败,依然无法降级。
更根本的解决手段是前文提到的min-replicas-to-write,主库在线从库数不足时拒绝写入,从源头上规避双写。
7.5 客户端在故障转移期间抛错,如何优雅处理
故障转移期间客户端抛错几乎无法完全避免,但可以把影响降到最低。主要手段有两个:合适的超时时间和重试机制。
Lettuce客户端里,连接池的maxWait不要设置太久,否则故障期间大量请求堆积在线程池里,切换完成后仍然会超时。建议connectTimeout控制在1到2秒,socketTimeout控制在2到3秒。
应用层还要做好重试逻辑。如果写入失败,标记请求可重试,等哨兵切换完成后再重放。这里要小心幂等性,避免重试造成数据重复。具体实现可以在业务表里加唯一键,或用Redis操作避免重复提交。
Spring Boot的Lettuce还自带拓扑刷新功能,但需要确认版本。较老的版本不会自动刷新哨兵拓扑,要升级到较新的客户端库并开启sentinelReconnect和定时刷新机制。
8. 哨兵模式的实践心得与后续选择
我见过太多团队在哨兵模式的配置上吃过亏,包括我自己。有一次线上主库宕机,哨兵切换倒是成功了,但新主库的性能比旧主库差很多,原因是最强的那台从库优先级配置最高,数据偏移量也大,但它所在机器的CPU配额只有原来的一半。这种硬件差异问题,单靠哨兵选主规则解决不了,需要在部署设计阶段就把硬件规格统一。
另一个深刻的体会是:哨兵模式不是一把万能钥匙。它解决的只是“主库挂了怎么办”的问题,不解决容量问题、不解决数据分片问题、也不解决跨机房容灾问题。如果你的Redis用量已经接近单机上限,再去追求高可用,就像在快漏水的船底补漆,治标不治本。
选择什么时候升级到Cluster,我建议看两个指标:一是当前主库内存使用是否经常超过单机物理内存的一半,二是写入吞吐是否到了单线程极限。查询QPS高还可以通过加从库分担读流量,但写流量卡在单主上,只能通过分片拆开。
如果数据量没那么大,但确实对稳定性有要求,先花时间把哨兵参数吃透、把故障演练做扎实,比盲目追求架构复杂度更有价值。哨兵模式本身足够成熟稳定,配合良好的运维能力,完全可以支撑很多核心业务的可用性要求。
最后分享一个团队内部一直沿用的习惯:每个季度的故障演练日,把Redis主库宕机、机房断网、切换后切回这三个场景都跑一遍,记录实际切换耗时和暴露配置问题。刚开始几次总会有意外发生,但跑过几轮之后,哨兵配置的收敛速度明显加快,线上故障响应也比以前从容得多。这也算是我对这类基础设施组件运维的一点真实经验。