谈谈 Redis 集群模式?
结论:Redis 的集群能力演进是三步:主从复制解决读扩展与数据备份,哨兵解决主节点故障自动切换,Cluster 解决水平分片与容量扩展。三者不是替代关系,Cluster 的每个分片内部依然是一主多从。选型看两点:单机能装下就"主从 + 哨兵",装不下或单节点写入打满才上 Cluster;不想改客户端就用代理层方案。
一、三种模式对照
| 维度 | 主从复制 | 哨兵(Sentinel) | Cluster |
|---|---|---|---|
| 解决的问题 | 读扩展、数据冗余 | 自动故障转移 | 分片 + 高可用 |
| 数据分布 | 全量复制,每节点都有全量 | 同主从 | 16384 槽分散到多个主节点 |
| 写能力 | 单主,不可扩展 | 单主 | 多主并行,可扩展 |
| 故障转移 | 手动 | 哨兵自动选举 | 集群内部自动 failover |
| 客户端复杂度 | 低 | 中(需连哨兵发现主) | 中(需处理 MOVED/ASK) |
| 跨 key 操作 | 无限制 | 无限制 | 必须在同一槽 |
| 最小规模 | 1 主 1 从 | 3 哨兵 + 1 主 1 从 | 3 主 3 从 |
二、Cluster 的分片与路由
┌─────────────────────────────────────────────┐
│ Cluster(16384 slots) │
└───────┬──────────────┬──────────────┬───────┘
│ │ │
┌───────▼──────┐ ┌─────▼────────┐ ┌───▼──────────┐
│ 主A slot │ │ 主B slot │ │ 主C slot │
│ 0-5460 │ │ 5461-10922 │ │ 10923-16383 │
└───────┬──────┘ └─────┬────────┘ └───┬──────────┘
│ │ │
┌───────▼──────┐ ┌─────▼────────┐ ┌───▼──────────┐
│ 从A' │ │ 从B' │ │ 从C' │
└──────────────┘ └──────────────┘ └──────────────┘
路由:slot = CRC16(key) % 16384
├─ 客户端已缓存槽映射 → 直接连对应节点
└─ 槽已迁移 → 节点返回 MOVED(永久迁移)或 ASK(临时迁移)
为什么是 16384 个槽而不是 65536?槽位图用位图表示,16384 个槽只占 2 KB,节点间 gossip 心跳要频繁携带槽位图,65536 会占 8 KB,心跳包与带宽开销翻四倍;而集群规模一般不超过 1000 个节点,16384 已经足够分散。
三、命令与配置
# 开启集群模式
redis-server --cluster-enabled yes --cluster-config-file nodes.conf \
--cluster-node-timeout 15000 --appendonly yes
# 创建 3 主 3 从(--cluster-replicas 1)
redis-cli --cluster create 10.0.0.1:6379 10.0.0.2:6379 10.0.0.3:6379 \
10.0.0.4:6379 10.0.0.5:6379 10.0.0.6:6379 --cluster-replicas 1
# 查看与运维
redis-cli -c -h 10.0.0.1 -p 6379 CLUSTER INFO
redis-cli -c CLUSTER NODES
redis-cli --cluster check 10.0.0.1:6379
redis-cli --cluster reshard 10.0.0.1:6379 # 在线迁移槽
redis-cli --cluster add-node 10.0.0.7:6379 10.0.0.1:6379
# 集群相关配置
cluster-enabled yes
cluster-node-timeout 15000
cluster-require-full-coverage no # 有槽不可用时是否拒绝整个集群写入
cluster-allow-reads-when-down no # 集群不可用时是否允许读
cluster-migration-barrier 1
四、常见追问
- 问:
cluster-require-full-coverage yes有什么风险?答:任意一个槽不可用(某主节点及其从节点全挂)整个集群就拒绝所有写入,可用性极差;通常设为 no,让未受影响的槽继续服务。 - 问:MOVED 和 ASK 的区别?答:MOVED 表示槽已永久迁到新节点,客户端应更新本地槽缓存;ASK 表示槽正在迁移中,本次请求临时去目标节点,客户端不更新缓存。
- 问:为什么跨 key 命令在 Cluster 下会失败?答:不同 key 可能落在不同槽、不同节点,MGET/MSET/事务/Lua 无法保证原子与一致性,Redis 直接返回 CROSSSLOT。用 hash tag
{user:1}把相关 key 固定到同一槽。 - 问:Cluster 下还能用 Lua 吗?答:能,但脚本里的所有 key 必须同槽;
KEYS与ARGV的构造要保证这一点。 - 问:哨兵和 Cluster 怎么选?答:数据量小于单机内存、写入不瓶颈 → 主从 + 哨兵,运维简单;需要水平扩容或单主写入打满 → Cluster。
- 问:Cluster 的故障转移靠什么判定?答:节点间 gossip +
cluster-node-timeout,多数主节点认为某主不可达后,由其从节点发起选举,获得多数票后晋升。
小结
- 主从管复制、哨兵管切换、Cluster 管分片,能力逐层叠加而非互斥。
- Cluster 的心智负担集中在"槽":路由、迁移、同槽限制、全槽可用性开关。
- 选型先看容量与写入是否超出单机,再评估客户端与运维是否能接受 Cluster 的限制。