Skip to content

Redis 集群模式:主从/哨兵/Cluster

提出问题

面试官问"Redis 怎么做高可用"时,大部分人会答"主从复制加哨兵"。但再追问一句:"主从切换如果从节点复制滞后太多,升主之后数据丢了怎么办?"或者"Redis Cluster 为什么是 16384 个槽而不是 65536 个?"——回答就开始犹豫了。

这三个架构不是简单的"从小到大的升级",而是不同业务规模下的最优解。选错了架构比不选更惨:10GB 数据用 Cluster 增加运维复杂度,100GB 数据用哨兵无法水平扩展。选择的关键不是"哪个更先进",而是"哪个匹配你的场景"。

分析问题

主从复制:一主多从,异步复制

主从复制是 Redis 最基础的高可用方案。一个主节点负责写,多个从节点同步数据、提供读流量。

bash
# 从节点配置
# redis.conf
replicaof 192.168.1.100 6379
replica-read-only yes

复制的核心流程分三步:

  1. 全量同步:从节点首次连接,主节点 fork 子进程做 RDB 快照,同时把增量命令写入 repl_backlog_buffer,从节点加载 RDB 后追上增量
  2. 增量同步:正常运行期间,主节点将写命令传播到从节点(psync 协议)
  3. 断线重连:从节点断开后重新连接,主节点根据偏移量决定做增量同步还是全量重同步

断线重连是关键风险点repl_backlog_buffer 是一个环形缓冲区,默认只有 1MB。如果从节点断开时间过长,主节点的新写入覆盖了从节点未同步的偏移量,就只能做全量重同步(FULLRESYNC)。对于 10GB+ 的实例,全量同步要传输整个 RDB 文件,占用主节点 CPU、磁盘 IO 和网络带宽,可能造成线上抖动。

bash
# 查看复制状态
127.0.0.1:6379> INFO replication
# Replication
role:master
connected_slaves:2
slave0:ip=192.168.1.101,port=6379,state=online,offset=1024000,lag=0
slave1:ip=192.168.1.102,port=6379,state=online,offset=1024000,lag=0
master_replid:xxx
master_repl_offset:1024000
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_histlen:1024000

生产建议repl-backlog-size 至少调到 64MB-256MB,具体取决于网络抖动容忍时间。公式:peak_write_bytes_per_second × max_reconnect_seconds

哨兵模式:自动故障转移

主从复制有个致命问题:主节点挂了不会自动切换。哨兵模式(Sentinel)解决了这个问题:一组 Sentinel 进程监控主从节点,当主节点不可达时,自动选举一个从节点升主。

bash
# sentinel.conf
sentinel monitor mymaster 192.168.1.100 6379 2
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 30000
sentinel parallel-syncs mymaster 1

哨兵选举采用的是 Raft-like 算法(不是完整 Raft 实现)。流程:

  1. 哨兵节点发现主节点 ODOWN(客观下线,需要 quorum 个哨兵确认)
  2. 哨兵节点给自己投票,并向其他哨兵拉票
  3. 拿到超过半数票的哨兵成为 leader,执行故障转移
  4. 选一个从节点升主(优先级高 → 复制偏移量大 → runid 小)

哨兵集群最少需要 3 个节点。2 个哨兵不满足"至少 2 个确认才 ODOWN"(quorum=2 时,如果 1 个哨兵挂了,剩下的 1 个永远达不到 quorum)。生产上建议 3 个或 5 个奇数节点,部署在不同机器上。

一个容易被忽略的坑:Sentinel 连接从节点也需要密码。如果 requirepassmasterauth 配置不一致,从节点升主后无法通过密码验证,导致故障转移失败。

bash
# 必须保持一致
requirepass Redis123
masterauth Redis123

Redis Cluster:去中心化分片

当单机内存超过 100GB,或者 QPS 超过单机上限(通常 10w+),就需要 Redis Cluster。Cluster 将数据分片到多个节点,每个节点负责一部分 Hash Slot。

bash
# 创建 6 节点集群(3 主 3 从)
redis-cli --cluster create \
  192.168.1.100:6379 192.168.1.101:6379 192.168.1.102:6379 \
  192.168.1.103:6379 192.168.1.104:6379 192.168.1.105:6379 \
  --cluster-replicas 1

16384 个 Hash Slot 的由来:Redis 作者在集群设计文档中解释过,16384 个槽的心跳消息(CLUSTER NODES)大小约 2KB(bitmap 表示)。如果增加到 65536 个槽,心跳消息增大到 8KB,而集群节点数通常不超过 1000,16384 个槽已经足够每节点至少负责 16 个槽。更密集的槽位只会增加心跳对带宽的消耗,没有实际收益。

客户端路由:客户端连接任意节点,如果 key 的 Slot 落在当前节点,直接处理;否则返回 MOVED 重定向,客户端更新本地路由缓存,下次直接访问正确节点。

bash
# 查看 slot 分配
127.0.0.1:6379> CLUSTER SLOTS
1) 1) (integer) 0
   2) (integer) 5460
   3) 1) "192.168.1.100"
      2) (integer) 6379
   4) 1) "192.168.1.103"
      2) (integer) 6379

Cluster 的代价:跨 Slot 操作(如 MGET 多个 key 在不同 Slot)会报 CROSSSLOT 错误。解决方案是哈希标签(Hash Tag){user:123}:key1{user:123}:key2 用大括号包住的部分做 CRC16,确保同一用户的 key 落在同一 Slot。

bash
# 使用哈希标签
MGET {user:123}:name {user:123}:age  # 正常执行,同一 Slot
MGET user:123:name user:456:age       # CROSSSLOT 错误

总结

架构数据量可用性扩缩容复杂度典型场景
主从复制10GB 以下手动切换手动加从节点读写分离
哨兵模式50GB 以下自动切换(秒级)手动加节点缓存层高可用
Redis Cluster百 GB 以上自动切换 + 分片在线扩缩容大规模数据分片

面试话术示例:"如果数据量在 50GB 以内,我倾向用哨兵模式,3 个哨兵节点 + 1 主 2 从,配置 repl-backlog-size 256MBmin-replicas-to-write 1 做写入保护。超过 100GB 必须用 Cluster,提前规划好 Slot 分布和 Hash Tag 策略,把跨 Slot 操作的业务逻辑在客户端封装好。选型不是选最好的,是选最合适的——运维团队能力、数据量级、业务读写模型,这些比单纯的技术对比更重要。"

参考:Redis 官方文档 Cluster 规范、Redis Sentinel 文档、《Redis 设计与实现》第 14-16 章

手撕 → 框架 → 生产化,一步步把 AI Agent 工程化搞透。