Appearance
Redis Cluster
主从和 Sentinel 模式仍然是单主写入——所有写请求都落在同一个主库上,数据容量和写入吞吐受限于单机。Redis Cluster 通过分片把数据分散到多个主节点,每个主节点负责一部分 slot,从而扩大容量和写入吞吐。
一、16384 个 slot 的分片模型
Redis Cluster 把整个键空间切成 16384 个 slot。每个 key 通过 CRC16(key) % 16384 算出 slot 编号,slot 被分配给各个主节点:
text
key → hash slot → master node → replica node16384 这个数字的设计考虑:足够多让节点间分片比较均匀,又不太大导致 gossip 协议通信量过大(每个 slot 的迁移状态都需要在节点间传播)。
分片模型带来的限制:
- 多 key 命令(
MGET、MSET、SINTER等)要求所有 key 在同一个 slot。跨 slot 的多 key 操作会报CROSSSLOT错误。 - Cluster 只能使用 0 号数据库——
SELECT 1在 Cluster 模式下不可用。单机实例迁移到 Cluster 前,原来靠多个 DB 做逻辑隔离的应用需要改成不同 key 前缀或拆成不同实例。
二、节点配置和集群总线
每个节点的 Redis 配置:
conf
port 6379
bind 192.168.10.11
cluster-enabled yes
cluster-config-file nodes-6379.conf # 集群拓扑的自动维护文件,不要手工编辑
cluster-node-timeout 15000 # 节点超时判断,15 秒
appendonly yes
dir /data/redis除了 6379 端口,Cluster 节点还会占用一个集群总线(cluster bus)端口,默认是 Redis 端口 + 10000,即 16379。总线端口用于节点间的 gossip 通信——交换槽位分配、节点状态和故障检测信息。防火墙放行时两个端口都要开,很多 Cluster 搭建失败就是因为只开了 6379 没开 16379。
在 Docker、NAT、多网卡或跨网段环境中,节点对外通告的地址需要显式配置,否则 Redis 自动检测到的地址可能是内网 IP 或容器地址,其他节点连不过来:
conf
cluster-announce-ip 192.168.10.11 # 客户端和其他节点实际使用的地址
cluster-announce-port 6379
cluster-announce-bus-port 16379通告地址会影响 MOVED 重定向返回的地址、gossip 通信的目标地址。这里配错时服务端启动正常,客户端却可能被重定向到一个不可达的内网地址。
三、创建集群
最小生产部署是 3 主 3 从(每个主库有一个从库用于故障转移):
bash
redis-cli --cluster create \
192.168.10.11:6379 \
192.168.10.12:6379 \
192.168.10.13:6379 \
192.168.10.14:6379 \
192.168.10.15:6379 \
192.168.10.16:6379 \
--cluster-replicas 1--cluster-replicas 1 表示每个主库配 1 个从库。redis-cli --cluster create 会自动分配主从关系和 16384 个 slot。
检查集群状态:
bash
redis-cli -c -h 192.168.10.11 -p 6379 CLUSTER INFO
redis-cli -c -h 192.168.10.11 -p 6379 CLUSTER NODES
redis-cli --cluster check 192.168.10.11:6379-c 参数表示启用 Cluster 模式——遇到 MOVED 重定向时客户端会自动跳转到正确的节点。
CLUSTER INFO 关键字段:
| 字段 | 含义 |
|---|---|
cluster_state | ok 或 fail——集群整体是否可用 |
cluster_slots_assigned | 已分配的 slot 数,正常应该是 16384 |
cluster_slots_ok | 正常服务的 slot 数 |
cluster_slots_fail | 有故障的 slot 数,大于 0 说明某些 slot 无主 |
cluster_known_nodes | 集群已知的总节点数 |
四、slot 重定向——MOVED 和 ASK
key 不属于当前节点时,Redis 返回重定向。有两种:
MOVED:slot 已经确定归属到另一个节点。返回目标节点地址,客户端应该更新自己的 slot-node 映射表,后续请求直接发给正确节点。使用 -c 参数的 redis-cli 会自动处理。
ASK:slot 正在迁移中,当前这个 key 暂时在目标节点上。客户端应该只对这次请求发到目标节点,不要更新映射表——因为 slot 还没迁移完,其他 key 可能还在当前节点。
查看 key 属于哪个 slot:
bash
redis-cli -c CLUSTER KEYSLOT user:1001应用接入 Cluster 时要用支持 Cluster 的客户端(JedisCluster、Lettuce 的 Cluster 模式等)。把 Cluster 当单机 Redis 连,很快就会遇到 MOVED 错误。
五、Hash Tag——让多个 key 落在同一个 slot
Cluster 下跨 slot 的多 key 命令会报错。如果确实需要把某几个 key 放一起操作(比如一个用户的多个属性),可以用 hash tag 控制它们落入同一个 slot:
bash
redis-cli -c CLUSTER KEYSLOT 'user:{1001}:profile'
redis-cli -c CLUSTER KEYSLOT 'user:{1001}:tokens'花括号 {} 里的内容参与 CRC16 计算,所以 user:{1001}:profile 和 user:{1001}:tokens 会落在同一个 slot,MGET 就能用了。
hash tag 只适合少量确实需要一起操作的 key。如果滥用——比如所有 key 都用 {common} 标签——大量数据会集中到一个 slot,Cluster 的分片效果就被抵消了,那个 slot 所在节点成为热点。
六、扩容——加节点 + 迁移 slot
扩容分两步:加节点,然后迁移一部分 slot 到新节点。
bash
# 1. 把新节点加入集群
redis-cli --cluster add-node 192.168.10.17:6379 192.168.10.11:6379
# 2. 迁移 slot 到新节点(交互式)
redis-cli --cluster reshard 192.168.10.11:6379reshard 会交互询问:迁移多少 slot、目标节点是哪个、从哪里迁。迁移过程中 slot 状态在 importing/migrating 之间切换,客户端可能遇到 ASK 重定向——Cluster 客户端会处理,但延迟可能略有上升。所以扩容窗口要观察应用错误率和延迟。
七、缩容——先迁走 slot,再删节点
缩容不能直接停节点——节点上有 slot,停了那些 slot 就不可用了。要先迁移 slot:
bash
# 1. 迁移节点上的所有 slot 到其他节点
redis-cli --cluster reshard 192.168.10.11:6379
# 2. 确认节点没有 slot
redis-cli -c CLUSTER NODES | grep 192.168.10.17
# 3. 删除节点
redis-cli --cluster del-node 192.168.10.11:6379 <node-id>node-id 从 CLUSTER NODES 输出里获取——那串 40 字符的十六进制 ID。
八、故障转移
Cluster 自带故障检测和转移。主库故障后,如果它有从库,集群会选举一个从库提升为主库,接管故障主库的 slot。
手工触发切换(用于维护窗口):
bash
# 在从库上执行,让它接管主库角色
redis-cli -h 192.168.10.14 -p 6379 CLUSTER FAILOVERCLUSTER FAILOVER 和 CLUSTER FAILOVER FORCE 的区别:前者是协商式,从库先和主库同步到最新再切换(数据安全优先);后者是强制式,从库不等同步完成直接提升(可用性优先,但可能丢数据)。
Cluster 的故障转移依赖多数主库能互相通信。网络分区时,少数派一侧的主库无法形成多数,会被认为故障——少数派一侧的从库也不会被提升。这是 Cluster 可用性模型的核心设计:宁可拒绝服务,也不产生两个互相不知道的"多数派"各自写数据(脑裂)。
九、常见问题
MOVED 错误
text
MOVED 3999 192.168.10.12:6379客户端不支持 Cluster(没有自动重定向)、cluster-announce-ip 配置错误导致返回了不可达地址、或者 slot 正在迁移中。排查顺序:先看客户端是否用了 Cluster 模式,再看 CLUSTER NODES 里通告的地址是否可达。
CLUSTERDOWN
text
CLUSTERDOWN The cluster is down集群整体不可用。原因:有 slot 没有分配(cluster_slots_assigned != 16384)、有 slot 的负责主库全部故障且无从库、多数主库之间无法通信(网络分区)。先 CLUSTER INFO 看 cluster_slots_ok 和 cluster_slots_fail,再 CLUSTER NODES 看哪些节点被标记了 fail。
CROSSSLOT
text
CROSSSLOT Keys in request don't hash to the same slot多 key 命令涉及的 key 不在同一个 slot。要么拆成单 key 命令分别执行,要么用 hash tag 让它们落到同一个 slot。
SELECT 不可用
text
ERR SELECT is not allowed in cluster modeCluster 只有 0 号数据库。应用配置里如果指定了 database: 1,迁移到 Cluster 后会直接报这个错。
十、巡检要点
bash
redis-cli --cluster check 192.168.10.11:6379
redis-cli -c -h 192.168.10.11 CLUSTER INFO
redis-cli -c -h 192.168.10.11 CLUSTER NODES巡检关注:
| 检查项 | 正常标准 |
|---|---|
| slot 覆盖率 | cluster_slots_assigned = 16384,cluster_slots_fail = 0 |
| 主库数量 | 和拓扑设计一致 |
| 从库分布 | 从库和对应主库在不同机器上(否则机器挂了主从一起挂) |
| 节点状态 | 无 fail 标记的节点 |
| 迁移状态 | 无残留的 importing/migrating slot |