我不会起名字322· 后端 / 算法 / 数据库 技术栈 力扣 Hot100 Go 项目 Redis MySQL文章目录Redis 从 0 到 1九主库宕机 30 秒自动切主——1 主 2 从 3 哨兵 Go 客户端一、现象凌晨 3 点主库挂了值班同学手忙脚乱二、docker-compose 搭建 1 主 2 从 3 哨兵收藏点 1三、故障转移全流程30 秒里发生了什么四、哨兵 5 个核心参数收藏点 2五、Go 实操哨兵客户端收藏点 3六、部署与排查清单七、总结Redis 从 0 到 1九主库宕机 30 秒自动切主——1 主 2 从 3 哨兵 Go 客户端上一篇《Redis 从 0 到 1八读写分离读到旧值——主从延迟 4 来源与 Go 探测器》我们量化了主从复制延迟链接https://blog.csdn.net/2501_92769340/article/details/167399552 。本篇解决下一个问题主库宕机谁来切主——答案是哨兵Sentinel。一、现象凌晨 3 点主库挂了值班同学手忙脚乱人工切主的典型现场告警 → 登录服务器 →REPLICAOF NO ONE把从库升主 → 其他从库指向新主 → 改配置、重启应用……顺利的话十几分钟中间业务要么报错要么读旧数据。哨兵做三件事把这个流程压到 30 秒内监控持续探测主从健康、通知故障时发事件、自动故障转移选一个新主、让其他从库重新跟随、广播新主地址。本篇用一份 docker-compose 把它完整跑起来。二、docker-compose 搭建 1 主 2 从 3 哨兵收藏点 1services:redis-master:image:redis:7.2-alpinecommand:redis-server--appendonly yesports:[6379:6379]redis-replica1:image:redis:7.2-alpinecommand:redis-server--appendonly yes--replicaof redis-master 6379redis-replica2:image:redis:7.2-alpinecommand:redis-server--appendonly yes--replicaof redis-master 6379sentinel1:image:redis:7.2-alpinecommand:redis-server /etc/sentinel.conf--sentinelvolumes:[./sentinel1.conf:/etc/sentinel.conf]sentinel2:image:redis:7.2-alpinecommand:redis-server /etc/sentinel.conf--sentinelvolumes:[./sentinel2.conf:/etc/sentinel.conf]sentinel3:image:redis:7.2-alpinecommand:redis-server /etc/sentinel.conf--sentinelvolumes:[./sentinel3.conf:/etc/sentinel.conf]sentinel.conf 三份内容相同换端口即可port 26379 sentinel monitor mymaster redis-master 6379 2 sentinel down-after-milliseconds mymaster 5000 sentinel failover-timeout mymaster 60000 sentinel parallel-syncs mymaster 1dockercompose up-d# 验证哨兵视角dockerexecsentinel1 redis-cli-p26379SENTINEL master mymasterdockerexecsentinel1 redis-cli-p26379SENTINEL replicas mymastermonitor ... 2的 2 是法定人数quorum至少 2 个哨兵都认为主库挂了才判定客观下线。3 个哨兵配 2 是标准搭配容忍 1 个哨兵自己脑抽。三、故障转移全流程30 秒里发生了什么dockerkillredis-master# 模拟主库宕机dockerexecsentinel1 redis-cli-p26379SENTINEL master mymaster# 观察 Info 里的 flags: odown - 等待几秒 - 新主的 IP 出现在输出里时间线默认参数下约 20~40 秒阶段耗时动作SDOWN5s单个哨兵连续 5 秒 PING 不通 → 主观下线ODOWN0s达到 quorum 个哨兵 SDOWN → 客观下线准备选举哨兵选举 leader数秒哨兵们互相投票选出一个来执行切换选新主秒级从在线从库中挑优先级高replica-priority 小、复制偏移量最大、runid 最小的切换秒级对新主REPLICAOF NO ONE其他从库REPLICAOF 新主旧主复活后被降级为新主的从库四、哨兵 5 个核心参数收藏点 2参数默认含义与调优down-after-milliseconds30000多久 PING 不通算主观下线。5 秒意味着网络抖动 5s 就会触发一次不必要切换太小会惊群切换常用 5~10squorummonitor 末位—判定客观下线需要的哨兵数。3 哨兵配 2failover-timeout180000一次切换的超时与两次切换的最小间隔同一次故障期间不会反复切parallel-syncs1切换时几个从库同时向新主全量同步。设大会加速但会打爆网络replica-priority从库侧100选主优先级数字小的优先不想被选中设为 0五、Go 实操哨兵客户端收藏点 3应用侧不需要自己连三个哨兵做发现——go-redis 的 FailoverClient 会packagemainimport(contextfmttimegithub.com/redis/go-redis/v9)funcmain(){rdb:redis.NewFailoverClient(redis.FailoverOptions{MasterName:mymaster,SentinelAddrs:[]string{127.0.0.1:26379,127.0.0.1:26380,127.0.0.1:26381},Password:redis123,DialTimeout:3*time.Second,ReadTimeout:2*time.Second,WriteTimeout:2*time.Second,PoolSize:20,MaxRetries:3,// 切换窗口内的命令由重试兜底})deferrdb.Close()ctx:context.Background()fori:0;i10;i{iferr:rdb.Set(ctx,sentinel:probe,i,time.Minute).Err();err!nil{fmt.Printf([%d] 写入失败切主窗口内属正常: %v\n,i,err)}else{v,_:rdb.Get(ctx,sentinel:probe).Result()fmt.Printf([%d] 当前主库读回: %v\n,i,v)}time.Sleep(time.Second)}}测试运行程序的同时docker kill redis-master观察输出——中间 1~3 次写入失败No route to host / connection refused随后自动恢复。这就是无感的真相有几秒窗口靠客户端重试吃掉。要想零失败业务层需要读旧值容忍或本地队列缓冲第 31 篇脑裂还会再谈切换期数据。FailoverClient 与第 2 篇普通 Client 的区别启动时向哨兵问现在谁是主拿到主地址后直连主变更时会重新询问。它不连哨兵转发流量哨兵只当查号台。六、部署与排查清单命令作用SENTINEL master mymaster查看主库状态、当前地址、SDOWN/ODOWN 标记SENTINEL replicas mymaster查看从库列表与偏移量SENTINEL sentinels mymaster查看其他哨兵确认两两可达防止半数分区误判SENTINEL ckquorum mymaster检查当前可达法定人数部署后必跑INFO sentinel哨兵上哨兵自己的视角部署检查 4 步ckquorum通过证明没有哨兵网络孤岛杀掉一个哨兵告警与切换仍正常杀掉主库计时从 ODOWN 到新主可写旧主复活确认它被自动降级为从库、没有两个主脑裂残留第 31 篇详谈。七、总结哨兵 监控 通知 自动故障转移3 个哨兵 quorum2 是标准配置切主默认 20~40 秒无感靠客户端重试吃下切换窗口down-after-milliseconds 在快速切换和避免惊群之间取舍Go 侧用 FailoverClient让查号自动化。下一篇预告哨兵解决了高可用但单实例的内存和吞吐天花板还在——10 万 QPS、几百 GB 数据怎么扩下一篇《16384 个槽怎么分6 节点 Cluster 与 Go 重定向处理》我们进入分片时代。