半夜三点,谁去切主
主从复制解决了读扩展与数据冗余,但留了个没人接的活:主库挂了,故障转移靠谁。人工切换意味着半夜告警、手忙脚乱、停服窗口;Redis Sentinel(哨兵)把这个活自动化——它本身不存数据,是一组独立运行的监控进程,干的就一件事:盯着主从,主库失联时自动把合适的从库提升为新主库,再把客户端指过去。
下线判定:一票不算,多数投票
哨兵判定主库故障是两段式的,处处透着「别误判」的谨慎。主观下线(sdown):单个哨兵在 down-after-milliseconds 内没收到主库的合理回复,它自己记一笔「我觉得主库挂了」——可能只是这个哨兵自己网络抽风。客观下线(odown):这个哨兵去问其他哨兵「你们觉得呢」,达到 quorum 配置的票数,主库被正式定性为客观下线——网络抖动、哨兵单点故障都被这套仲裁挡在误判之外。
定性之后还有一场选举:多个哨兵协商出一个领队哨兵(leader)来执行切换,选举规则用的就是熟悉的 Raft 多数派思想——必须拿到过半哨兵的同意。所以哨兵要至少部署三个、且奇数:两个哨兵挂一个就凑不出多数派,整套机制瘫在半空。领队选出后执行 failover:从从库里挑一个数据最新的(优先看 offset),提升为新主库,其余从库改挂到新主,旧主恢复后降级为从库。
客户端怎么找到主库
故障转移之后主库地址变了,客户端不能写死 IP——接入哨兵架构时,客户端配置的是哨兵的地址列表,用的时候先问哨兵「主库是谁」,拿到地址再连。主库切换后,哨兵会在订阅频道里发布新主库地址,客户端收到通知刷新本地缓存。这一步让「切换」对业务代码完全透明:
JedisSentinelPool pool = new JedisSentinelPool(
"mymaster", // 主库逻辑名
Set.of("10.0.0.1:26379", // 哨兵列表,不是redis地址
"10.0.0.2:26379",
"10.0.0.3:26379"),
poolConfig, "pass");
// 读写主库地址由哨兵实时告知,切换自动跟随脑裂:旧主还魂的那几秒
哨兵架构有个著名的暗坑叫脑裂:主库与从库之间网络分区,哨兵们判主库客观下线、把某个从库提升为新主——但旧主库还活着,客户端(尤其是没及时收到通知的)还在往旧主写数据。分区恢复后旧主降级为从库、全量同步新主的数据,分区期间写进旧主的那几秒数据,全部蒸发。缓解手段是 min-replicas-to-write 1:主库至少有一个从库在线才接受写入——被孤立的旧主写不进数据,蒸发窗口被大幅压缩。防不到零,但够用。
哨兵解决了高可用,但主从架构的内存天花板没变:一台机器装不下的数据量,加再多从库也没用。哨兵管可用性,数据分片找 Cluster——横向扩展的最后一讲,下一篇收官这个话题。
评论 (0)