🛡️ 高可用
主从复制 · 哨兵模式 · Cluster 集群 · 哈希槽与重定向 · 脑裂问题
1. Redis 主从复制原理?全量同步和增量同步?
复制过程:
- 从库执行
SLAVEOF master_ip port(或配置 replicaof),发送 psync 请求 - 全量同步:主库生成 RDB 快照(BGSAVE)→ 传输给从库 → 从库加载。期间新写入的命令进 该从库的复制客户端输出缓冲区(受
client-output-buffer-limit replica限制,超限会断开从库)—— repl_backlog 是主库的全局环形缓冲,只服务断线后的部分重同步,不要混淆 - RDB 加载完,主库把缓冲区增量命令发给从库追平
- 增量同步(断线重连):repl_backlog(环形缓冲)只有主库有;从库只记录自己的 master_repl_offset,断线期间主库命令仍写 backlog,重连后从库发 psync(offset),主库把 offset 之后的命令补发。offset 太旧(超出 backlog)→ 退化为全量同步
主从读写分离:主写从读。但注意:主从是异步复制,从库可能有延迟,且写主读从有"读不到刚写的"问题。
🎯 面试要点
- 全量同步在从库重启/网络长期断开时会发生,大实例会阻塞主库 fork → 调大 repl-backlog-size(默认 1MB)
- 从库默认只读(replica-read-only yes);从库也可以再挂从库(级联,减轻主库压力)
- 传播方式:主库写命令同时写 repl backlog 和 AOF(如开启)
2. 哨兵(Sentinel)机制?故障转移流程?
哨兵:独立的监控进程集群(通常 3 个),监控主从、自动故障转移、通知客户端新主地址。解决"主挂了怎么办"。
关键概念:
- 主观下线(sdown):单个哨兵发现主库心跳超时(down-after-milliseconds)
- 客观下线(odown):多数哨兵都判定下线(投票)——避免单哨兵误判
- Leader 选举:哨兵间用 Raft 协议选一个 Leader 执行故障转移
- 故障转移:Leader 选一个从库(复制偏移量最新、优先级最高)→ 提升为新主(SLAVEOF NO ONE)→ 其他从库改为复制新主 → 通知客户端(通过发布订阅)
🎯 面试要点
- 哨兵数量取奇数(3/5/7),> 半数在线才能判定客观下线——2 个哨兵就有单点风险
- 脑裂风险:主库与哨兵断连但仍在运行 → 哨兵提升新主 → 旧主恢复后数据冲突。缓解:min-replicas-to-write 配置(写前至少 N 个从库确认),旧主写不进去
- 客户端通过 sentinel get-master-addr-by-name 动态获取主地址(Redisson 原生支持)
3. Cluster 集群原理?哈希槽?
Cluster(Redis 3.0+):无中心化集群,解决单机容量和吞吐瓶颈。
- 数据分片:16384 个哈希槽。key 的槽位 =
CRC16(key) % 16384,槽均匀分配给各节点(如 3 主节点:5461/5462/5461) - 每个节点存部分槽;节点间 gossip 协议互通状态
- 客户端路由:客户端算槽 → 请求对应节点;槽不在本节点时返回 MOVED(正式迁移)或 ASK(迁移中)重定向,客户端缓存后重试
- 高可用:每个主节点挂 1+ 从节点,主挂后从节点自动提升(Raft 风格投票)
- 槽迁移:集群扩容/缩容时按槽迁移数据,不停服
多 key 操作的约束
# 不同 key 的槽不同 → 多 key 命令(MGET/MSET/LUA)会报错
MGET user:1 user:2
# (error) CROSSSLOT Keys in request don't hash to the same slot
# 解决:使用 hash tag,让 key 落同一槽
MGET {user}:1 {user}:2 # {} 内部分参与 CRC16,两个 key 同槽
# 代价:同 tag 的 key 集中在同一节点,热点倾斜
🎯 面试要点
- 16384 槽的原因:心跳包中槽位 bitmap 2KB,gossip 传输划算;超过 1000 节点网络消息量大(官方建议 ≤ 1000 节点)
- Cluster 不支持:多 key 操作(除非 hash tag)、Lua 跨槽、事务跨槽
- 对比哨兵:哨兵解决"高可用",Cluster 解决"高可用 + 水平扩展";小数据量用哨兵即可
- 客户端缓存槽映射(JedisCluster/Redisson 都有 ClusterConnection),MOVED 触发刷新