Skip to content

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 node

16384 这个数字的设计考虑:足够多让节点间分片比较均匀,又不太大导致 gossip 协议通信量过大(每个 slot 的迁移状态都需要在节点间传播)。

分片模型带来的限制:

  • 多 key 命令(MGETMSETSINTER 等)要求所有 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_stateok 或 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}:profileuser:{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:6379

reshard 会交互询问:迁移多少 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-idCLUSTER NODES 输出里获取——那串 40 字符的十六进制 ID。

八、故障转移

Cluster 自带故障检测和转移。主库故障后,如果它有从库,集群会选举一个从库提升为主库,接管故障主库的 slot。

手工触发切换(用于维护窗口):

bash
# 在从库上执行,让它接管主库角色
redis-cli -h 192.168.10.14 -p 6379 CLUSTER FAILOVER

CLUSTER FAILOVERCLUSTER 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 INFOcluster_slots_okcluster_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 mode

Cluster 只有 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