
搭建一套高可用的Redis哨兵集群是很多团队从单机Redis迈向生产环境的必经之路。网上关于哨兵的教程不少但多数要么只讲概念不讲落地要么给了一堆命令但没解释为什么要这么配。这篇文章我基于实际部署经验把一主两从三哨兵的完整过程、配置参数背后的原理、以及我踩过的坑一次性写清楚希望能帮你少走弯路。这套架构解决的核心问题很简单主节点挂了怎么办。主从复制解决了数据备份和读扩展但master宕机后需要人工切换业务会中断。哨兵集群的职责就是自动发现master故障、选举新的master、并通知客户端更新连接信息。文章适合正在搭建Redis高可用方案的运维和开发同学也适合准备面试时梳理哨兵机制的读者。1. 架构设计与核心思路1.1 为什么需要一主两从三哨兵先想明白一个基础问题主从复制本身有什么不足Redis的主从复制解决了两个问题一是数据冗余master的数据实时同步到slave即使master节点磁盘损坏slave上还有副本二是读写分离把读流量分散到从节点上减轻master压力。但主从复制有个致命缺陷——master宕机后整个写入能力直接丧失。你需要人工登录某台slave执行REPLICAOF NO ONE把它提升为master然后让其他slave重新指向它再修改客户端的连接配置。这个过程少说也要几分钟而且人很容易在紧张中出现操作失误。哨兵Sentinel就是来解决这个问题的。它是一个独立运行的进程专门监控Redis主从节点的健康状况。当master不可用时哨兵集群通过投票选出一个新master自动完成故障转移整个过程不需要人工干预业务中断时间可以控制在秒级。再来说为什么是“一主两从”和“三哨兵”。一主两从的拓扑是Redis官方推荐的底线配置。两个从节点保证master宕机后至少有一个从节点拥有完整数据可用于提升另一个从节点则作为新master的备用提供持续的读服务。如果你只有一主一从master宕机后虽然也能用从节点顶上但这台从节点既是新的master又承担所有读流量一旦它再出问题整个集群就彻底瘫痪了。两个从节点给了系统容错的缓冲。三个哨兵则是保证故障转移决策的正确性。哨兵之间通过投票机制达成共识只有当多数哨兵确认master不可用时才执行故障转移。两个哨兵时如果一个哨兵宕机剩下一个无法形成多数派故障转移就永远无法触发——这是脑裂和可用性之间的权衡。三哨兵允许一个哨兵宕机剩下两个哨兵还能正常协作完成故障转移在保证正确性的前提下尽可能提升可用性。这也是为什么生产环境至少要部署三个哨兵的原因。1.2 哨兵集群的工作机制速览哨兵本质上是一个运行在特殊模式下的Redis服务器但它不用来存储业务数据。它的核心工作可以概括为四件事监控Monitoring哨兵每隔一秒向master和slave发送PING命令检查它们是否存活。如果master在指定时间内没有响应哨兵会先将其标记为主观下线sdownsubjective down。通知Notification当被监控的Redis节点状态发生变化时哨兵通过Pub/Sub机制通知其他哨兵和客户端。自动故障转移Automatic Failover当足够多的哨兵满足quorum数量都认为master主观下线时master被标记为客观下线odownobjective down。此时哨兵集群会选举出一个leader哨兵由它执行故障转移流程从从节点中选出新的master让其余从节点复制新master并通知客户端更新配置。配置提供者Configuration Provider客户端在初始化时连接哨兵集群获取当前master的地址。当master发生切换后哨兵会通知客户端连接新的master。这里有一个非常关键的点主观下线和客观下线的区别。单个哨兵发现自己与master之间的心跳超时只能说明这个哨兵自己联系不上master了可能是master真挂了也可能是网络分区导致这个哨兵与master隔离。如果只有这个哨兵认为master不可用就立刻触发故障转移很容易因为网络抖动产生误判。因此引出了客观下线的概念当至少quorum个哨兵都认为master主观下线时才会真正触发故障转移流程。---------------- PING ---------------- | Sentinel 1 | ----------- | Master | ---------------- ---------------- ---------------- PING ---------------- | Sentinel 2 | ----------- | Master | ---------------- ---------------- ---------------- PING ---------------- | Sentinel 3 | ----------- | Master | ---------------- ----------------所有哨兵都独立监测master并通过Pub/Sub频道互相沟通判断。这种设计保证了单个哨兵的误判不会导致整个集群做错误的切换。1.3 方案选型为什么不用Cluster在动手部署之前还有一个绕不开的问题既然Redis Cluster也能实现高可用和分片为什么还要用哨兵我的建议是两者解决的场景不同不要混为一谈。Redis Cluster主要解决的是数据量超出单机内存时的水平扩展问题。它通过哈希槽hash slot把数据分散到多个主节点每个主节点有若干从节点当某个主节点挂掉后对应的从节点自动晋升。但Cluster架构的代价是客户端实现更复杂而且多key操作在跨槽位时会受到限制需要用户自己处理hash tag运维的复杂性也更高。哨兵集群解决的是单个master的可用性问题架构简单、数据完整保留在同一份逻辑空间里客户端只需连哨兵获取master地址一体化程度高。如果业务数据量不大单机内存能装下但需要高可用和自动故障转移哨兵是性价比更高的选择。从我的实际经验看很多团队在初期数据量不大时直接上Cluster结果发现多key事务、Lua脚本跨槽位限制一大堆反而把业务复杂度抬高了。正确的姿势是先判断容量需求单机Redis能搞定就上哨兵搞不定再考虑Cluster。2. 哨兵机制的核心细节与原理2.1 三个定时任务哨兵的心脏哨兵的工作不是被动的它通过三个定时任务维持对集群状态的感知第一个是每10秒一次的INFO任务。每个哨兵每10秒会向架构中的master和slave发送INFO命令用来获取最新的主从拓扑信息。比如当前master挂了、某台slave被提升为master后哨兵通过INFO感知到新的拓扑关系然后更新自己的配置缓存。第二个是每2秒一次的发布订阅任务。哨兵通过Redis的Pub/Sub频道__sentinel__:hello交换彼此的IP、端口、运行ID以及对自己所监控的master的判断结果。这个频道让所有哨兵形成一个松耦合的通信网络任何哨兵都不需要把所有其他哨兵地址提前配置好只要连接同一个master就能通过hello频道互相发现。第三个是每1秒一次的PING任务。每个哨兵每秒向所有已知的Redis节点master、slave、其他哨兵发送PING用来检测节点是否在线。主观下线状态就是基于这个PING的响应情况来判断的。这三个定时任务保证了监控的实时性、拓扑信息的一致性和哨兵之间的相互感知是哨兵机制正常工作的基石。2.2 客观下线与quorum玩法的门道前面提到sentinel monitor命令中有一个quorum参数比如配置里写的是sentinel monitor mymaster 127.0.0.1 6379 2这个2就是quorum代表触发客观下线需要的最小哨兵确认数。在3个哨兵的集群里2的意思是最少要两个哨兵都认为master主观下线才会把master标记为客观下线并启动故障转移流程。值得注意的是quorum并不是必须等于哨兵数量的一半以上它可以是2也可以是1但设置过低会增大误判风险。比如quorum1意味着只要任一个哨兵联系不上master就立刻触发切换网络抖动很容易造成频繁切换对业务影响反而更大。故障转移的执行还需要另一个条件在当前配置纪元configuration epoch内哨兵必须以多数派majority的身份获得投票支持。这里有quorum和majority两个概念容易混淆quorum用于判定master客观下线它可以在配置中灵活调整比如3个哨兵配2、5个哨兵配3。majority用于决定哪个哨兵来主导执行故障转移它由当前存活的哨兵总数决定必须超过一半。比如3个哨兵的集群中有一个挂了剩下的2个中必须有2个投票一致超过1个的半数是2才能当选leader执行切换。换句话说3个哨兵能容忍1个哨兵宕机4个哨兵理论上也能容忍1个但如果挂了2个剩下的2个虽然构成半数但在某些极端情况下可能无法选出leader。官方推荐使用奇数个哨兵就是出于这个考虑。2.3 哨兵leader选举和Raft协议客观下线只是宣布master“死了”真正执行故障转移的哨兵还需要被选举出来。这里的选举机制借鉴了Raft协议的核心思想逻辑非常精巧。当哨兵A发现master达到客观下线条件后它向其他哨兵发送SENTINEL is-master-down-by-addr命令请求对方投票给自己成为故障转移的执行者。每个哨兵在同一个配置纪元内只能投一票遵从先到先得原则。如果某个哨兵获得了半数以上majority的投票它就当选为leader负责执行故障转移流程。这套机制防止了多个哨兵同时发起切换导致的数据混乱。比如两个哨兵同时把不同从节点提升为master就会产生两个master的情况这在Redis哨兵架构中是被严格禁止的。竞选出leader后只能由leader操作拓扑变更其他哨兵配合更新配置。这里我建议不要被Raft这个名词吓倒。你不需要完整实现Raft只需要理解“多数派投票 配置纪元”这个核心思想就能解释哨兵集群在各种故障场景下的行为。2.4 主节点选举规则从节点晋升的优先序leader哨兵确定后接下来就是从节点中选新master的问题。选举规则是按照优先级降序筛选过滤掉处于断开状态的从节点。过滤掉最近down-after-milliseconds毫秒内没有响应过INFO命令的从节点可以理解为近期与哨兵失联过的节点。过滤掉与master断连时间超过down-after-milliseconds * 10毫秒的从节点。这个规则是为了防止选出一个数据落后太多的从节点——它和master断连太久提升后数据丢失严重不如不选。筛选完成后在剩余候选节点中按以下顺序比较优先级slave-priority可以在配置中设置数值越小优先级越高0表示永远不提升。复制偏移量offset越接近master丢的数据越少优先选择。运行IDrun id字典序小的优先纯粹为了打破平局。这里有一个很重要的实操点slave-priority这个参数平时很容易被忽略但它是规划故障转移时最有效的控制手段。比如某个从节点所在机器CPU更强、内存更大或者所在机房距离客户端更近你可以把它的slave-priority设为1其他从节点设为2这样故障转移时会优先提升它。2.5 脑裂问题的深层理解与预防哨兵架构中有一个比较隐蔽但后果严重的风险——脑裂split-brain。简单说在master故障切换过程中如果旧的master并没有真正宕机而是因为网络分区与哨兵失联那么哨兵可能已经把某个从节点提升为新的master但旧的master在网络恢复后仍然存活这时集群中短暂存在两个master客户端写入的数据就会分散到两台机器上。如果旧的master在分区结束后发现自己已经变成从节点它会尝试复制新master并清空自己身上在分区期间写入的新数据。这个过程对业务来说是灾难性的分区期间写入旧master的数据全部丢失。Redis提供了两个配置项来缓解这个问题min-replicas-to-write 1 min-replicas-max-lag 10含义是如果当前master连接的从节点数量少于1个或者从节点的延迟超过10秒master就拒绝写入。这样在网络分区发生时旧的master失去多数从节点的连接很快会触发拒绝写入避免脑裂期间产生不可同步的新数据。当然这两个配置本质上是在可用性和数据一致性之间做取舍。配置min-replicas-to-write 1意味着即使只有一个从节点在线master也正常提供服务如果追求更高的数据安全性可以调大到2但相应的单从节点故障时会直接导致master拒绝写入业务可用性受影响。需要根据业务对数据丢失的容忍度权衡。3. 一主两从三哨兵部署实操3.1 环境准备与架构规划我的实验环境是三台Ubuntu 22.04的虚拟机每台2核4G内存。实际生产环境建议物理隔离部署每台机器放一个Redis实例和一个Sentinel实例这样即使一台机器完全宕机哨兵数量和Redis节点数量各减一集群仍然可用。版本我选的Redis 7.0.x。7.0相比6.x在内存效率、命令延迟方面都有优化而且官方维护期更长。下载编译安装的过程很简单wget https://download.redis.io/releases/redis-7.0.12.tar.gz tar xzf redis-7.0.12.tar.gz cd redis-7.0.12 make -j4 make install PREFIX/usr/local/redis编译安装完二进制文件在/usr/local/redis/bin下包含redis-server、redis-sentinel、redis-cli等。为了方便管理我建议创建独立的运行用户和目录useradd -r -s /sbin/nologin redis mkdir -p /data/redis/{6379,6380,6381} mkdir -p /data/redis/sentinel/{26379,26380,26381} mkdir -p /var/log/redis chown -R redis:redis /data/redis /var/log/redis下面是节点规划表我建议你按照这个表格准备配置文件后面所有操作都围绕这个拓扑展开角色节点IPRedis服务端口Sentinel端口数据目录master192.168.56.101637926379/data/redis/6379slave1192.168.56.102637926379/data/redis/6379slave2192.168.56.103637926379/data/redis/6379看到这个表格你可能会疑惑为什么每个机器上Redis端口和Sentinel端口都是6379和26379这样安排完全没问题因为Redis和Sentinel进程分别在各自机器上占用这些端口互不冲突。三台机器都是相同的端口配置更易于批量管理和自动化脚本处理。当然如果你只有两台机器或者想在同机部署端口规划就需要改变后面我会单独说明。3.2 Redis主从节点配置详解先配置master节点也就是192.168.56.101上的Redis。修改/data/redis/6379/redis.conf关键配置如下# 绑定IP生产环境务必改为实际网卡IP不要用127.0.0.1 bind 0.0.0.0 # 启用保护模式。如果设置了密码或者绑定非本机地址建议开启 protected-mode yes # Redis服务端口 port 6379 # 后台运行 daemonize yes # PID文件和日志文件位置 pidfile /var/run/redis_6379.pid logfile /var/log/redis/redis_6379.log # 数据持久化目录 dir /data/redis/6379 # 设置访问密码生产环境必须配置 requirepass Redis2024 # 设置从节点访问master的认证密码 masterauth Redis2024这里requirepass和masterauth是两回事requirepass用来认证客户端访问本节点的请求masterauth用来让本节点作为从节点连接master时进行认证。实际配置中两个都要设置因为master在故障转移后可能变成从节点需要提前准备好认证信息。然后是配置两个从节点。slave1192.168.56.102上的/data/redis/6379/redis.conf和master的大部分配置一致只需要额外增加一行# 指定master节点的IP和端口host为master的IPport为master的端口 replicaof 192.168.56.101 6379有些老教程里写的是slaveofRedis 5之后统一改成了replicaof两者含义相同但新版本建议用replicaof。slave2192.168.56.103同样加上这一行指向master的IP和端口。重点说一下protected-mode这个参数。很多人第一次搭哨兵时Redis能从本机连上但远程访问时总被拒绝看日志提示DENIED Redis is running in protected mode原因就是bind 127.0.0.1加protected-mode yes的组合Redis默认只允许本机回环地址访问。如果你设置了bind为实际IP并且配置了requirepass密码保护模式不会拦截连接。但如果你只是临时测试把protected-mode设为no也可以生产环境不建议。配置完启动三个Redis服务# master上执行 redis-server /data/redis/6379/redis.conf # slave1和slave2上分别执行 redis-server /data/redis/6379/redis.conf启动后验证主从关系在master上执行redis-cli -a Redis2024 info replication输出应该包含# Replication role:master connected_slaves:2 slave0:ip192.168.56.102,port6379,stateonline,offsetxxx,lag1 slave1:ip192.168.56.103,port6379,stateonline,offsetxxx,lag1看到两个从节点都是online状态主从复制链路就通了。这里我建议你在master上写几条测试数据然后在从节点上执行redis-cli -a Redis2024 get testkey验证数据是否同步确认无误后再进入哨兵配置环节。3.3 哨兵配置monitor参数与常用选项哨兵的配置文件路径通常是/etc/redis/sentinel.conf或我们之前规划的目录。每台机器上的Sentinel配置大体相同唯一区别是sentinel monitor这行指定的master地址。为了保证可维护性我建议三台机器上的Sentinel配置尽量保持一致。看一份完整的Sentinel配置以master机器192.168.56.101上的/data/redis/sentinel/26379/sentinel.conf为例# 哨兵监听端口 port 26379 # 后台运行 daemonize yes # 日志和PID文件 pidfile /var/run/redis-sentinel_26379.pid logfile /var/log/redis/sentinel_26379.log # 工作目录 dir /data/redis/sentinel/26379 # 监控的master信息 # sentinel monitor master-group-name ip port quorum sentinel monitor mymaster 192.168.56.101 6379 2 # 主观下线判断时间master在10秒内没有响应PING即判定为主观下线 sentinel down-after-milliseconds mymaster 10000 # 故障转移超时时间3分钟 sentinel failover-timeout mymaster 180000 # 故障转移后同时向新master发起同步的从节点数量 sentinel parallel-syncs mymaster 1 # 如果Redis配置了密码此处需要配置Sentinel连接Redis的密码 sentinel auth-pass mymaster Redis2024这里每行参数都值得展开说。sentinel monitor mymaster 192.168.56.101 6379 2是哨兵的核心配置。mymaster是这个master组的逻辑名称可以自定义但所有哨兵必须保持一致。192.168.56.101 6379是master的初始地址。注意如果master发生故障转移Sentinel会自动更新这个配置项为新master的地址并重写配置文件。quorum配置中最后的2的含义前面已经讲过最少需要多少个哨兵确认master主观下线才触发客观下线和故障转移。3个哨兵配2是标准做法。down-after-milliseconds设多长需要根据网络状况权衡。设太短——比如1000——在网络抖动的情况下很容易触发误判频繁切换反而影响业务设太长——比如60000——意味着master宕机后业务要等1分钟才能恢复。我的建议是本地网络环境设置10000跨机房环境适当上调到15000或20000。这个参数不是死的生产环境要通过演练找到适合自己网络延迟的值。parallel-syncs控制的是故障转移完成后同时向新master发起全量同步的从节点数量。设置为1的意思是逐台从节点进行同步避免多台从节点同时触发全量同步对master造成过大压力。如果从节点数量少、数据量也不大调成3甚至更大也不会造成太大问题但保险起见我建议保持1。三台机器的sentinel.conf内容是完全一样的。等等这里有一个细节要注意配置里sentinel monitor mymaster 192.168.56.101 6379 2中的IP是master的初始IP。在从节点和哨兵同机的场景下这个IP不会变因为故障转移后哨兵会自动更新配置文件。所以三份配置保持相同是完全没问题的。启动三个哨兵# 三台机器分别执行 redis-sentinel /data/redis/sentinel/26379/sentinel.conf启动后验证哨兵集群状态在任意一台机器上执行redis-cli -p 26379 sentinel master mymaster输出中应该包含当前master的IP和端口以及从节点信息1) name 2) mymaster 3) ip 4) 192.168.56.101 5) port 6) 6379 ...再执行redis-cli -p 26379 sentinel replicas mymaster redis-cli -p 26379 sentinel sentinels mymaster分别能查到两个从节点和另外两个哨兵的信息说明一主两从三哨兵的架构已经生效。3.4 启动顺序与各节点启动脚本关于启动顺序我的建议是固定的三步走先启动所有Redis节点确保主从同步正常。顺序上先master后slave。再启动所有哨兵节点。三个哨兵的启动顺序没有严格要求但建议一个一个起观察每个哨兵是否正常加入__sentinel__:hello频道。最后做验证检查。这个顺序背后的逻辑是哨兵启动后需要立即从master获取拓扑信息如果master还没起哨兵会进入待机状态虽然最终也能恢复但日志里会大量刷连接错误影响排查问题的效率。为了方便以后管理我建议写一个启动脚本来统一处理这些事。脚本内容很简单#!/bin/bash # Redis哨兵集群启动脚本 # 用法: ./start-all.sh [redis|sentinel|all] case $1 in redis) for port in 6379; do redis-server /data/redis/${port}/redis.conf done ;; sentinel) for port in 26379; do redis-sentinel /data/redis/sentinel/${port}/sentinel.conf done ;; all) for port in 6379; do redis-server /data/redis/${port}/redis.conf done sleep 2 for port in 26379; do redis-sentinel /data/redis/sentinel/${port}/sentinel.conf done ;; *) echo Usage: $0 {redis|sentinel|all} exit 1 ;; esac同样的脚本放在三台机器上各机器执行需要启动的部分。这里我踩过的坑是在阿里云ECS等云主机上Sentinel配置文件如果被修改过权限比如chmod 777redis-sentinel启动时会报*** FATAL CONFIG FILE ERROR (Redis 7.0.12) ***的错原因是Sentinel会实时改写配置文件必须确保运行用户对配置文件有写权限。3.5 故障转移演示kill掉master观察自动切换部署完成不代表万事大吉我强烈建议做一次故障转移演练验证哨兵集群真的能如预期工作。先在master192.168.56.101上写入一个测试keyredis-cli -a Redis2024 set testkey before-failover然后模拟master宕机在master机器上直接kill掉Redis进程# 找到Redis进程 ps -ef | grep redis-server | grep 6379 # 杀掉进程 kill -9 pid此时观察哨兵日志tail -f /var/log/redis/sentinel_26379.log你会看到一系列状态变化sdown master mymaster 192.168.56.101 6379 odown master mymaster 192.168.56.101 6379 #quorum 2/2 try-failover master mymaster 192.168.56.101 6379 failover-state-select-slave master mymaster 192.168.56.101 6379 selected-slave slave 192.168.56.102:6379 192.168.56.102 6379 failover-state-send-slaveof-noone slave 192.168.56.102:6379 192.168.56.102 6379 failover-state-wait-promotion-slave slave 192.168.56.102:6379 192.168.56.102 6379 promoted-slave slave 192.168.56.102:6379 192.168.56.102 6379 failover-state-reconf-slaves master mymaster 192.168.56.101 6379 slave-reconf-sent slave 192.168.56.103:6379 192.168.56.103 6379 slave-reconf-inprog slave 192.168.56.103:6379 192.168.56.103 6379 slave-reconf-done slave 192.168.56.103:6379 192.168.56.103 6379 failover-end master mymaster 192.168.56.101 6379 switch-master mymaster 192.168.56.101 6379 192.168.56.102 6379这段日志完整展示了故障转移的流程。关键事件是switch-master它表示master已经从192.168.56.101切换到了192.168.56.102。整个过程从sdown到switch-master大约耗时十几秒主要取决于down-after-milliseconds的配置。切换完成后验证一下数据# 在新的master上执行 redis-cli -a Redis2024 get testkey应该返回before-failover说明数据完整地转移到了新master上。如果你在slave上执行同样的命令会发现步骤中slave2从新master同步到了这条数据。这时候查看各节点的角色# 在新master192.168.56.102上 redis-cli -a Redis2024 info replication # role:masterslave1是192.168.56.103 # 在旧master192.168.56.101上 redis-cli -a Redis2024 info replication # 重启后会发现角色已经变成slave并且replicaof新master看到旧master在重启后自动变成了新master的从节点这一点很重要。因为哨兵集群在切换后会把旧master的信息更新到配置中所以旧master一旦恢复上线会被要求复制新的master它的数据会自动对齐。如果你发现故障转移后数据丢失了优先检查从节点的复制偏移量。日志中selected-slave slave 192.168.56.102:6379这一步Sentinel其实已经比较过所有从节点的复制偏移量选择偏移量最接近旧master的从节点晋升。但如果你手动调整过优先级就可能选出一个偏移量较小的节点数据可能不是最新的。3.6 通过Docker部署哨兵集群的注意事项如果你不想在三台物理机或虚拟机上部署也可以使用Docker Compose在一台机器上模拟全套架构。Docker方式的好处是能快速拉起环境做测试和演练坏处是网络模型和容器生命周期管理给哨兵部署增加了一些细节坑。一个常见问题是Sentinel在容器里默认使用容器内IP作为自己的地址如果这个IP在宿主机网络之外不可达其他Sentinel就无法和它通信。解决办法是给Sentinel容器配置network_mode: host或者指定sentinel announce-ip为宿主机的实际IP。另一个问题是如果只使用Docker的默认bridge网络容器重启后IP会变化Sentinel之间依赖IP的通信会紊乱。建议使用Docker Compose时固定网络或使用自定义网络并关闭自动分配IP配合container_name使用更稳。Docker Compose的编写思路version: 3.8 services: redis-master: image: redis:7.0.12 container_name: redis-master restart: always network_mode: host command: [redis-server, --port, 6379, --requirepass, Redis2024, --masterauth, Redis2024, --appendonly, yes] redis-slave1: image: redis:7.0.12 container_name: redis-slave1 restart: always network_mode: host command: [redis-server, --port, 6380, --replicaof, 127.0.0.1, 6379, --requirepass, Redis2024, --masterauth, Redis2024, --appendonly, yes] redis-slave2: image: redis:7.0.12 container_name: redis-slave2 restart: always network_mode: host command: [redis-server, --port, 6381, --replicaof, 127.0.0.1, 6379, --requirepass, Redis2024, --masterauth, Redis2024, --appendonly, yes] sentinel1: image: redis:7.0.12 container_name: sentinel1 restart: always network_mode: host command: [redis-sentinel, /etc/redis/sentinel.conf] volumes: - ./sentinel1.conf:/etc/redis/sentinel.conf上面这种方式用network_mode: host把容器网络直接绑定到宿主机哨兵报告给其他哨兵的地址就是宿主机的实际IP避免了很多容器网络的坑。相应的Docker容器内redis的端口就变成宿主机端口了如果你本机已经装了Redis需要调整端口规划比如Redis用6379、6380、6381Sentinel用26379、26380、26381。4. 常见问题排障与配置细节4.1 主观下线但始终无法客观下线现象是某个Sentinel日志显示sdown master mymaster但一直停留在sdown状态迟迟没进入odown。这个问题通常出在sentinel monitor配置的quorum值上。如果quorum设置为2但实际只有1个Sentinel对这个master做了sdown判断就无法满足客观下线条件。另一个常见原因是Sentinel之间网络不通。比如E CS安全组只放行了6379端口没放行26379端口那么Sentinel之间无法通过__sentinel__:hello频道交流心跳和投票信息所有Sentinel都各自为政永远凑不够quorum。排查时检查每个Sentinel能否通过redis-cli -h 其他哨兵IP -p 26379 ping返回PONG说明Sentinel间网络正常。还有一个原因是Sentinel配置的master IP端口不正确。如果你在配置里写的是sentinel monitor mymaster 127.0.0.1 6379 2而Sentinel部署在其他机器上它连的127.0.0.1是它自己永远连不上master自然无法参与后续判断。配置里必须写master在网络上可访问的实际IP。4.2 故障转移后客户端无法连接新master这种情况多发生在客户端没有使用哨兵机制连接Redis而是直接配置了master的地址。当master切换后客户端还在连旧master自然会连接失败。正确做法是客户端通过哨兵获取master地址。以Java生态中最常用的Lettuce为例RedisURI sentinelUri RedisURI.sentinel(192.168.56.101, 26379, mymaster) .withPassword(Redis2024); RedisClient client RedisClient.create(sentinelUri);使用Spring Boot时配置更简单spring: data: redis: sentinel: master: mymaster nodes: - 192.168.56.101:26379 - 192.168.56.102:26379 - 192.168.56.103:26379 password: Redis2024Spring Data Redis内部会自动通过Sentinel节点发现当前master并与之建立连接当master切换时也会自动感知并重连。4.3 哨兵日志报错-denied或权限不足Sentinel在运行期间需要写入自己的配置文件来记录拓扑变化如果配置文件的属主是root而Sentinel进程以redis用户运行就会报权限错误故障转移也无法正常记录。我在生产环境遇到过的报错是*** FATAL CONFIG FILE ERROR (Redis 7.0.12) *** Reading the configuration file, at line xx sentinel monitor mymaster 192.168.56.101 6379 2 Cant open file /data/redis/sentinel/26379/sentinel.conf for writing解决办法是确认文件权限chown redis:redis /data/redis/sentinel/26379/sentinel.conf chmod 644 /data/redis/sentinel/26379/sentinel.conf另外如果配置了protected-mode yesRedis Sentinel默认只允许本机连接远程客户端执行SENTINEL命令会报错。如果你需要远程查看Sentinel状态需要在配置里加上bind 0.0.0.0并考虑设置Requirepass密码或者用protected-mode no关闭保护模式生产环境不推荐。4.4 down-after-milliseconds设置太短的教训我有一次在测试环境把down-after-milliseconds设成了3000结果某次网络设备更新导致所有节点之间的连接同时中断了3秒左右哨兵立刻把master标记为下线并触发故障转移但network恢复后旧master其实没宕机集群里出现了短暂的双master现象。虽然最终靠min-replicas-to-write和哨兵的配置收敛机制恢复了但那次事件给我上了一课哨兵的参数要结合网络真实延迟来设置不能只看数值越小恢复越快。PING是每秒一次网络闪断如果只是1秒内其实不至于触发sdown但3秒就能连续丢3个心跳。在本地网络带宽稳定、RTT通常在1ms以内的环境down-after-milliseconds设10000是合理的但如果你在跨机房或者公网环境部署建议先做一次网络抖动测试再决定这个值。4.5 遇到问题时的排查命令汇总需要快速定位问题时以下命令能帮你快速判断目前集群状态命令用途redis-cli -h ip -p 26379 sentinel master mymaster查看当前master地址redis-cli -h ip -p 26379 sentinel replicas mymaster查看从节点列表及状态redis-cli -h ip -p 26379 sentinel sentinels mymaster查看其他哨兵信息redis-cli -h ip -p 26379 info sentinel查看哨兵整体状态摘要redis-cli -a pass -h ip -p 6379 info replication查看Redis节点主从角色和复制状态tail -f /var/log/redis/sentinel_26379.log实时追踪哨兵判断和切换日志这些命令组合基本上能覆盖大多数哨兵集群问题的排查需求。最关键的是养成看日志的习惯Sentinel日志已经按事件标记了状态变化比猜配置快得多。4.6 客户端侧集成与高可用验证完成集群部署后建议在客户端写一段简单的验证程序确认通过哨兵获取的master地址是正确的。我用Spring Boot做了个简单测试。启动应用后在日志里能看到类似这样的输出Starting SentinelConnection... SentinelConnection established with 192.168.56.101:26379 Redis master discovered: 192.168.56.101:6379然后做一个实验往Redis写一个key再kill掉master观察应用的自动重连行为。正常情况下故障转移完成后日志里会输出Redis connection to 192.168.56.101:6379 lost, attempting reconnect... Redis master switched to: 192.168.56.102:6379如果你的应用没有自动重连能力就需要依赖客户端连接池的配置了。Redis的Jedis和Lettuce都内置了故障转移感知功能但需要正确配置否则故障转移后连接池里还是旧master的连接。4.7 真实场景中的扩展思考哨兵集群解决了单点故障但它不解决容量问题。随着业务增长单master的内存和写能力会成为瓶颈。这时候可以分两步走第一步是给master挂更多的从节点把读流量分摊得更细第二步是当单机内存快撑不住时把数据按业务拆分到多个哨兵集群每个集群服务自己的业务线。关于持久化建议哨兵集群的Redis节点开启AOF并且设置为appendfsync everysec这样在故障转移时最多丢失1秒的数据配合min-replicas-to-write等配置能在可用性和数据安全性之间取得较好的平衡。最后一个建议部署完后一定要做定期演练。我见过不少团队配置完哨兵集群后觉得很安全结果半年后第一次真实故障时发现Sentinel配置文件权限早就被某次上线脚本改了、或者auth-pass密码过期导致故障转移完全没触发。高可用系统最重要的是持续验证而不是部署那天证明它能用。我个人的体会是哨兵集群本身并不复杂但它的价值完全体现在细节上。参数调优、权限管理、网络规划、客户端适配任何一个环节掉链子都可能让这个“高可用”名存实亡。按照上面的步骤搭建并完成一次完整的故障转移演练你对哨兵机制的理解会上升一个层次面试中涉及Redis高可用的问题也能从容应对。