Redis 集群模式:主从/哨兵/Cluster
提出问题
面试官问"Redis 怎么做高可用"时,大部分人会答"主从复制加哨兵"。但再追问一句:"主从切换如果从节点复制滞后太多,升主之后数据丢了怎么办?"或者"Redis Cluster 为什么是 16384 个槽而不是 65536 个?"——回答就开始犹豫了。
这三个架构不是简单的"从小到大的升级",而是不同业务规模下的最优解。选错了架构比不选更惨:10GB 数据用 Cluster 增加运维复杂度,100GB 数据用哨兵无法水平扩展。选择的关键不是"哪个更先进",而是"哪个匹配你的场景"。
分析问题
主从复制:一主多从,异步复制
主从复制是 Redis 最基础的高可用方案。一个主节点负责写,多个从节点同步数据、提供读流量。
# 从节点配置
# redis.conf
replicaof 192.168.1.100 6379
replica-read-only yes复制的核心流程分三步:
- 全量同步:从节点首次连接,主节点
fork子进程做 RDB 快照,同时把增量命令写入repl_backlog_buffer,从节点加载 RDB 后追上增量 - 增量同步:正常运行期间,主节点将写命令传播到从节点(
psync协议) - 断线重连:从节点断开后重新连接,主节点根据偏移量决定做增量同步还是全量重同步
断线重连是关键风险点:repl_backlog_buffer 是一个环形缓冲区,默认只有 1MB。如果从节点断开时间过长,主节点的新写入覆盖了从节点未同步的偏移量,就只能做全量重同步(FULLRESYNC)。对于 10GB+ 的实例,全量同步要传输整个 RDB 文件,占用主节点 CPU、磁盘 IO 和网络带宽,可能造成线上抖动。
# 查看复制状态
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 进程监控主从节点,当主节点不可达时,自动选举一个从节点升主。
# 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 实现)。流程:
- 哨兵节点发现主节点
ODOWN(客观下线,需要 quorum 个哨兵确认) - 哨兵节点给自己投票,并向其他哨兵拉票
- 拿到超过半数票的哨兵成为 leader,执行故障转移
- 选一个从节点升主(优先级高 → 复制偏移量大 → runid 小)
哨兵集群最少需要 3 个节点。2 个哨兵不满足"至少 2 个确认才 ODOWN"(quorum=2 时,如果 1 个哨兵挂了,剩下的 1 个永远达不到 quorum)。生产上建议 3 个或 5 个奇数节点,部署在不同机器上。
一个容易被忽略的坑:Sentinel 连接从节点也需要密码。如果 requirepass 和 masterauth 配置不一致,从节点升主后无法通过密码验证,导致故障转移失败。
# 必须保持一致
requirepass Redis123
masterauth Redis123Redis Cluster:去中心化分片
当单机内存超过 100GB,或者 QPS 超过单机上限(通常 10w+),就需要 Redis Cluster。Cluster 将数据分片到多个节点,每个节点负责一部分 Hash Slot。
# 创建 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 116384 个 Hash Slot 的由来:Redis 作者在集群设计文档中解释过,16384 个槽的心跳消息(CLUSTER NODES)大小约 2KB(bitmap 表示)。如果增加到 65536 个槽,心跳消息增大到 8KB,而集群节点数通常不超过 1000,16384 个槽已经足够每节点至少负责 16 个槽。更密集的槽位只会增加心跳对带宽的消耗,没有实际收益。
客户端路由:客户端连接任意节点,如果 key 的 Slot 落在当前节点,直接处理;否则返回 MOVED 重定向,客户端更新本地路由缓存,下次直接访问正确节点。
# 查看 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) 6379Cluster 的代价:跨 Slot 操作(如 MGET 多个 key 在不同 Slot)会报 CROSSSLOT 错误。解决方案是哈希标签(Hash Tag):{user:123}:key1 和 {user:123}:key2 用大括号包住的部分做 CRC16,确保同一用户的 key 落在同一 Slot。
# 使用哈希标签
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 256MB 和 min-replicas-to-write 1 做写入保护。超过 100GB 必须用 Cluster,提前规划好 Slot 分布和 Hash Tag 策略,把跨 Slot 操作的业务逻辑在客户端封装好。选型不是选最好的,是选最合适的——运维团队能力、数据量级、业务读写模型,这些比单纯的技术对比更重要。"
参考:Redis 官方文档 Cluster 规范、Redis Sentinel 文档、《Redis 设计与实现》第 14-16 章