Redis学习笔记:Cluster 分片集群从原理到实战部署
一、什么是 Redis Cluster
Redis Cluster 是 Redis 官方提供的分布式解决方案,从 Redis 3.0 开始引入。它通过数据分片(Sharding)和自动故障转移(Failover),实现了:
- 水平扩展:将数据自动分布到多个节点
- 高可用性:部分节点故障时集群仍可正常工作
- 去中心化架构:无中心代理节点,所有节点通过 Gossip 协议通信
解决了什么问题
| 问题 | 说明 |
|---|---|
| 单机容量瓶颈 | 单个 Redis 实例最多存储几个 GB 到几十 GB 数据,Cluster 可以将数据分散到多台机器 |
| 单点故障 | 主节点宕机后,从节点自动晋升,无需人工干预 |
| 读写压力 | 多主节点并行处理请求,写入吞吐量线性提升 |
| 扩容困难 | 传统方式需要停机迁移数据,Cluster 支持在线动态扩缩容 |
二、Redis 部署方案对比
| 特性 | 单机实例 | 主从复制 | Sentinel 哨兵 | Cluster 分片集群 |
|---|---|---|---|---|
| 数据容量 | 受单机内存限制 | 受单机内存限制 | 受单机内存限制 | 可扩展至多台机器 |
| 写入性能 | 单点写入 | 单点写入(主) | 单点写入(主) | 多主写入,线性扩展 |
| 自动故障转移 | ❌ | ❌ | ✅ | ✅ |
| 自动分片 | ❌ | ❌ | ❌ | ✅ |
| 在线扩缩容 | ❌ | ❌ | ❌ | ✅ |
| 配置复杂度 | ⭐ 简单 | ⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐ |
| 适用场景 | 开发/缓存 | 读写分离 | 高可用场景 | 大规模高可用场景 |
什么时候选择 Cluster?
- 数据量超过单机内存(建议单机 ≤ 16GB)
- 需要高写入吞吐量
- 要求自动故障转移和高可用性
- 希望在线扩缩容,避免停机
注意:如果数据量小且不需要高可用,单机或 Sentinel 更简单高效。Cluster 的事务和 Lua 脚本仅支持同一槽位的 Key。
三、核心架构原理
3.1 哈希槽(Hash Slot)
Redis Cluster 没有采用一致性哈希,而是使用固定 16384 个哈希槽(Hash Slot):
CRC16(key) % 16384 → 所属槽位
- 每个 Key 通过
CRC16算法计算出哈希值,对 16384 取模得到槽号 - 每个节点负责一段连续的槽位范围
- 新增/移除节点时,只需迁移槽位,不需要 rehash 所有数据
为什么是 16384 个槽?
Redis 作者在 FAQ 中解释过:16384 个槽用 2KB 的空间即可在节点间传递完整的槽位信息(16384 bit ÷ 8 ÷ 1024 = 2KB),而 65535 个槽则需要 8KB,在网络开销和灵活性之间取得了平衡。
3.2 Gossip 协议
Cluster 节点间通过 Gossip(八卦)协议 通信,每个节点:
- 每秒随机选择 5 个节点发送
PING - 交换节点状态、槽位信息、主从关系
- 如果收到
PONG超时,标记节点为PFAIL(可能故障) - 当多数主节点都标记某个节点为
PFAIL,升级为FAIL并触发故障转移
这种去中心化设计意味着没有单点瓶颈,但最终一致性意味着短时间内节点间可能存在信息不一致。
3.3 主从架构
每个主节点(Master)可以有 1 个或多个从节点(Slave):
- 主节点:负责处理读写请求和槽位数据
- 从节点:异步复制主节点数据,主节点宕机时选举晋升
- 建议为每个主节点至少配置一个从节点,保证高可用
四、环境搭建:Docker Compose
下面我们用 Docker Compose 启动 6 个 Redis 节点(3 主 3 从),这是生产推荐的最小集群配置。
version: '3.8'
services:
redis-node-1:
image: redis:7.2.4
container_name: redis-cluster-1
command: redis-server --port 6379 --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --appendonly yes --bind 0.0.0.0
ports:
- "6379:6379"
- "16379:16379"
networks:
- cluster-net
redis-node-2:
image: redis:7.2.4
container_name: redis-cluster-2
command: redis-server --port 6379 --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --appendonly yes --bind 0.0.0.0
ports:
- "6380:6379"
- "16380:16379"
networks:
- cluster-net
redis-node-3:
image: redis:7.2.4
container_name: redis-cluster-3
command: redis-server --port 6379 --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --appendonly yes --bind 0.0.0.0
ports:
- "6381:6379"
- "16381:16379"
networks:
- cluster-net
redis-node-4:
image: redis:7.2.4
container_name: redis-cluster-4
command: redis-server --port 6379 --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --appendonly yes --bind 0.0.0.0
ports:
- "6382:6379"
- "16382:16379"
networks:
- cluster-net
redis-node-5:
image: redis:7.2.4
container_name: redis-cluster-5
command: redis-server --port 6379 --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --appendonly yes --bind 0.0.0.0
ports:
- "6383:6379"
- "16383:16379"
networks:
- cluster-net
redis-node-6:
image: redis:7.2.4
container_name: redis-cluster-6
command: redis-server --port 6379 --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --appendonly yes --bind 0.0.0.0
ports:
- "6384:6379"
- "16384:16379"
networks:
- cluster-net
networks:
cluster-net:
driver: bridge
配置参数详解
| 参数 | 含义 |
|---|---|
--cluster-enabled yes |
启用集群模式 |
--cluster-config-file nodes.conf |
集群自动生成的配置文件(不需要手动创建) |
--cluster-node-timeout 5000 |
节点超时时间(ms),超时被视为故障 |
--appendonly yes |
开启 AOF 持久化 |
--bind 0.0.0.0 |
允许来自所有网络接口的连接 |
16379 端口 |
集群总线端口(数据端口 + 10000),用于节点间 Gossip 通信 |
五、创建分片集群
5.1 启动容器
docker-compose up -d
5.2 创建集群
进入任意一个容器并执行创建命令:
# 进入容器
docker exec -it redis-cluster-1 sh
# 创建集群(注意这里用的是容器名,不是 IP!)
redis-cli --cluster create \
redis-cluster-1:6379 \
redis-cluster-2:6379 \
redis-cluster-3:6379 \
redis-cluster-4:6379 \
redis-cluster-5:6379 \
redis-cluster-6:6379 \
--cluster-replicas 1
命令解析:
--cluster-replicas 1:每个主节点分配 1 个从节点,总共 6 个节点 → 3 主 3 从- 如果不加这个参数,6 个节点全为主节点(不推荐,无高可用)
执行后 Redis 会打印槽位分配方案并让你确认,输入 yes 继续。
5.3 参考输出
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica redis-cluster-4:6379 to redis-cluster-1:6379
Adding replica redis-cluster-5:6379 to redis-cluster-2:6379
Adding replica redis-cluster-6:6379 to redis-cluster-3:6379
...
[OK] All 16384 slots covered.
看到 [OK] All 16384 slots covered. 表示所有 16384 个槽位分配完毕,集群创建成功。
六、验证集群状态
6.1 查看节点信息
docker exec -it redis-cluster-1 redis-cli cluster nodes
输出示例:
16beb014f42bf997e7e9dc40e4a03d96588ca2f7 172.23.0.5:6379@16379 master - 0 1784104659539 2 connected 5461-10922
c2c4b89c1359f8b7fc200fb9fb3f5e591132461c 172.23.0.4:6379@16379 myself,master - 0 1784104658000 1 connected 0-5460
ff93232a5b0ce6abfd4c04dad4e778e47cf19d45 172.23.0.2:6379@16379 slave 16beb014f42bf997e7e9dc40e4a03d96588ca2f7 0 1784104659000 2 connected
d098439d4b4ad4a461777737e7ea2ab2d9591d1b 172.23.0.7:6379@16379 slave c2c4b89c1359f8b7fc200fb9fb3f5e591132461c 0 1784104659640 1 connected
5089042042bcc6ad2edf0b6dc07d5721c0c4fcf5 172.23.0.6:6379@16379 slave 7556fed402a74a5dd377365e16d41253d2e1ee99 0 1784104658000 3 connected
7556fed402a74a5dd377365e16d41253d2e1ee99 172.23.0.3:6379@16379 master - 0 1784104659000 3 connected 10923-16383
字段解读:
| 字段 | 示例 | 含义 |
|---|---|---|
| 节点 ID | 16beb0... |
节点唯一标识(持久化到 nodes.conf) |
| 地址 | 172.23.0.5:6379@16379 |
数据端口@集群总线端口 |
| 角色 | master / slave |
主节点或从节点 |
| 主节点 ID | 如 16beb0...(slave 行) |
该从节点所属的主节点 |
| 连接状态 | myself,master |
当前连接的节点是自己 |
| 槽位范围 | 0-5460 |
该节点负责的哈希槽范围 |
6.2 集群完整性检查
docker exec -it redis-cluster-1 redis-cli --cluster check redis-cluster-1:6379
输出:
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
七、数据读写与 MOVED 重定向
7.1 不使用集群模式
docker exec -it redis-cluster-1 redis-cli set foo bar
返回:
(error) MOVED 12182 172.23.0.3:6379
为什么会这样?
这个错误是 Redis Cluster 的典型行为:
- 客户端向
redis-cluster-1发送SET foo bar - Redis 对
foo计算哈希槽:CRC16("foo") % 16384 = 12182 redis-cluster-1不负责 12182 号槽(它负责 0-5460)- 它知道负责该槽的节点是
172.23.0.3:6379,返回MOVED错误 - 客户端应该重定向到正确节点执行命令
7.2 使用集群模式(-c 参数)
# 写入
docker exec -it redis-cluster-1 redis-cli -c set foo bar
# 读取(不需要在特定节点读)
docker exec -it redis-cluster-3 redis-cli -c get foo
-c(cluster 模式)让 redis-cli 自动跟随 MOVED 重定向,透明地连接到正确的节点执行命令。
生产建议:应用代码中应使用支持 Cluster 的 Redis 客户端(如
redis-py-cluster、Lettuce、JedisCluster),它们会自动处理 MOVED 重定向和槽位缓存。
7.3 查看 Key 的哈希槽
docker exec -it redis-cluster-1 redis-cli CLUSTER KEYSLOT foo
# 返回: (integer) 12182
7.4 查询所有槽位分布
docker exec -it redis-cluster-1 redis-cli CLUSTER SLOTS
输出示例:
1) 1) (integer) 0 <-- 起始槽位
2) (integer) 5460 <-- 结束槽位
3) 1) "172.23.0.2" <-- 主节点 IP
2) (integer) 6379
3) "node_id_1"
4) 1) "172.23.0.5" <-- 从节点 IP
2) (integer) 6379
3) "node_id_4"
2) 1) (integer) 5461
2) (integer) 10922
3) 1) "172.23.0.3"
2) (integer) 6379
...
八、故障转移
8.1 手动模拟故障
关闭一个主节点,观察从节点自动晋升:
# 停止一个主节点(比如 redis-cluster-1,它负责 0-5460 槽)
docker stop redis-cluster-1
# 稍等片刻(默认超时 5 秒),查看集群状态
docker exec -it redis-cluster-2 redis-cli cluster nodes
可以看到原本 redis-cluster-1 的从节点(如 172.23.0.2:6379)已经晋升为 master。
8.2 故障转移原理
- 主节点宕机后,从节点检测到
PING超时 - 从节点等待集群超时时间(
cluster-node-timeout,默认 5 秒) - 通过 Raft 类似算法 发起选举
- 获得多数主节点投票后晋升为新主节点
- 接管原主节点的所有槽位
8.3 恢复原主节点
# 重启之前关闭的节点
docker start redis-cluster-1
# 它会以从节点身份重新加入集群,成为新主节点的副本
九、Hash Tag:将关联 Key 存入同一槽位
9.1 为什么需要 Hash Tag?
Redis Cluster 的事务(MULTI/EXEC)和 Lua 脚本要求所有操作的 Key 必须在同一个哈希槽,否则会报错:
(error) CROSSSLOT Keys in request don't hash to the same slot
同时,批量操作(MGET/MSET)跨节点也有额外的网络开销。
9.2 Hash Tag 语法
Key 中 {...} 部分只使用花括号内的内容计算哈希槽:
# 这两个 Key 都会用 "user:1" 计算哈希槽
{user:1}:name
{user:1}:age
9.3 验证
# 查看两个 Key 的哈希槽(注意:不加 -c 不会自动重定向但 KEYSLOT 命令不受影响)
docker exec -it redis-cluster-1 redis-cli CLUSTER KEYSLOT {user:1}:name
docker exec -it redis-cluster-1 redis-cli CLUSTER KEYSLOT {user:1}:age
两个命令返回相同的槽位号。
9.4 实际使用
# 写入关联 Key
docker exec -it redis-cluster-1 redis-cli -c set {user:1}:name Alice
docker exec -it redis-cluster-1 redis-cli -c set {user:1}:age 30
# 批量读取(在同一槽位,效率更高)
docker exec -it redis-cluster-1 redis-cli -c mget {user:1}:name {user:1}:age
9.5 使用场景
| 场景 | 说明 |
|---|---|
| 事务操作 | MULTI/EXEC 需要所有 Key 在同一槽位 |
| Lua 脚本 | EVAL 脚本中所有 Key 必须在同一槽位 |
| 批量读写 | MGET/MSET 跨槽位需要多次网络请求 |
| 关联数据 | 用户信息、订单明细等高关联度数据 |
十、总结与最佳实践
关键要点
- Cluster 适用场景:数据量大、写入吞吐高、需要自动故障转移的大规模 Redis 部署
- 最小生产配置:6 节点(3 主 3 从),每个主节点至少一个副本
- 客户端必须支持 Cluster:使用
-c模式或集成 Cluster 客户端库 - 事务限制:MULTI/EXEC 和 Lua 脚本需要配合 Hash Tag
{...} - 节点数建议:主节点数量通常为奇数(方便选举投票),建议 3、5、7 个
注意事项
- 网络延迟:跨节点操作比单节点慢,合理设计 Key 分布
- 批量操作:尽量用 Hash Tag 让关联 Key 落在同一槽位
- 扩缩容:生产环境扩缩容时关注槽位迁移对性能的影响
- 监控:重点关注
cluster_state、cluster_slots_ok、cluster_known_nodes等指标

浙公网安备 33010602011771号