资讯动态

Redis哨兵机制详解:从主从复制到高可用故障转移

发布时间:2026/8/30 6:18:14 来源:尧图企业网站定制
1. 从主从复制聊到哨兵先把问题想清楚我接触Redis的时间不算短了最早做缓存架构的时候脑子里只有单机Redis后来业务量上来单机扛不住读压力才开始认真搞主从复制。主从复制这一关迈过去之后很多人会觉得“OK我有一主两从读写分离数据有备份总该稳了吧”。但真正跑过生产环境的人都知道主从复制只是解决了数据冗余和读扩展的问题它完全没有解决一个最致命的场景主节点挂了怎么办。主节点一宕机整个写入链路直接瘫痪从节点虽然还活着但它们是只读的业务层的写入请求全部超时。更尴尬的是从节点手里拿到的数据可能比主节点落后它们也根本不会主动接管主节点的角色。这时候如果值班同学半夜被报警叫醒手动执行SLAVEOF NO ONE把某个从节点提升为主节点再让其他从节点重新指向新主这个过程快则几分钟慢则半小时对线上业务来说就是一次事故。哨兵机制Sentinel就是冲着这个痛点去的。它是Redis官方提供的高可用解决方案核心能力就三件事监控、通知、自动故障转移。哨兵会持续盯着所有主从节点的健康状况一旦发现主节点真正挂了它会自动在从节点里选一个新的主节点把其他从节点重新指向新主整个过程对业务侧尽量做到无感知。说直白一点主从复制解决了“数据怎么多存几份”的问题哨兵解决的是“主节点没了谁来顶替”的问题。这篇文章我会把哨兵机制从原理到配置、从故障转移到实战排障完整拆一遍内容包括哨兵集群为什么至少要三个节点、主观下线和客观下线到底怎么回事、Raft选举在故障转移里扮演什么角色、quorum和majority这两个数怎么理解、生产环境的配置怎么给才合理以及一套可以本地复现的故障转移演练流程。适合刚接触Redis高可用的人也给已经在用哨兵但遇到过头疼问题的人一些排查思路。2. 哨兵集群的架构与核心机制拆解2.1 哨兵到底在扮演什么角色先明确一点哨兵本身也是一个Redis实例只不过它运行在特殊模式下不存储业务数据专门负责管理其他Redis实例。一个完整的哨兵架构通常由三部分组成若干Redis数据节点一个主节点、多个从节点、若干哨兵节点至少三个以及业务侧使用的客户端或连接池。哨兵对外提供的能力可以概括为四个监控、通知、自动故障转移、配置提供者。监控就是持续检查主从节点是否存活通知是当被监控实例出现异常时哨兵可以通过API或脚本通知运维人员自动故障转移是核心主节点不可用时会自动执行主从切换配置提供者则是指客户端可以从哨兵那里获取当前主节点的地址这样即使发生了故障转移客户端也能自动连上新的主节点。这里有一个容易忽略的点哨兵同时是服务提供方和服务消费方。它把主从拓扑的信息发给客户端让客户端知道该往哪台机器写、往哪台机器读。客户端通过sentinel get-master-addr-by-name命令或者订阅哨兵的频道来获取最新主节点信息。所以哨兵不只是内部做故障转移它还会直接参与客户端的连接管理。没有这一层就算哨兵完成了切换客户端还死盯着旧主节点的IP那一切也是白搭。2.2 为什么哨兵至少要部署三个节点很多初学Redis的人第一次听到“哨兵至少要三个”都会觉得奇怪我一个主节点配一个哨兵不行吗一个哨兵盯一个主节点感觉挺合理的啊。但如果只有一个哨兵这玩意就成了单点故障——哨兵自己挂了整个高可用体系直接失效。所以哨兵至少要有两个以上。但两个也不行。这里涉及一个核心概念仲裁quorum。哨兵判断主节点是否真的下线需要多个哨兵达成一致意见这叫客观下线。如果只有两个哨兵假设其中一个哨兵因为网络分区跟其他节点失联了它独自判断主节点下线要执行故障转移但它联系不上另一个哨兵来确认这时候两个哨兵各执一词根本没有办法达成共识。更麻烦的是即使两个哨兵能通信如果其中一个哨兵宕机剩下一个哨兵就凑不齐法定人数故障转移一样无法执行。所以在生产环境里哨兵集群至少三个节点是硬性要求。用Raft算法的术语来说三个节点允许挂掉一个集群还能正常工作这叫多数派存活。为什么不用四个或五个当然可以但节点越多通信成本越高共识达成越慢三个是在高可用和复杂度之间的一个最佳平衡点。如果你是在本地或者测试环境做实验两个哨兵勉强也能跑但我强烈建议不要把这个习惯带到生产环境。三个哨兵是底线如果你的Redis主节点分布在多个机房哨兵应该跨机房部署避免整个机房网络故障时哨兵集体失联。2.3 哨兵的三把“定时巡逻”每个哨兵都在做什么每个哨兵节点内部维护着三个定时任务这三个任务就是哨兵感知整个集群状态的基础。理解这三个任务再去看主观下线、客观下线的判断逻辑就不会觉得云里雾里了。第一个定时任务每10秒可以配置向所有已知的主节点和从节点发送INFO命令。这个任务是用来刷新拓扑结构的。哨兵通过INFO命令拿到主节点下面的从节点列表、复制偏移量、角色信息等然后更新自己内存里的实例结构。这样即使后来有新的从节点加入哨兵也能自动发现不需要人工配置。10秒这个频率在大多数场景下够用如果集群节点变化频繁可以适当调小但不建议低于5秒否则会给主节点带来不必要的压力。第二个定时任务每2秒向哨兵自己的__sentinel__:hello频道发布一条消息同时订阅这个频道来接收其他哨兵的消息。消息内容包括哨兵自身的IP、端口、运行ID以及它当前监控的主节点信息。通过这个机制哨兵之间可以互相感知对方的存在并且能交换对主节点状态的判断。这是哨兵节点之间“通气”的通道也是后面客观下线判断的数据来源之一。我在实际排查问题的时候会专门用SUBSCRIBE __sentinel__:hello命令去看消息流确认哨兵之间是不是通信正常。第三个定时任务每1秒向所有已知的主节点、从节点和哨兵节点发送PING命令。这个任务是心跳探测用来判断实例是否存活。如果某个实例在配置的down-after-milliseconds时间内都没有回应PING哨兵就会把这个实例标记为“主观下线”。为什么叫主观因为这个判断只是当前这个哨兵自己的看法其他哨兵可能还认为这个实例是活的。这三个定时任务的设计其实非常巧妙1秒的心跳保证了故障发现的及时性2秒的频道通信保证了哨兵之间的信息同步10秒的INFO刷新保证了拓扑数据的准确性。每一层各司其职组合起来就是一套完整的高可用监控体系。3. 从主观下线到故障转移哨兵的核心决策链路3.1 主观下线与客观下线的边界先说主观下线Subjectively Down简称SDOWN。当某个哨兵在down-after-milliseconds时间内没有收到某个实例的PING响应时它会在本地把这个实例标记为SDOWN。注意这里说的是“某个哨兵”也就是说每个哨兵都有自己的判断彼此之间不直接互相影响。再说客观下线Objectively Down简称ODOWN。当某个哨兵认为主节点SDOWN之后它不会马上启动故障转移而是会通过SENTINEL is-master-down-by-addr命令询问其他哨兵“你觉得这个主节点挂了吗”如果其他哨兵也认为它挂了并且同意的主观下线数量达到了quorum值那么这个主节点就会被标记为ODOWN。只有从SDOWN升级到ODOWN才算是触发了故障转移的前提条件。我遇到过不少人在配置的时候把quorum理解成“必须所有哨兵都同意”这是一个误区。quorum是“最少需要几个哨兵同意”它配置在sentinel monitor master-name ip port quorum这一行里。比如三个哨兵quorum设置为2那就意味着至少两个哨兵都认为主节点挂了才判定客观下线。这里有一个值得反复琢磨的细节主节点被判定ODOWN之后故障转移的真正执行者并不是发起的那个哨兵而是由所有哨兵通过Raft算法选出来的一个领导者Leader。也就是说SDOWN判断是每个哨兵自己的事ODOWN是大家投票集体决策的事而故障转移的执行权则集中在选出的领导者手里。这三层逻辑分开每个环节的职责都很清晰。3.2 Raft选举哨兵们怎么选出那个“话事人”Redis哨兵的领导者选举机制借鉴了Raft算法但做了一些简化。整个过程可以用一句话概括当客观下线条件满足后每个哨兵都有资格成为领导者但它们要获得超过一半哨兵节点的投票才能胜出。具体流程是这样的哨兵A发现主节点ODOWN于是它给自己投一票然后向其他哨兵发送SENTINEL is-master-down-by-addr命令这个命令同时带有请求对方给自己投票的意图。其他哨兵收到这个请求后如果它们也认为主节点客观下线了而且在自己当前这一轮选举中还没有投过票就会把票投给请求方。选举成功的关键是获得超过一半的哨兵节点的选票。三个哨兵需要2票五个哨兵需要3票以此类推。这也是为什么三个哨兵只能容忍一个节点故障——如果两个哨兵挂了剩下一个哨兵即使自己投自己也只有1票达不到2票的多数派要求选举就无法完成。我在实际运维中观察到的现象是选举过程通常非常快毫秒级就能完成。因为哨兵之间的通信是通过2秒一次的频道消息和命令请求完成的网络正常情况下延迟很低。但如果你遇到故障转移迟迟不触发的情况其中一个常见原因就是选举超时——比如某些哨兵节点网络不稳定导致选举无法凑齐多数派。这个在后面的排查章节会专门展开。3.3 故障转移的完整动作拆解当哨兵领导者选出来之后真正的故障转移动作才正式开始。整个过程分为三步筛选从节点、选择最优从节点、执行角色切换。第一步是筛选。领导者会遍历所有从节点过滤掉那些不具备切换条件的节点。过滤规则有三条第一从节点必须在线如果它已经SDOWN了直接排除第二从节点最近一段时间内没有和主节点断连过如果主从之间经常掉线说明网络不稳定不适合接管第三从节点的复制偏移量不能太落后如果它比主节点落后太多数据选它当新主会导致大量数据丢失。第二步是选择最优从节点。在通过筛选的节点里领导者会按照一个优先级顺序来选先比较slave-priority配置项值越小优先级越高这个配置允许运维人员手动指定某台机器作为优先接管者如果priority相同再比较复制偏移量偏移量越大说明数据越新如果偏移量也一样再比较运行IDID越小的优先。这套排序规则保证了选出来的新主节点是数据最完整、最健康的那个。第三步是执行角色切换。领导者向选中的从节点发送SLAVEOF NO ONE命令让它停止复制关系升级为主节点。然后领导者会定期检查新主节点的状态确认它已经能正常处理写入。确认之后领导者会向其他从节点发送SLAVEOF 新主IP 新主端口命令让它们重新指向新的主节点。最后如果旧主节点恢复了领导者也会给它发送SLAVEOF命令让它变成新主的从节点。整个流程走完集群就完成了从故障到恢复的闭环。这里有一个重要的提示故障转移过程中新主节点的数据和旧主节点之间必然存在一个时间窗口的差异。因为哨兵从发现主节点宕机到完成切换中间有一段时间主节点是没有写入的所以新主节点一定比旧主节点少了一些数据。这是分布式系统里无法避免的代价业务侧需要通过幂等设计或者消息补偿机制来容忍这一部分数据丢失。哨兵机制追求的是高可用不是数据零丢失这个概念一定要分清。4. 生产环境哨兵配置参数怎么给才靠谱4.1 核心配置项逐行解析哨兵的配置主要写在一个sentinel.conf文件里下面我列出一份生产环境常用配置并逐行解释每个参数的含义和取值依据。port 26379 daemonize yes protected-mode no dir /var/lib/redis-sentinel sentinel monitor mymaster 192.168.1.100 6379 2 sentinel down-after-milliseconds mymaster 10000 sentinel failover-timeout mymaster 30000 sentinel parallel-syncs mymaster 1 sentinel auth-pass mymaster your_passwordport 26379是哨兵节点的监听端口默认就是26379一般不需要改。daemonize yes是让哨兵在后台运行避免挂在前台导致终端一关哨兵就停。protected-mode no要注意生产环境必须结合防火墙或者安全组来控制访问不能只依赖protected-mode。dir设置哨兵的工作目录哨兵会在这里写入一些状态文件比如sentinel.conf的自动更新版本。sentinel monitor这行是最核心的它指定了要监控的主节点名称、地址和quorum值。名称mymaster是自定义的同一个哨兵集群里多个主节点可以用不同名称区分地址是主节点的初始IP和端口最后的2就是quorum值前面已经详细讲过三个哨兵节点就填2。down-after-milliseconds是主观下线的判断窗口。我这里设置的10000毫秒意味着哨兵在10秒内没有收到PING响应就会主观认为实例挂了。这个值不能设得太小否则网络抖动会导致频繁误判也不能太大否则真故障时故障转移响应太慢。通常建议在5秒到15秒之间具体根据你业务的网络环境和SLA要求来定。failover-timeout是故障转移的超时时间。它涵盖了从判定主节点ODOWN到完成切换的所有操作的总时限。这个值设得太短会导致切换还没完成就超时重试设得太长会拖慢整体恢复速度。我一般建议设置在30秒左右如果你的从节点数量多或者网络环境复杂可以适当放宽到60秒。parallel-syncs是指故障转移完成后同时有多少个从节点向新主节点发起同步。如果设置为1所有从节点会排队一个一个同步设置为更大的值多个从节点同时同步整体恢复更快但对新主节点的网络和CPU压力更大。稳妥起见生产环境设置为1避免新主节点短时间内承受过多SLAVEOF同步请求。auth-pass用来配置主节点的认证密码。如果你的Redis开启了requirepass哨兵和从节点同步都需要这个密码。注意哨兵本身也需要配置自己的访问密码通过sentinel auth-user、requirepass等参数控制。4.2 两个最容易踩坑的配置细节配置哨兵的过程中我见过最多的问题集中在两个细节上。第一个是sentinel monitor里的IP地址。这里填的应该是主节点的地址但问题是在主从切换之后这个地址会被哨兵自动改写为当前新主节点的地址。也就是说哨兵会自动维护这份配置。如果你在配置里写的是域名而不是IP有些版本的哨兵在自动更新配置时会遇到解析问题导致故障转移后配置异常。稳妥做法是直接填IP而且保证这个IP是主节点和从节点之间互相可达的地址。另一个容易忽略的情况是如果主节点到从节点的复制是通过内网IP但哨兵部署在另一个网段哨兵填的地址必须能通才行否则哨兵会一直认为主节点不可达。第二个细节是maxmemory和哨兵的关系。很多人会问哨兵本身是Redis实例它也有内存上限吗哨兵不存储业务数据理论上的内存占用很小但随着监控的实例数量增加以及INFO命令返回的数据累积哨兵的内存也会缓慢增长。虽然在大多数场景下几十上百个实例都不会让哨兵内存告急但我还是建议给哨兵设置一个合理的maxmemory和maxmemory-policy allkeys-lru避免极端情况下内存溢出导致哨兵进程崩溃。这个坑虽然不常见一旦踩到就是雪上加霜。还有一个配置层面的经验哨兵的配置文件要保证哨兵进程有权限写入。因为哨兵在运行过程中会动态修改sentinel.conf比如更新主节点地址、记录最近的故障转移信息。如果文件权限不对哨兵启动时会报错运行中更新配置也会失败。我遇到过把配置文件放在root目录下导致哨兵无法写入的案例排查了半天才发现是权限问题。5. 本地实操用Docker复现一次完整的故障转移5.1 环境准备与容器编排理论讲完如果不实际操作一遍总觉得不够踏实。下面我用Docker Compose在本地搭建一套最小的哨兵集群包括一个主节点、两个从节点、三个哨兵节点。这套环境完全可以在你自己的开发机上跑起来用来验证哨兵机制的全流程。先创建目录结构mkdir redis-sentinel-lab cd redis-sentinel-lab然后编写docker-compose.ymlversion: 3.8 services: redis-master: image: redis:7.0 container_name: redis-master command: [redis-server, --port, 6379, --appendonly, yes] networks: sentinel-net: ipv4_address: 172.20.0.10 redis-slave-1: image: redis:7.0 container_name: redis-slave-1 command: [redis-server, --port, 6379, --slaveof, redis-master, 6379, --appendonly, yes] depends_on: - redis-master networks: sentinel-net: ipv4_address: 172.20.0.11 redis-slave-2: image: redis:7.0 container_name: redis-slave-2 command: [redis-server, --port, 6379, --slaveof, redis-master, 6379, --appendonly, yes] depends_on: - redis-master networks: sentinel-net: ipv4_address: 172.20.0.12 sentinel-1: image: redis:7.0 container_name: sentinel-1 command: [redis-sentinel, /etc/sentinel/sentinel.conf] volumes: - ./sentinel-1.conf:/etc/sentinel/sentinel.conf depends_on: - redis-master - redis-slave-1 - redis-slave-2 networks: sentinel-net: ipv4_address: 172.20.0.21 sentinel-2: image: redis:7.0 container_name: sentinel-2 command: [redis-sentinel, /etc/sentinel/sentinel.conf] volumes: - ./sentinel-2.conf:/etc/sentinel/sentinel.conf depends_on: - redis-master - redis-slave-1 - redis-slave-2 networks: sentinel-net: ipv4_address: 172.20.0.22 sentinel-3: image: redis:7.0 container_name: sentinel-3 command: [redis-sentinel, /etc/sentinel/sentinel.conf] volumes: - ./sentinel-3.conf:/etc/sentinel/sentinel.conf depends_on: - redis-master - redis-slave-1 - redis-slave-2 networks: sentinel-net: ipv4_address: 172.20.0.23 networks: sentinel-net: driver: bridge ipam: config: - subnet: 172.20.0.0/24哨兵的配置文件需要准备三份内容基本一致端口各自不同。以sentinel-1.conf为例port 26379 dir /tmp sentinel monitor mymaster 172.20.0.10 6379 2 sentinel down-after-milliseconds mymaster 5000 sentinel failover-timeout mymaster 10000 sentinel parallel-syncs mymaster 1注意这里sentinel monitor里的IP我直接用了主节点的容器IP172.20.0.10。因为哨兵默认不允许使用127.0.0.1来监控外部实例且Docker容器之间需要用分配的静态IP通信。启动整个集群docker-compose up -d等所有容器都起来之后先验证主从状态docker exec redis-master redis-cli INFO replication应该能看到role:master并且有两个从节点。再验证哨兵是否正常监控docker exec sentinel-1 redis-cli -p 26379 sentinel master mymaster输出里应该有主节点的IP、端口、状态等信息。到这里一套最小高可用集群就起来了。5.2 模拟主节点宕机观察哨兵如何“接管”现在进入最刺激的环节——把主节点直接停掉看看哨兵集群会做出什么反应。docker stop redis-master停掉后我们立刻查看哨兵的日志docker logs sentinel-1 --tail 50你会看到类似这样的日志序列sdown master mymaster 172.20.0.10 6379 odown master mymaster 172.20.0.10 6379 #quorum 2/2 try-failover master mymaster 172.20.0.10 6379 vote-for-leader 8f7c9a1b... 1 electing-leader master mymaster 172.20.0.10 6379 failover-state-select-slave master mymaster 172.20.0.10 6379 selected-slave slave 172.20.0.12:6379 failover-state-send-slaveof-noone slave 172.20.0.12:6379 failover-state-wait-promote-slave slave 172.20.0.12:6379 promoted-slave slave 172.20.0.12:6379 failover-state-reconf-slaves master mymaster 172.20.0.10 6379 slave-reconf-sent slave 172.20.0.11:6379 slave-reconf-in-prog slave 172.20.0.11:6379 failover-end master mymaster 172.20.0.10 6379 switch-master mymaster 172.20.0.10 6379 172.20.0.12 6379这个日志序列值得逐行读一遍。sdown说明当前哨兵主观判断主节点下线了odown说明已经有quorum数量的哨兵达成一致客观下线vote-for-leader记录了自己投票给某个哨兵electing-leader进入选举流程selected-slave是选出了要提升的从节点promoted-slave确认新主节点提升成功switch-master表示整个切换完成主节点地址从172.20.0.10变成了172.20.0.12。从我的实测经验来看在down-after-milliseconds设置为5000毫秒、failover-timeout设置为10000毫秒的情况下从主节点停止到切换完成整个过程大约在5到10秒之间。这个时间窗口内业务会有短暂的写入失败但相比手动切换的分钟级恢复已经是质的提升。最后验证一下集群的新状态docker exec redis-slave-1 redis-cli INFO replication docker exec sentinel-1 redis-cli -p 26379 sentinel master mymaster你会发现从节点1的master指向了172.20.0.12而哨兵记录的新主节点也是172.20.0.12。如果我们把之前停掉的旧主节点重新启动docker start redis-master等待几秒后再看它的角色它会自动变成新主节点172.20.0.12的从节点。这就是哨兵机制自动完成的“收编”动作。5.3 手动重新选举与哨兵命令速查除了自动故障转移哨兵的运维命令也非常有用。日常维护中我经常用到下面这几个。查看当前所有主节点的状态redis-cli -p 26379 sentinel masters查看某个主节点的详细信息redis-cli -p 26379 sentinel master mymaster查看某个主节点下有哪些从节点redis-cli -p 26379 sentinel slaves mymaster查看所有哨兵节点redis-cli -p 26379 sentinel sentinels mymaster获取当前主节点地址这也是客户端连接哨兵时最常用的命令redis-cli -p 26379 sentinel get-master-addr-by-name mymaster手动触发一次故障转移。这个命令在灰度切换、机器维护等场景下非常有用。比如你打算对主节点所在机器做内核升级希望先手动把主节点切走避免维护窗口期的生产事故redis-cli -p 26379 sentinel failover mymaster执行完这个命令后哨兵会立刻执行一次故障转移流程即使主节点当前是健康的。注意手动failover和自动failover的区别在于手动failover不需要等客观下线条件满足只要哨兵集群本身是健康的立即执行。这个命令我用过很多次在计划内维护场景下非常顺手。还有一个命令用来检查哨兵的仲裁状态redis-cli -p 26379 sentinel ckquorum mymaster它会返回类似OK 3 usable Sentinels的结果告诉你当前有多少哨兵可用quorum是否能满足。每次部署完哨兵集群我建议先跑一遍这个命令确认仲裁可用再去配置客户端。6. 常见问题与排查技巧实录6.1 哨兵频繁误判主节点下线怎么办误判是哨兵运维里最常见的头疼问题。表现是主节点明明活着但哨兵日志里出现了sdown、odown的记录甚至触发了自动故障转移导致线上不必要的切换。误判的根本原因通常是两个方向。一是down-after-milliseconds设置得太小稍微一点网络抖动就会导致PING超时。排查的时候先看哨兵日志里sdown和odown的时间分布如果出现在网络高峰期或者机器负载高的时段大概率就是网络延迟或CPU饥饿导致的PING响应超时。解决办法是把down-after-milliseconds适当调大比如从5秒调到10秒或15秒。二是主节点所在的机器本身负载过高。Redis是单线程模型如果主节点同时承担了大量慢查询或大key操作主线程阻塞哨兵的PING请求无法及时响应也会触发误判。这种问题光调哨兵参数治标不治本需要从业务侧治理大key和慢查询。另外还要检查哨兵节点和主节点之间的网络质量。我遇到过因为防火墙规则变更导致哨兵到主节点之间的PING请求被丢弃的情况。用redis-cli -p 26379 ping手动测试每个哨兵和主节点之间的连通性是排查的第一步。6.2 故障转移触发了但没有切换成功这种情况更让人揪心哨兵日志里已经出现了try-failover但后续没有promoted-slave切换卡在中途或失败。原因主要集中在两个方面。第一个是选举失败。如果哨兵节点数量不足以凑齐多数派领导者选举就会一直无法完成。比如三个哨兵挂了一个剩下的两个都能互相通信按理说其中一个能拿到2票但如果那个挂掉的哨兵没有恢复选举就无法完成。排查方式是查看所有哨兵的日志看vote-for-leader是否出现如果一直没有选举成功需要检查哨兵节点之间的网络连通性。第二个原因是筛选从节点失败。failover-timeout超时前领导者会尝试筛选从节点如果所有从节点都因为复制偏移量落后太多被排除切换就会失败。这种情况通常发生在主节点和从节点的复制链路长期异常从节点数据严重滞后。排查方式是用INFO replication查看从节点的master_link_status和slave_repl_offset确认复制链路是否健康。还有一个小概率但很隐蔽的问题哨兵配置文件中的主节点地址和从节点实际地址不一致导致哨兵无法正确识别主从关系。比如你在sentinel monitor里写的是旧IP但Redis实例已经换了机器换了IP哨兵就会一直盯着一个不存在的地址故障转移自然无法正常工作。这种问题在云环境里特别容易踩到因为云主机重建后IP可能会变化。6.3 故障转移后客户端连不上新主节点这是非常典型的“切换成功但服务没有恢复”的场景。哨兵这边日志显示switch-master但业务侧依然报错写入失败。原因在于客户端没有正确使用哨兵机制。很多客户端框架比如Jedis、Lettuce、Redisson都提供了哨兵模式但你在配置的时候必须传入哨兵的地址而不是主节点的地址。以Jedis为例SetString sentinels new HashSet(); sentinels.add(172.20.0.21:26379); sentinels.add(172.20.0.22:26379); sentinels.add(172.20.0.23:26379); JedisSentinelPool pool new JedisSentinelPool(mymaster, sentinels);关键点在于mymaster要和sentinel monitor里配置的主节点名称一致。客户端连接的是哨兵池哨兵池会通过sentinel get-master-addr-by-name获取当前主节点地址并建立连接。如果客户端配置里写的是旧主的地址故障转移后连接肯定失效。还有一种情况是客户端连接池缓存了旧主节点的连接。即使哨兵池已经感知到新主节点如果业务代码里一直复用旧的Jedis实例写入还是走旧连接。解决办法是使用支持哨兵模式的连接池并确保在故障转移后能自动重建连接。这个问题我在一些老项目里见过代码里直接用new Jedis(旧主IP, 6379)写死了地址完全没有走哨兵这类问题只能通过改造代码解决哨兵再完善也救不了。6.4 哨兵运维问题速查表我把上面这些常见问题和排查思路整理成一个速查表方便你排障时对照。现象可能原因排查命令/思路解决方案哨兵日志出现sdown但主节点正常down-after-milliseconds过小检查主节点负载和网络延迟调大down-after-milliseconds故障转移卡在选举阶段哨兵节点数不足或网络分区sentinel ckquorum mymaster查看可用哨兵数保证至少3个哨兵且互相连通切换成功但选出的从节点数据偏旧主从复制延迟过大对比master_repl_offset和slave_repl_offset优化复制链路排查大key写操作客户端切换后写入失败客户端配置未走哨兵模式检查代码中Jedis实例创建方式改用JedisSentinelPool或Lettuce哨兵模式旧主恢复后没有自动变成从节点网络隔离导致的split-brain场景查看旧主日志中的角色信息通常会自动处理如异常可手动SLAVEOF哨兵内存持续增长监控实例过多且没有maxmemory限制查看哨兵进程RSS和INFO memory配置maxmemory和合适的内存策略6.5 防止脑裂的配置建议最后谈一个比较进阶的话题脑裂Split Brain。在哨兵场景下脑裂指的是因为网络分区旧主节点和其他节点失联但它本身还活着业务写入还在继续写入旧主哨兵这边检测到主节点不可达完成故障转移选出了新主。等网络恢复后旧主节点被降级为从节点但它分区间写入的数据会因为SLAVEOF同步被清空造成数据丢失。脑裂无法被完全避免但可以通过合理配置min-replicas-to-write和min-replicas-max-lag来降低影响。我给一个具体配置建议min-replicas-to-write 1 min-replicas-max-lag 10这个配置的含义是主节点在写数据之前至少要保证有1个从节点连接正常且从节点同步延迟不超过10秒。如果从节点数量少于1个或延迟超过10秒主节点会拒绝写入。这样在网络分区的情况下被隔离的旧主节点会因为缺少从节点确认而拒绝写入从而减少脑裂期间的数据写入量。这套配置不是银弹它会影响极端情况下的可用性。比如所有从节点都宕机了主节点也会拒绝写入因为min-replicas-to-write无法满足。所以具体值要根据业务对数据一致性和可用性的权衡来定。如果你对数据一致性要求很高可以设置min-replicas-to-write 1如果你更看重可用性可以保持默认配置0允许主节点在无从节点时继续写入。7. 最后再分享几条实战经验哨兵这套机制我前前后后在多个环境折腾过这里挑几条最实在的经验分享出来。第一件事不要把哨兵当成万能的高可用方案。如果你的业务对数据零丢失有硬性要求哨兵做不到你需要的是类似Redis Cluster或者引入消息队列做补偿而不是指望哨兵帮你兜底。在使用哨兵之前一定要和业务方同步清楚“故障转移期间可能会有少量数据丢失”这个事实。第二件事运维自动化里面一定要有哨兵自身的监控。哨兵集群是保障Redis高可用的基础设施但哨兵自己也会挂。我见过不少团队对Redis节点做了很细的监控但哨兵进程本身没有纳入监控体系哨兵全挂了三天才知道等于整个Redis集群是在裸奔。至少要监控哨兵进程是否存活、哨兵是否能正常响应命令、哨兵和主节点之间的PING是否正常。第三件事故障演练要定期做不能只在部署的时候验证一次。我建议每季度至少做一次主节点宕机的演练把从节点提升、客户端自动切换这些链路都跑一遍确认整个体系仍然有效。而且演练最好在业务低峰期做提前和业务方打好招呼免得造成恐慌。第四件事哨兵配置文件里的dir目录一定要留足磁盘空间。正常情况下哨兵的日志和状态文件不大但如果你开启了比较详细的日志级别长时间运行后日志文件还是会增长。我遇到过一次因为/var/log分区写满导致哨兵进程异常退出的情况当时排查了很久才发现是磁盘满了。哨兵机制的底层逻辑其实不复杂但生产环境里的问题往往出在配置不合理、网络异常、客户端接入方式不对这些细节上。把这篇文章里的内容消化透遇到哨兵相关的问题应该能少走不少弯路。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价