7.3 Cluster集群:16384个槽 本节摘要:Redis Cluster 把键空间切成 16384 个槽,按 CRC16 对键取模分配到多个主节点,每个主再挂从库兜底。节点间用 Gossip 协议交换拓扑,客户端按槽路由直连目标节点。分片换来了容量与写入的线性扩展,代价是多键命令受槽约束、部分场景下有重定向开销。 槽:分片的最小单位 为什么不按键直接分?键的数量是变化的,槽是固定的。固定 16384 个槽当作"抽屉",键按公式进抽屉,抽屉按节点分配——挪动数据时挪的是抽屉,关系清晰、迁移有界: 集群槽位分布与 MOVED 重定向 集群槽位分布与 MOVED 重定向 客户端首次访问后会把槽位表缓存在本地,此后请求直连目标节点,性能与单机几乎无异。
本节摘要:Redis Cluster 把键空间切成 16384 个槽,按 CRC16 对键取模分配到多个主节点,每个主再挂从库兜底。节点间用 Gossip 协议交换拓扑,客户端按槽路由直连目标节点。分片换来了容量与写入的线性扩展,代价是多键命令受槽约束、部分场景下有重定向开销。
为什么不按键直接分?键的数量是变化的,槽是固定的。固定 16384 个槽当作"抽屉",键按公式进抽屉,抽屉按节点分配——挪动数据时挪的是抽屉,关系清晰、迁移有界:
slot = CRC16(key) mod 16384 # 槽属于哪个节点,由集群的槽位表决定
> CLUSTER KEYSLOT user:1001 # 算键属于哪个槽 (integer) 9990 > CLUSTER COUNTKEYSINSLOT 9990 (integer) 2 > CLUSTER NODES # 看槽位在节点间的分布

客户端首次访问后会把槽位表缓存在本地,此后请求直连目标节点,性能与单机几乎无异。只有槽迁移的过渡期会出现 ASK 重定向。
MOVED 与 ASK 的区别值得单独说清,它是理解集群行为的关键一对。MOVED 是"户口已迁":目标键的槽已正式归新节点管,客户端收到后应更新本地槽表,以后都去新节点;ASK 是"搬家进行中":槽还挂在老节点名下,但这个键已经搬过去了,本次请求跟着重定向去新节点拿结果,槽表不用改。一个改认知、一个只改这一次——迁移完成后 ASK 自然消失。
# 迁移期在老节点上访问已搬走的键 > GET user:1001 (error) ASK 9990 10.0.0.12:6390 # 本次去新节点取,别改槽表 # 迁移完成后同样请求 > GET user:1001 (error) MOVED 9990 10.0.0.12:6390 # 槽已易主,更新槽表
每个节点开两个端口(如 6390 与 16390):服务端口对客户端,总线端口跑 Gossip 协议——节点间持续交换彼此的状态与槽位视图。集群没有哨兵那种专职裁判:某个主库多数节点都 ping 不通时,其余主库投票把它标记下线,它的从库发起选举接班——裁决权分散在每个主节点手里,仲裁与数据同置。
多键操作与事务要求所有键同槽,hash tag 由此而来:
# 花括号内的内容参与槽计算,以下三键同槽 SET {order:1001}.items "..." SET {order:1001}.buyer "..." ZADD {order:1001}.timeline 1 created # 现在可以对它们 MGET 或 MULTI 打包
hash tag 是把双刃剑,另一面就是数据倾斜。所有 {order:1001} 系列的键都挤进同一个槽、同一台机器——订单聚合操作方便了,但某个超大商户或爆款活动的键全落在一处时,那个节点先热先满。治理原则两条:tag 的粒度选"业务上确实需要原子操作的最小单位",能用键前缀分组就别用全局同一个 tag;上线前用 CLUSTER KEYSLOT 抽查热点业务的键分布,分布图上出现一根独高的柱子,就是倾斜的预警。
# 六个实例的最小配置(每台机器各自端口) cluster-enabled yes cluster-config-file nodes-6390.conf cluster-announce-ip 10.0.0.11 # 一条命令拉起集群 redis-cli --cluster create 10.0.0.11:6390 10.0.0.11:6391 \ 10.0.0.12:6390 10.0.0.12:6391 10.0.0.13:6390 10.0.0.13:6391 \ --cluster-replicas 1 redis-cli -c -p 6390 cluster info # cluster_state:ok 才算就绪
-c 开启集群跟随模式,重定向自动跳转;应用客户端用支持 Cluster 的库(连任一节点即可自动发现全拓扑)。
建群后的第一件事是核对槽位全覆盖。CLUSTER INFO 输出里的四个数字要连起来看:cluster_slots_assigned 应为 16384、cluster_slots_ok 应与 assigned 相等、cluster_slots_pfail 与 cluster_slots_fail 应为零。assigned 不满说明有槽没分配出去,部分键会无处安放;ok 不等于 assigned 说明有槽处于迁移中断或节点失联状态,都是要立刻处理的事。这张输出也适合直接接进巡检脚本,异常状态一目了然。
# 新节点入群并从拥挤节点匀槽过来 redis-cli --cluster add-node 10.0.0.14:6390 10.0.0.11:6390 redis-cli --cluster reshard 10.0.0.11:6390 # 交互式迁槽 redis-cli --cluster check 10.0.0.11:6390 # 检查槽完整性
迁移逐键进行、可断点续做,业务无感但会抖,避开高峰。缩容反着来:先把槽迁空再删节点。日常巡检盯三个数:cluster_state 是否 ok、各节点槽数是否均衡、每个主库是否都有健康从库。
cluster_state 为什么会变 fail,把算术讲透:集群要求过半主节点存活才继续服务。三主集群挂一台,剩两台,过半成立,服务继续;挂两台,剩一台,过半不成立,整个集群拒绝写入(默认配置下)。推而广之,五主容两台、七主容三台——容错数就是"节点数减一再除二"。规划集群规模时先回答"机房级故障要不要扛",答案决定主节点数下限。另一个相关参数 cluster-require-full-coverage 控制的是另一件事:部分槽无人负责时是否整体停服,默认 yes(任一槽缺失即拒绝写入),对可用性极端敏感的业务可改 no,代价是那些槽上的键彻底不可访问。
⚠️ 常见坑:一半以上主库同时不可用(如三主里挂两台)时集群整体拒绝写入——三主集群的容错只有一台,要更高容错就加主库数量;DB0 之外的多库在集群里被砍掉;Lua 与 pipeline 同样受同槽约束,跨槽会直接报错。