Redis内存溢出导致Sentinel主从切换失败的深层原因与修复方案
先说结论:根本原因并非Sentinel自身出现故障,而是它所依赖的Redis实例在OOM(内存溢出)状态下进入只读或拒绝写入模式,导致Sentinel无法正常执行INFO、CONFIG GET等探测命令,从而误判主节点已下线。更隐蔽的问题在于——当主库的used_memory接近maxmemory上限,且淘汰策略设置为noeviction时,写入操作会直接返回(error) OOM command not allowed when used memory > 'maxmemory'。Sentinel心跳检测失败后,便会误触发主从切换。

检查Sentinel是否因OOM受到“间接影响”
首先确认Sentinel进程自身是否健康,再排查它所连接的Redis实例。操作步骤非常直接:
redis-cli -p 26379 ping—— 查看Sentinel是否正常响应。redis-cli -p 26379 sentinel masters—— 检查主节点状态字段中的flags是否包含s_down或o_down标记。- 对主库执行
redis-cli info memory | grep -E "used_memory|maxmemory|mem_fragmentation_ratio",如果used_memory等于maxmemory且mem_fragmentation_ratio大于1.5,那么Sentinel很可能已经与主库失联。
为Sentinel增设内存保护“兜底机制”
Sentinel本身并不管理内存,但你可以借助外部机制防止它因Redis OOM而误判。核心思路是让主库在内存压力下主动降级,而非被动崩溃:
- 在主库配置中启用
maxmemory-policy allkeys-lru(避免使用noeviction),从而防止写入直接失败。 - 设置
min-sla ves-to-write 1和min-sla ves-max-lag 10,让主库在从库延迟过高或掉线时主动拒绝写入,这比OOM后被动崩溃更可控。 - 定期使用
redis-cli --bigkeys扫描,对检测到的超过1MB的hash或zsetkey添加EXPIRE,避免单个key耗尽内存。 - 在部署层添加cgroup限制:对Redis进程设置
memory.max(Linux cgroup v2),这比Redis自身的maxmemory更为硬性,能防止jemalloc分配超限。
修复后,验证Sentinel行为是否恢复正常
调整配置后,必须验证Sentinel能否正确感知主库恢复,而不是卡在“半下线”状态。以下关键操作不可忽视:
- 手动触发一次
redis-cli -p 26379 sentinel failover mymaster,观察日志中是否出现+switch-master事件。 - 清空主库内存:依次执行
redis-cli config set maxmemory 4gb→redis-cli flushdb→ 再config set maxmemory 8gb,检查Sentinel是否重新连接并更新is-master-down-by-addr结果。 - 重点监控
redis-cli -p 26379 sentinel sentinels mymaster输出中每个sentinel的last-ping-reply和last-ok-ping-reply时间差。如果超过down-after-milliseconds值,说明通信仍然存在异常。
最容易忽略的一点是:Sentinel默认每10秒发送一次INFO,但若主库OOM后响应变慢,Sentinel可能在超时前重试三次,累积大量TIME_WAIT连接,最终耗尽文件描述符。因此,调大tcp-keepalive并同步降低down-after-milliseconds需要同时进行,不可只改其中一项。
