
一次线上事故让我老老实实搭了三哨兵这事发生在两年前一个周五的晚上机房一台 Redis 主节点因为内存飙升被内核 OOM 干掉了。当时我们的架构是典型的一主两从——读写都在主节点上从节点只拿来做冷备份。结果主库一挂整个订单服务全部超时。我等了十来分钟才缓过神手动登录服务器把某个从节点提成主库再让业务方改配置重启服务。从故障发生到业务恢复整整 22 分钟。后来查监控才发现业务从第 30 秒就开始报错了。那次之后我就想明白一件事主从复制解决的是数据冗余但解决不了谁来决策切换的问题。于是我把搭建 Redis 哨兵集群Sentinel排在了下一周的迭代里。本文不是概念科普是我从零开始搭一套一主二从三哨兵架构的完整记录包括配置细节、故障转移实测、以及我踩过的几个坑。如果你也在规划线上 Redis 高可用方案或者刚准备接触哨兵集群这篇应该能让你少走点弯路。1. 主从复制的三个困局和哨兵的破局思路在动手搭建之前先把哨兵要解决的问题讲透。否则你会觉得它不过是一堆配置看不懂它为什么这么设计。1.1 没有哨兵时主节点宕机到底难在哪主从复制架构已经很常见了主节点处理写请求从节点异步同步数据。看起来挺稳妥但主节点一旦挂掉你就会立刻面对三个问题发现慢没人主动通知你主节点挂了通常要等业务方反馈超时、告警平台弹出大量错误日志你才后知后觉。哪怕你的监控间隔只有 30 秒叠加人工响应时间半小时故障时长是常态。切换难你想把某个从节点提升为主节点需要手动执行SLAVEOF NO ONE还要逐个登录其他从节点让它们重新指向新主库。写上十几条命令任何一步写错 IP 或者端口整个复制拓扑就乱了。决策依赖经验两个从节点数据同步延迟不一样应该选哪个当新主库如果选了延迟最大的从节点会丢多少数据这种决策几乎完全靠运维人员的经验积累换个新人来操作风险翻倍。一句话总结就是数据冗余已经做到了但高可用还差一个自动决策的大脑。1.2 哨兵集群的破局方式Redis Sentinel哨兵不是 Redis 的一个插件而是独立运行的进程你可以理解为一群专门盯着 Redis 节点的监控保安。它做了四件事监控定期检查主节点和从节点是否存活。通知状态变化时通过 API 告诉运维系统或者其他程序。自动故障转移主节点挂了自动从从节点里挑一个提升为新主节点并通知其他从节点改换门庭。配置提供客户端连哨兵就能拿到当前的主节点地址。这样主从切换后客户端不用改配置也能继续工作。哨兵自身也做成集群一般部署 3 个以上实例原因后面我会讲。在故障转移时它不是靠某一台哨兵拍板而是所有哨兵投票决策这就是哨兵集群和单个哨兵进程的本质区别。2. 哨兵怎么判断挂了——主观下线与客观下线的完整逻辑很多教程直接丢配置导致读者看完能照着敲一遍但遇到诡异问题时完全没法排查。我建议先花五分钟理解哨兵的判断机制这对后面调参和排错都非常关键。2.1 三个定时任务构成哨兵的心跳网络每个哨兵进程对它的监控对象会周期性地执行三个任务每 10 秒向主节点和从节点发送INFO命令用来获取当前复制拓扑结构。主从关系发生变化时哨兵就是靠这个任务来感知的。每 2 秒向__sentinel__:hello频道发布自己的信息同时订阅这个频道。这相当于哨兵之间的实时对讲机每个哨兵通过这个频道发现其他哨兵也能顺便交换自己对主节点的看法。每 1 秒向主节点、从节点和其他哨兵发送PING命令用来判断目标是否存活。这是最核心的心跳机制。理解了这三个任务你就知道哨兵对节点挂了的判断是有延迟的。默认情况下心跳每 1 秒一次如果配置了down-after-milliseconds30000意味着要连续 30 秒收不到正常PONG回复才会触发下线判定。2.2 主观下线SDOWN与客观下线ODOWN这是哨兵机制中最容易混淆的两个概念。主观下线某个哨兵实例自己发现主节点心跳异常它自己判定主节点下线了。注意这只是它一个人的看法不代表集群共识。客观下线主观下线后这个哨兵会通过SENTINEL is-master-down-by-addr命令询问其他哨兵你们看主节点也挂了吗当收到足够多的确认后它才会认定主节点客观下线。这里足够多取决于quorum配置。比如配置了quorum 2就表示至少需要 2 个哨兵包括发起询问的那个都认为主节点挂了才能进入故障转移流程。这个设计的核心思想很直白单个哨兵可能出现网络抖动或误判少数服从多数才能降低误切换概率。2.3 故障转移是怎么一步步执行的一旦主节点被判定为客观下线哨兵集群里就会触发选举和故障转移流程。流程大致如下发起客观下线判定的哨兵会尝试向其他哨兵拉票每个哨兵只有一票票数过半才能成为领导者。领导者哨兵从所有从节点中筛选候选优先选择复制偏移量最大的从节点也就是数据最完整的那个作为新主节点。向候选节点发送SLAVEOF NO ONE让它变成主节点。向其他从节点发送SLAVEOF 新主节点IP 新主节点端口让它们重新复制新主。把原来的主节点标记为已下线等它恢复上线后自动降级为新主节点的从节点。顺带一提哨兵集群本身也需要一个领导者来执行切换不然 3 个哨兵同时对从节点发命令会乱套。选举领导者的票数要求是超过一半而不是超过quorum所以哨兵数量建议部署至少 3 个。只搞 2 个哨兵的话主观下线后永远凑不出过半票数故障转移根本跑不起来——这是很多人踩过的坑。3. 搭建前的规划版本、节点、目录一个都不能少说干就干之前先聊聊规划。我这次搭建用的是三台服务器每台上面部署一个 Redis 实例和一个哨兵实例。3.1 版本选择别用太老的 2.x / 3.xRedis 版本建议直接用 6.x 或 7.x。Sentinel 机制在 Redis 2.8 时代就已经有了发展到现在已经很稳定但老版本2.x、3.x在配置写回、子进程行为、以及一些命令兼容上还是有不少坑。新版对于主从复制的 psync 机制和内存管理也有大幅改善。特别是 7.x对复制延迟的优化和新加的repl-diskless-sync选项能让故障转移后的全量同步压力小很多。我的环境用的是Redis 7.0.12操作系统是 CentOS 7.9。这个组合没踩到什么版本兼容性的问题。3.2 节点规划服务器Redis 角色哨兵角色IP:端口节点 A主节点初始哨兵 A192.168.10.10:6379 / 26379节点 B从节点 1哨兵 B192.168.10.20:6379 / 26379节点 C从节点 2哨兵 C192.168.10.30:6379 / 26379注意哨兵默认端口是 26379不是常规的 6379。如果你在生产环境买了云服务器记得在安全组里同时放行这两个端口。另外生产环境一定要把哨兵和 Redis 分开部署在不同的物理机或虚拟机上否则一台宿主机挂掉上面的 Redis 和哨兵一起没了哨兵集群的意义就大打折扣。3.3 下载和安装我选择编译安装因为要保证三台机器的版本完全一致。具体步骤不用多讲官网下载 tar.gz 包解压后make即可。给你看下我用的流程# 下载 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 # 安装到 /usr/local/redis make install PREFIX/usr/local/redis编译安装需要的 gcc 等依赖系统一般自带。如果make报错检查一下 gcc 版本就行。注意Redis 5.x 之后编译不再需要手动make MALLOClibc来处理 jemalloc 的问题但如果你在make时报错提示找不到jemalloc.h可以尝试make MALLOClibc绕过。3.4 目录规划与日志我习惯把配置文件和日志放在一起管理方便备份和排查/etc/redis/ # 存放 redis.conf 和 sentinel.conf /var/log/redis/ # 存放日志文件 /var/lib/redis/ # 存放 RDB 和 AOF 持久化文件 /var/run/redis/ # 存放 pid 文件先把目录建好mkdir -p /etc/redis /var/log/redis /var/lib/redis /var/run/redis4. 手把手搭建一主二从三哨兵# /etc/redis/redis_6379.conf # 绑定内网 IP不要用 0.0.0.0 暴露到公网 bind 192.168.10.10 port 6379 daemonize yes pidfile /var/run/redis_6379.pid logfile /var/log/redis/redis_6379.log dir /var/lib/redis # 配置密码生产环境必须主从复制和哨兵都需要用到 requirepass Redis2024 # 持久化选择 appendonly yes appendfilename appendonly.aof注意哨兵与 Redis 之间的通信也涉及密码验证这一块最容易写错。哨兵配置里有两个密码字段sentinel auth-pass master-name 密码哨兵连接 Redis 节点时用的密码。如果哨兵之间也需要认证还要配置sentinel auth-user和sentinel auth-passRedis 6 之后支持 ACL 用户可以给哨兵单独建一个最小权限用户。为了简化我直接在requirepass里配置了统一密码并在所有哨兵配置中声明同一个密码。4.1 配置主节点第一台机器192.168.10.10上编辑/etc/redis/redis_6379.conf作为主节点配置。以下是关键配置# /etc/redis/redis_6379.conf # 绑定内网 IP不要用 0.0.0.0 暴露到公网 bind 192.168.10.10 port 6379 daemonize yes pidfile /var/run/redis_6379.pid logfile /var/log/redis/redis_6379.log dir /var/lib/redis # 配置密码生产环境必须主从复制和哨兵都需要用到 requirepass Redis2024 # 持久化选择 appendonly yes appendfilename appendonly.aof启动主节点/usr/local/redis/bin/redis-server /etc/redis/redis_6379.conf用客户端验证一下能不能连上/usr/local/redis/bin/redis-cli -h 192.168.10.10 -p 6379 -a Redis2024 ping # 返回 PONG4.2 配置从节点第二台192.168.10.20和第三台192.168.10.30机器的 Redis 配置基本相同只是bind、pidfile、logfile、dir等路径对应各自机器。核心区别在于多了两行主从配置# /etc/redis/redis_6379.conf节点 B bind 192.168.10.20 port 6379 daemonize yes pidfile /var/run/redis_6379.pid logfile /var/log/redis/redis_6379.log dir /var/lib/redis requirepass Redis2024 appendonly yes # 主从复制配置指定主节点地址和密码 replicaof 192.168.10.10 6379 masterauth Redis2024节点 C 也照此配置只是把 IP 换成 192.168.10.30 对应的地址replicaof仍然指向主节点 192.168.10.10。启动两个从节点后回到主节点查看复制状态redis-cli -h 192.168.10.10 -p 6379 -a Redis2024 info replication你会在Replication部分看到类似这样的信息role:master connected_slaves:2 slave0:ip192.168.10.20,port6379,stateonline,offsetxxx,lag0 slave1:ip192.168.10.30,port6379,stateonline,offsetxxx,lag0看到stateonline就说明两个从节点已经正常同步了。4.3 配置三个哨兵每台机器上创建一个/etc/redis/sentinel_26379.conf。三份配置差异只在bindIP 和当前哨兵自己的端口相关的标识信息。主节点所在机器节点 A的哨兵配置如下# /etc/redis/sentinel_26379.conf bind 0.0.0.0 port 26379 daemonize yes pidfile /var/run/redis-sentinel_26379.pid logfile /var/log/redis/sentinel_26379.log # 声明监控的主节点 # 格式sentinel monitor master-name ip port quorum sentinel monitor mymaster 192.168.10.10 6379 2 # 连接主从节点的密码 sentinel auth-pass mymaster Redis2024 # 判定主节点主观下线的时间阈值毫秒 sentinel down-after-milliseconds mymaster 30000 # 故障转移完成后允许从节点指向新主节点的最大时间毫秒 sentinel failover-timeout mymaster 180000 # 故障转移时同时并行向新主节点发起同步的从节点数量 sentinel parallel-syncs mymaster 1bind 0.0.0.0这里我解释一下。很多教程写bind 127.0.0.1但那样哨兵之间无法通过网络互相通信只能发现本地哨兵整个集群就是错的。生产环境建议绑定内网网卡 IP 或者 0.0.0.0同时用防火墙/安全组限制来源 IP。mymaster是主节点的一个逻辑名字不是 IP也不是主机名。你可以随便命名但所有哨兵配置里必须完全一致。哨兵内部就是通过这个名字来追踪主从复制拓扑的。quorum为什么填 2 而不是 3因为 3 个哨兵的情况下主节点挂了只要 2 个哨兵确认就可以认为客观下线可以少等一个哨兵的响应故障转移更快。当然这也意味着极端情况下仅 2 个哨兵确认也能触发切换第 3 个哨兵可能刚好处于网络分区或宕机状态。如果你希望更保守可以填 3。4.4 启动顺序很重要第一次启动哨兵时可以先启动主节点确保主节点正常后再启动三个哨兵。虽然顺序不是死的但有个潜在的坑如果先启动哨兵它发现主节点不存在会一直打日志反复尝试连接。这时候它会阻塞在初始化状态后面的哨兵加入也会受影响。所以我推荐启动顺序主节点 → 从节点 → 哨兵 A → 哨兵 B → 哨兵 C。启动哨兵的命令/usr/local/redis/bin/redis-sentinel /etc/redis/sentinel_26379.conf启动后用这个命令看看哨兵视角里的主节点/usr/local/redis/bin/redis-cli -h 192.168.10.10 -p 26379 sentinel master mymaster输出结果里会显示一堆字段和值重点看这几项num-slaves2表示发现了两个从节点。num-other-sentinel2表示发现了另外两个哨兵。flagsmaster说明哨兵认为主节点状态正常。三个哨兵的输出都能看到彼此num-other-sentinel2时说明集群的基础健康状态没问题。5. 故障转移实测手动杀掉主节点后到底发生了什么搭建完成只是第一步。作为有 P0 教训的人我强烈建议你主动做一次故障演练而不是等线上出问题再来验证。这一步能告诉你哨兵集群到底靠不靠谱。5.1 暴力杀掉主节点直接在节点 A 上执行redis-cli -h 192.168.10.10 -p 6379 -a Redis2024 shutdown nosave或者更暴力一点直接 kill 掉 redis-server 进程。为了让过程更真实就模拟物理宕机吧kill -9 主节点redis进程PID5.2 盯哨兵日志看完整决策链路此时去节点 B 或节点 C 查看哨兵日志tail -f /var/log/redis/sentinel_26379.log你会看到下面几步逐步出现我摘录关键行并加了注释# 第一步当前哨兵对主节点进入主观下线状态 sdown master mymaster 192.168.10.10 6379 # 第二步其他哨兵也同意达到 quorum 数量进入客观下线状态 odown master mymaster 192.168.10.10 6379 #quorum 2/2 # 第三步当前哨兵被选举为领导者开始执行故障转移 try-failover master mymaster 192.168.10.10 6379 # 第四步选举从节点作为新主节点这里选中的是从节点 B promote-slave slave 192.168.10.20:6379 192.168.10.20 6379 # 第五步将其他从节点重新指向新主节点 slave-reconf-sent slave 192.168.10.30:6379 # 第六步新从节点开始和新主节点同步 slave-reconf-inprog slave 192.168.10.30:6379 # 第七步同步完成 slave-reconf-done slave 192.168.10.30:6379 # 第八步整个故障转移流程结束 failover-end master mymaster 192.168.10.20 6379日志时间戳能清楚地告诉你每个阶段花费了多长时间。我当时的实测结果是从杀掉主节点到promote-slave执行完毕大约 20 秒到所有从节点完成重定向同步大约 40 秒。这里面大头是down-after-milliseconds30000也就是主观下线判定占用了约 30 秒。如果你想让切换更快可以把这个值调小到 10000但代价是网络抖动可能引发频繁误切换。生产环境我建议保留默认 30000除非你做过足够的网络延迟评估。5.3 验证新主节点数据状态故障转移完成后查看新主节点节点 B的info replicationredis-cli -h 192.168.10.20 -p 6379 -a Redis2024 info replication应该能看到role:master并且只有一个从属节点节点 CIP 为 192.168.10.30。节点 C 的数据是从旧主节点同步来的最后状态数据一致性完全由 Redis 复制机制保证。5.4 旧主节点恢复后它会自动站到新主节点身后这一步验证起来很爽也最能体现哨兵的容错能力。重新把节点 A 的 Redis 启动起来/usr/local/redis/bin/redis-server /etc/redis/redis_6379.conf然后去节点 B现在的主节点上查看复制状态你会发现role:master connected_slaves:2 slave0:ip192.168.10.30,port6379,stateonline slave1:ip192.168.10.10,port6379,stateonline旧主节点节点 A 恢复后哨兵会自动向它发送REPLICAOF命令把它降级为当前主节点的从节点。整个过程完全不用人工干预。看到这一幕我当时的感受是如果线上那 22 分钟也能有这待遇该多好。6. 哨兵集群的常见坑与排查心得搭建过程中和后续生产使用中我陆陆续续踩过不少坑挑几个值得记录的写在这里。6.1 坑一sentinel monitor的quorum与选举票数过半数傻傻分不清这也是哨兵里最容易造成理解偏差的地方。网上很多文章会把quorum2解释成需要 2 个哨兵同意才切换这个说法不完整。实际上面还有个领导者选举环节需要超过一半的哨兵同意。假设你只部署了 2 个哨兵配置quorum 1当主节点挂掉后虽然能达到客观下线条件但选领导者时最多只能拿到 1 票无法超过 1 的半数要至少 2 票故障转移直接卡死。**结论就是生产环境哨兵节点必须是奇数个最少 3 个。**2 个哨兵是伪高可用。6.2 坑二配置文件被哨兵改写了导致后面启动飘了哨兵启动后会自动修改自己的配置文件。比如我原本在节点 A 的sentinel.conf里写的是sentinel monitor mymaster 192.168.10.10 6379 2故障转移完成后你再看这个文件会发现 IP 变成了192.168.10.20还多了一行sentinel known-replica的记录。这就意味着如果你把同一个配置文件复制到多台机器上用会出大事。我曾经图省事直接把节点 A 的sentinel.conf通过 scp 复制到节点 B 和节点 C改一下 bind IP 就启动了。结果所有哨兵都认为同一个主节点地址配置文件互相覆盖各种错乱。正确做法是每台哨兵的配置文件从零开始编写或者复制后必须清理掉运行时生成的信息如sentinel known-replica、sentinel known-sentinel、generated-conf之类的内容。6.3 坑三主从复制密码没配对日志里全是-NOAUTH如果 Redis 配置了requirepass从节点的masterauth必须和主节点密码一致哨兵配置里的sentinel auth-pass也必须一致。三处任何一个不一致结果就是哨兵能发现节点但从节点同步失败哨兵执行故障转移时也连不上主节点日志里反复出现-NOAUTH Authentication required。排查的时候直接看 Redis 和哨兵的日志如果看到大量 NOAUTH 报错优先去对这三处密码配置。6.4 坑四Docker 部署哨兵时announce-ip必须配如果你参考网上教程用 Docker 部署 Redis 哨兵会发现一个诡异现象哨兵之间明明通了但故障转移时总失败日志显示目标节点地址连接不上。原因是 Docker 容器内的 IP 和宿主机网段不一致。哨兵在广播自己的信息时默认使用容器内部 IP比如 172.18.0.x而容器外无法通过这个 IP 访问容器。解决办法是在哨兵配置里显式声明对外发布的地址和端口sentinel announce-ip 宿主机内网IP sentinel announce-port 26379Redis 节点如果也跑在 Docker 里同样有类似问题但那个属于容器网络规划的范畴这里不展开讲。总之容器化部署哨兵announce 系列配置不能省。6.5 坑五客户端直连主节点故障转移成了笑话这是高可用方案落地时最容易被忽略的一环。我之前遇到过哨兵集群一切正常故障转移也成功执行了但业务方还是报错。排查到最后发现客户端的 Redis 配置里直接写死了主节点 IP比如spring: redis: host: 192.168.10.10 port: 6379主节点都切换到节点 B 了客户端还在连 192.168.10.10当然连不上。哨兵只解决了服务端的高可用客户端必须要能感知哨兵的决策。正确做法是使用支持哨兵的客户端连接方式。以 Java 的 Spring Data Redis 为例spring: redis: sentinel: master: mymaster nodes: - 192.168.10.10:26379 - 192.168.10.20:26379 - 192.168.10.30:26379 password: Redis2024客户端先连哨兵询问当前主节点是谁然后建立真正的连接。主节点切换后客户端会收到role change通知重新向哨兵获取新的主节点地址并重连。这才是完整的闭环。如果你测试哨兵集群一定要把客户端接入方式一起测了。6.6 坑六持久化策略不佳故障转移后丢了大量数据这里有个容易被忽视的细节哨兵选举新主节点时会优先选择复制偏移量最靠前的从节点。但如果线上 Redis 主节点关闭了持久化内存里还有大量未落盘的数据主节点瞬间宕机时那些没来得及同步到从节点的写操作就彻底丢了。而且从节点的数据也可能落后很多。所以在生产环境主节点和从节点都必须开启 AOF 持久化appendonly yes务必配置。同时关注主从复制的lag字段如果从节点长时间滞后说明带宽或处理能力有问题只靠哨兵也扛不住真正的数据丢失。6.7 坑七脑裂区间内的写丢失min-replicas参数能救一点哨兵自动切换并非完美无瑕存在一个被称为脑裂窗口的经典问题如果主节点并没有真正宕机只是和哨兵集群之间网络分区了哨兵会误判并触发故障转移。此时旧主节点还在接收写请求但它已经不是客户端眼中的主节点了这部分新写入的数据会因为再也无法同步而丢失。Redis 提供了两个参数来缓解min-replicas-to-write 1 min-replicas-max-lag 10含义是如果主节点连接的从节点数量少于 1 个或者从节点数据同步延迟超过 10 秒就拒绝写请求。这样在脑裂场景下旧主节点会很快发现自己失去了所有从节点从而停止写入。当然这是用可用性换一致性需要你结合业务场景权衡。我在线上配置里会加上这两个参数宁可牺牲几秒可用性也不想出现大面积数据丢失。7. 从高可用到生产可运维几个进阶建议把这套哨兵集群搭完、验证完只能算完成了 60% 的工作量。下面这几个点才是让高可用方案真正能上线扛事的关键。7.1 监控告警不能只盯着 Redis 本身哨兵日志里会出现-sdown、-odown、failover-end这些关键词。建议在日志采集系统里对它们建立告警规则。比如出现odown说明主节点被判定客观下线P0 级告警。出现failover-end说明自动故障转移完成需要人工确认切换是否正确。出现大量-sdown可能是网络抖动虽然不是致命的但需要关注连续性和持续时间。另外info replication里的lag字段和master_link_status也要定期检查。哨兵负责故障转移而摘除隐患和定期体检仍然要自己做。7.2 用 RedisInsight 做可视化管理很多人在网上找Redis Desktop Manager之类的可视化工具确实方便。官方还有一个叫 RedisInsight 的工具免费且跨平台可以同时管理 Redis 和哨兵。装上之后能直观看到当前主节点是谁哪些从节点在线。哨兵集群里有哪些哨兵实例。主从复制的拓扑图和延迟情况。我在本地用 RedisInsight 观察哨兵切换前后的拓扑变化比敲命令行直观得多。特别是团队里有新人时可视化面板可以降低理解门槛。7.3 从哨兵集群到分布式锁的边界问题说到 Redis 的高可用很多人会追问分布式锁怎么办。这里我必须提个醒哨兵解决的是高可用问题但并不能完美解决分布式锁的安全性问题。主节点挂了故障转移期间有短暂的时间窗口旧主节点上锁还没同步到新主节点可能导致两个客户端同时拿到锁这就是经典的 Redis 分布式锁不安全场景。如果你的业务对分布式锁的强一致性要求极高得评估是否引入 Redlock 等机制或者直接使用 etcd、ZooKeeper 这类强一致组件。但作为大多数业务场景哨兵 合理的锁超时策略已经够用关键是要清楚边界在哪里。7.4 从这套架构还能延伸出什么搭建过哨兵集群之后你会发现集群管理的核心思路是一致的独立的决策层 心跳探测 多数派决策。这套思路同样适合其他中间件的高可用设计比如 ZooKeeper 的选举机制、Kafka 的 controller 选举、ES 的 master 选举。你在 Redis 上踩过的坑、总结出的经验后续接触其他集群系统时很多可以直接复用。最后再说一个细节。生产环境里我建议每个哨兵进程使用一个单独的低权限系统账号运行不要在 root 下启动。哨兵配置里还有sentinel deny-scripts-reconfig yes这样的选项用来禁止配置中自动执行脚本减少被攻击面。高可用是要让你睡得着觉不是让你把安全防线拆掉。回头看这次搭建过程配置本身其实就十几行真正的价值在于理解哨兵为什么要这么设计以及故障转移发生前后的完整链路。希望这篇记录能帮你在搭建和排错时省点时间。如果真遇到问题了别怕把日志从头翻一遍哨兵汇报的每个状态变化都在日志里写得清清楚楚顺着读下去答案自己会出来。