
我印象里凡是搞过Redis高可用的人基本都绕不开哨兵这关。Sentinel作为Redis官方的高可用方案本身设计得很巧妙但启动和配置的过程特别是第一次上手时能踩的坑真不少。这篇文章就是把我自己从“配置写错导致哨兵直接起不来”到“主从切换永远触发不了”这一路上的实际问题按主题拆开做成一份可以随时翻的避坑记录。如果你正在搭哨兵环境或者已经被哨兵折腾得没脾气这份内容应该能帮上忙。1. 哨兵机制与启动前的核心认知1.1 哨兵到底在解决什么问题先花点篇幅把哨兵的角色理顺。Redis哨兵的核心职责是监控、通知、自动故障转移和配置中心。一句话说就是当主节点挂掉时能自动从从节点里挑一台顶上对客户端来说不用手动改连接地址。这个概念不难理解但很多人首次上手时脑子里只有“哨兵可以自动切换”这个模糊印象忽略了它依赖的底层机制。我见过不少开发者直接照着别人博客抄一份sentinel.conf启动后发现主库下线了哨兵却没反应然后开始怀疑人生。问题很可能就出在down-after-milliseconds设得太长或者仲裁quorum值设得太随意。这里有个很关键的类比哨兵就像小区门口的保安他不是每时每刻都能发现异常而是按照固定频率去巡楼。sentinel monitor配置里指定的主节点名、IP、端口和仲裁数决定了他“盯哪栋楼、几个人一起说事才算出事”。哨兵之间是通过Redis的发布订阅机制互相通信的仲裁数quorum表示至少要有多少个哨兵认为主节点不可用才会触发主观下线到客观下线的状态转换。这里特别容易忽略的是quorum并不是你写多少就一定能达到多少它取决于你的哨兵实例总数。如果配置了3个哨兵quorum设为2那么两个哨兵达成一致才会切换如果只部署了1个哨兵quorum就算写了2也毫无意义。1.2 启动前必须理清的角色关系我踩的第一个坑就是把哨兵当成“一个独立工具”没有把它和Redis实例之间的角色关系理清楚。实际上哨兵节点和Redis节点是独立的进程但它们之间的配置是有联动的。比如你的主节点设置了密码那么哨兵配置里必须同步设置对应的认证信息。具体来说sentinel auth-pass master-name password是针对主节点的而requirepass和masterauth则分别对应Redis实例本身的访问密码和主从复制时的认证密码。真的有太多人在主库设置了requirepass却忘了在从库配masterauth结果主从复制日志里一直刷MASTER aborted replication。另外哨兵自己也是Redis实例这意味着它同样受protected-mode、bind等配置影响。默认情况下如果你的哨兵监听的是公网IP或0.0.0.0而且没有设置密码它可能会拒绝响应命令——这就是一启动“看起来活着但什么都没干”的经典原因之一。所以在动手改配置之前先把拓扑图画清楚哪台机器上跑主库哪台跑从库哪几台跑哨兵哪些实例需要密码主从复制的认证怎么传哨兵和客户端之间怎么交互。这个环节省了后面全是坑。2. 配置文件层面的高频踩坑点2.1 配置项名称与格式错误配置文件是踩坑的主战场。Redis的配置项都是小写字母中划线的形式哨兵特有的指令以sentinel开头。最常见的错误是大小写混用比如把Sentinel monitor写成了大写S进程直接拒绝启动。另外就是格式顺序问题。sentinel monitor master-name ip port quorum里master-name是你自己随便起的逻辑名但注意这个名字不能带空格最好也不要带中文。IP地址这一项在较新的Redis版本里支持主机名但为了稳定建议直接用IPv4地址。还有一个很容易踩的格式坑sentinel down-after-milliseconds master-name milliseconds。有些博客示例会写成sentinel down-after-milliseconds mymaster 30000这是对的。但如果你把数字里的单位字母也写进去比如30000ms启动时同样会报错。别笑我真的见过有人这么干然后查了半天才发现是单位后缀的问题。哨兵启动时会严格校验配置文件里的每一行任何一条格式不合法整个进程都可能起不来而且报错信息未必直接指向出错的那一行。所以修改配置文件后建议先在前台启动一遍不加载daemonize配置如果报错它会直接打印出哪一行有问题。2.2 密码与权限配置的“连锁反应”哨兵和Redis之间的密码认证关系是我见过最混乱的部分。核心逻辑是Redis实例自身的requirepass控制外部访问Redis的权限。从库如果要从主库同步数据需要配置masterauth否则复制建立不了。哨兵要接管主库必须配置sentinel auth-pass master-name password这个密码通常是主库的requirepass。哨兵之间互相通信时同样需要认证这靠的是Redis实例本身的requirepass或哨兵配置文件里的auth-pass。我实际遇到过一种情况主库和从库都设置了requirepass从库的masterauth也写了replication正常但哨兵一启动立刻在主库日志里看到NOAUTH Authentication required。原因就是哨兵配置里漏了sentinel auth-pass。它尝试向主节点发送INFO命令时被拒无法获取主节点状态自然也不会触发任何故障转移。还有一个冷门但致命的权限问题如果从库在启动时通过replicaof配置跟随主库而这个配置是在主库出现故障后才补上去的那么当主库恢复并变成从库时新提升的主库可能没有正确的masterauth配置导致复制链断裂。这类问题通常不会在启动那一刻暴露而是会在故障切换后突然冒出来。2.3 保护模式与绑定IP的深坑protected-mode是Redis 3.2以后引入的机制目的是防止默认配置下实例暴露到公网。它导致的启动问题往往是“哨兵看起来在运行但外部客户端连不上”。具体表现为哨兵日志正常没有报错但你从另一台机器执行redis-cli -p 26379 -a pass sentinel master mymaster返回(error) DENIED Redis is running in protected mode。这就是因为哨兵进程没有设置密码又监听了非本地回环地址于是它默认拒绝外部的非认证请求。解决的常规办法有两个一是给哨兵也设置requirepass但注意这会导致客户端和哨兵通信时也必须带密码所有连接调用都需要改二是在配置文件里显式加一行protected-mode no。从安全性角度讲我更建议加密码而不是关闭保护模式。但这里有个隐藏的坑当你在哨兵配置里写下sentinel auth-pass时其实它针对的是被监控的Redis主节点不是哨兵自己。哨兵进程自身的保护模式还是由自己的protected-mode决定。很多人在被保护模式拦截时误以为加上sentinel auth-pass就能解决结果根本没生效。3. 启动过程与运行期的实操经验3.1 从“完全起不来”到“正常启动”的完整流程我在本地复现了一套最小化的哨兵环境三个节点一个主库6379、一个从库6380、一个哨兵26379。配置文件的完整示例大致如下。主库的redis.conf核心段bind 0.0.0.0 port 6379 daemonize yes requirepass YourPass123从库的redis.conf核心段bind 0.0.0.0 port 6380 daemonize yes replicaof 127.0.0.1 6379 masterauth YourPass123 requirepass YourPass123哨兵的sentinel.conf核心段bind 0.0.0.0 port 26379 daemonize yes protected-mode no sentinel monitor mymaster 127.0.0.1 6379 1 sentinel auth-pass mymaster YourPass123 sentinel down-after-milliseconds mymaster 5000 sentinel failover-timeout mymaster 10000 sentinel parallel-syncs mymaster 1这里有两种启动方式。一种是直接执行redis-sentinel /path/to/sentinel.conf另一种是redis-server /path/to/sentinel.conf --sentinel两种方式等价但有一个区别要注意redis-server的daemonize是在配置文件里设置的如果为yes它会在后台运行但如果你在前台运行redis-sentinel日志会直接打到控制台便于排查启动报错。哨兵正常启动后日志里会看到类似这样的行monitor master mymaster 127.0.0.1 6379 slave slave 127.0.0.1:6380 127.0.0.1 6380看到monitor和slave基本就能确认哨兵已经成功监控了主从拓扑。如果只看到monitor而没有slave通常是从库的复制关系没建好。3.2 明明“活着”却什么都不干的排查这是哨兵启动后最常见的困惑日志没报错、进程在跑、客户端也能通过哨兵拿到主节点地址但模拟故障后它死活不切换。第一步是确认日志。哨兵运行中会输出大量带或-前缀的日志条目。比如从节点掉线会有sdown slave主节点掉线会有sdown master。如果连sdown都没出现说明哨兵根本没察觉主节点异常。这时候去检查down-after-milliseconds这个值表示“心跳多久没收到才认为节点挂了”默认是30秒。如果你测试时等不到30秒就着急当然不会看到任何反应。第二步是确认仲裁数。我在配置里写的是quorum为1这用于测试是没问题的因为只有一个哨兵实例它自己就能做决定。但在生产环境quorum设为1意味着“只要一个哨兵说主挂了就切换”这在网络抖动时极其危险很可能造成脑裂。生产环境至少3个哨兵quorum设为2。第三步是观察哨兵日志里是否有odown客观下线。只有当绝大多数哨兵达成一致才会进入故障转移流程。如果你只起了1个哨兵但quorum写成2那么永远也不会进入odown自然也不会切换。相信我这个场景我实际踩到过当时还以为是网段隔离问题查了半天才发现是quorum配置不合理。第四步是检查failover-start日志。如果连这行都没有说明哨兵虽然发起了客观下线但仲裁后的决策被某些条件卡住了。比较常见的条件是sentinel failover-timeout设得太短导致还没完成主库选择就超时了。也有一种情况是并行同步数parallel-syncs设得太大新主库选拔时大量从库同时向它同步造成网络拥塞故障转移迟迟无法收尾。3.3 配置重写机制带来“灵异”现象哨兵有一个特性会让第一次接触的人觉得很“玄幻”哨兵进程会自动重写自己的配置文件。什么意思比如你手写了sentinel monitor mymaster 127.0.0.1 6379 1等哨兵跑了一段时间你再看这个文件可能会发现它变成sentinel monitor mymaster 127.0.0.1 6379 1 sentinel myid 0d7f9c2a1b... sentinel known-replica mymaster 127.0.0.1 6380 sentinel known-sentinel mymaster 127.0.0.1 26380 ...这些新增的内容是哨兵运行时自动记录并回写到配置文件里的。好处是哨兵之间能互相发现坏处是如果你在生产环境手动编辑配置文件哨兵可能在下次改动后把它的运行时状态覆盖进去导致你的修改“消失了”。我遇到过最迷惑的操作是改完down-after-milliseconds后重启哨兵发现改动没生效。后来才发现哨兵退出时会把运行时的内存配置回写文件覆盖了我手动改的那一行。解决办法是修改配置文件前先通过命令停机哨兵改完再启动或者修改后让哨兵自动重写一次再确认内容。另外sentinel myid是哨兵的唯一ID不要擅自复制或修改它影响哨兵集群的选举。如果你把两个哨兵的myid设成一样它们会互相认为对方是自己导致选举异常这是非常根因诡异的问题。3.4 主服务切换后客户端如何感知切换成功后哨兵会把新的主节点信息通过发布订阅推给客户端。但前提是客户端实现了哨兵协议比如Java的Jedis、Lettuce或Python的redis-py都有对应的Sentinel连接池。这里有个容易踩的坑客户端连接哨兵时使用的master-name必须和你配置里的sentinel monitor的master-name完全一致。比如配置里叫mymaster客户端写my-master那么哨兵会返回(error) NO such master with that name客户端自然无法拿到正确的主节点地址。另一个容易被忽略的问题是故障转移完成后哨兵的配置会被重写自动更新主节点和从节点的信息。但客户端如果持有旧的连接池可能还会尝试向旧主库写入数据。解决思路是让客户端连接哨兵自动发现主节点而不是自己在代码里写死IP。我之前用客户端直连Redis主库哨兵一切换代码里那个IP就失效了导致线上告警刷屏。改成连接哨兵的Sentinel连接池后库挂了也能自动跟着切换体验完全不一样。4. 踩坑实录典型报错与排查速查4.1 常见启动报错对照表我整理了一个速查表基本覆盖了哨兵启动阶段曝过的大多数问题。报错信息或现象根因解决方案sentinel monitor后提示Unknown command配置项大小写写错或格式不对检查拼写必须是sentinel monitor且参数完整进程起不来日志无输出配置文件无法写入或daemonize配置冲突检查目录权限用绝对路径先在前台启动看报错DENIED Redis is running in protected mode未设置密码且监听非回环地址设置requirepass或显式配置protected-mode no日志里持续报-failover-abort主节点在切换期间被哨兵认为仍然存活查看down-after-milliseconds是否设得太短检查网络抖动打开配置文件发现内容总被改写哨兵自动回写运行时状态先停机再改配置改完启动后再次确认从库复制日志一直报MASTER aborted replication从库没有配置masterauth在从库配置masterauth并与主库requirepass保持一致哨兵能监控但不能故障转移quorum配置大于哨兵实例总数检查quorum与哨兵节点数量之间的关系4.2 一套实用的排查顺序很多新手遇到哨兵异常后第一反应是把哨兵进程杀掉重启这个习惯其实很危险。哨兵自己会保存runtime状态重启可能导致故障转移状态丢失反而不利于定位问题。我建议按这个顺序排查先确认Redis主从本身存活和复制正常。执行INFO replication看主库的role:master和从库的connected_slaves如果主从复制没建立好哨兵再怎么配置也是白搭。再确认哨兵能连通所有节点。用一个命令就能测redis-cli -p 26379 -a YourPass123 sentinel replicas mymaster如果返回不了从节点列表说明哨兵对从节点的信息同步有问题重点检查认证和网络。接着看哨兵日志中的状态转换序列。正常情况是sdown到odown再到failover-start。卡在哪一步就去查那一步相关的配置。比如卡在odown之前多半是仲裁数达不到卡在failover-start之后多半是故障转移超时或选举问题。最后才考虑重启。重启前先备份一份sentinel.conf因为哨兵退出时可能会覆盖它。备份后用redis-server /path/to/sentinel.conf --sentinel方式前台启动观察完整输出。4.3 关于“持续更新”版本差异的提醒既然标题写了“持续更新”这篇内容其实也适用于讲一个概念Redis的版本迭代会对哨兵配置产生细微影响。Redis 6.0以后replicaof取代了slaveof但兼容旧写法。从Redis 5.0开始哨兵支持ACL可以通过用户权限来限制命令而不是只依赖requirepass。如果你使用ACL用户那么哨兵配置里写明的密码需要和ACL用户密码一致同时这个用户至少要有访问主节点和从节点的权限。另外从Redis 7.0开始部分配置项行为有调整。比如down-after-milliseconds对主观下线的影响更严格哨兵对时钟跳变也更敏感。建议部署前先阅读当前版本自带sentinel.conf示例文件而不是拿着网上老教程照抄。正是因为这个组件一直在迭代所以踩坑记录本身就是活的。后面我会在遇到新问题后继续往这个主题里补保持更新状态。5. 留给后来人的几句提醒这套哨兵配置和排查方法几乎全是从事故里磨出来的。最后再分享几条我自己比较深的体会。第一件不要为了图方便在单台机器上跑多个哨兵进程。真实环境里哨兵应该独立分布在不同的物理节点或至少不同机架上否则一旦物理机宕机你部署的所有哨兵同时失联高可用就成了一张废纸。演示环境怎么玩都行生产环境必须认真选点。第二件故障转移的触发条件里网络抖动比进程崩溃更难排查。我自己就遇到过主节点负载短暂飙高哨兵判定主观下线触发了一次误切换结果过了几秒主节点恢复反而产生了无谓的复制风暴。后来我把down-after-milliseconds调大才把这问题压住。这个值没有一个万能答案要根据你业务的实际响应时间来调而不是照抄默认值。第三件哨兵配置文件的修改一定要在停机状态下操作。这句话值得再强调一次。在线直接编辑配置文件大概率会被哨兵自动回写覆盖。改完配置以后也建议用SENTINEL CONFIG SET命令在线变更然后通过SENTINEL CONFIG GET核对而不是手动改动文件。如果你和我一样在启动哨兵的路上一路踩坑别慌把每条报错和日志都存下来这套过程本身就是最好的学习材料。之后如果又碰到什么“灵异现象”我还是会继续往这个主题里补充记录就是最好的复盘。