Redis Cluster 与 Hash Slot 原理

→ 返回 Redis

Redis Cluster 是去中心化的分片与高可用方案:数据按 16384 个 Hash Slot 分给多个主节点,每个主节点可配置从节点;客户端缓存 slot → node 路由表并直接访问目标节点,不需要一个固定代理承担所有请求。

                 客户端(缓存 slot 路由)
                 /          |          \
        Master A          Master B          Master C
       slot 0~5460      5461~10922       10923~16383
           │                │                 │
       Replica A'       Replica B'        Replica C'
 
节点间:Cluster Bus + Gossip + 故障检测 + 配置传播
redis-cli --cluster create host1:7000 host2:7001 host3:7002 \
  host4:7003 host5:7004 host6:7005 --cluster-replicas 1
redis-cli -c -p 7000 CLUSTER SHARDS
redis-cli -c -p 7000 CLUSTER NODES

Hash Slot 如何计算

普通 key 的槽位:

slot = CRC16_XMODEM(key) mod 16384
     = CRC16_XMODEM(key) & 0x3FFF

CRC16 产生 16 位结果,Redis Cluster 取低 14 位,因此槽位范围是 0~16383。槽是分片迁移和路由的最小逻辑单位;一个槽包含许多 key,迁移槽实际是逐个迁移属于该槽的 key。

为什么是 16384

这是路由粒度、集群消息大小与实际节点规模之间的工程折中:

  • 16384 = 2^14,取模可用位运算;
  • 节点可用 16384 bit(约 2 KiB)位图表达自己负责的槽,适合在集群消息中传播;
  • 槽数量远大于常见主节点数量,扩容时仍能较均匀地重新分配;
  • 槽过多会增加状态传播和管理成本,并不等于能支持同等数量的实用主节点。

Redis 官方建议的实际节点规模远小于 16384;16384 是槽数和理论主节点上限,不是推荐部署规模。

哈希标签 Hash Tag

若 key 包含第一个合法的 {...},只对花括号中的非空内容计算 CRC16:

CLUSTER KEYSLOT user:1
CLUSTER KEYSLOT '{user:1}:profile'
CLUSTER KEYSLOT '{user:1}:orders'
# 后两个 key 都只 hash user:1,因此落在同一 slot

Hash Tag 让多 key 命令、事务或 Lua 所需 key 落到同槽,但大量 key 使用同一个标签会制造热点,失去分片效果。


客户端路由与 MOVED

Cluster 客户端启动时通过 CLUSTER SHARDS(新客户端优先)或 CLUSTER SLOTS 获取路由表:

GET order:100
  │
  ├─ 客户端计算 slot,命中本地 slot → node 缓存
  ├─ 直接请求目标节点
  └─ 若路由过期,节点返回 -MOVED <slot> <host:port>
                         │
                         └─ 客户端刷新映射并重新请求

MOVED 表示槽的正式归属已经改变,客户端应更新槽映射。客户端通常会借此刷新整张路由表,因为故障切换或扩容往往同时改变一批槽。

普通 Redis 客户端不一定支持 Cluster;需要使用 JedisCluster、Lettuce Cluster、Redisson Cluster 等能计算槽位、维护拓扑并处理重定向的客户端。


在线迁槽与 ASK

把 slot 8 从 A 迁到 B 时,核心状态为:

A:slot 8 MIGRATING → B
B:slot 8 IMPORTING ← A

迁移并非瞬间完成:槽内已有 key 被分批从 A 搬到 B,这段时间同一槽的 key 可能暂时分布在两端。

  • A 上 key 仍存在:A 直接执行;
  • A 上 key 已迁走或不存在:A 返回 ASK 8 B;
  • 客户端仅对这一次请求向 B 先发送 ASKING,再执行原命令;
  • 全部 key 迁完并正式修改槽归属后,后续错误变为 MOVED。
ASK   = 临时重定向,不永久修改本地 slot 表
MOVED = 永久重定向,应更新 slot 表

迁槽期间多 key 操作更容易遇到 key 分处两端的问题,应在低峰执行、限制迁移速率并关注大 key。迁移单位虽然是槽,但网络传输单位最终仍是 key;一个超大 key 会拖慢迁移。


节点通信与 Gossip

Cluster 节点之间使用独立的集群总线传播:

  • 节点身份、角色、配置纪元和槽归属;
  • PING / PONG 心跳与随机节点信息;
  • MEET 加入集群;
  • 主观/客观故障信息与故障转移投票;
  • PUBLISH 等需要集群传播的信息。

Gossip 不要求每次心跳都携带完整拓扑,而是让信息逐步扩散。优点是没有单一元数据中心;代价是拓扑收敛需要时间,网络抖动时各节点可能短暂看到不同状态。

每个节点都有唯一 Node ID,并在本地保存整个集群的槽归属视图。配置纪元用于解决多个节点声称同一槽归属时的新旧冲突。


故障检测与副本提升

某节点长时间无响应
  ▼
多个节点传播 PFAIL(疑似故障)
  ▼ 达到主节点多数派判断
FAIL
  ▼
故障主节点的从节点延迟竞选
  ▼
主节点按配置纪元投票
  ▼
获多数票的从节点提升为主并接管原 slots

从节点会综合复制进度等条件决定是否有资格参与,避免数据落后太多的副本轻易成为新主。切换期间客户端通过 MOVED 和拓扑刷新学习新路由。

Cluster 仍是异步复制:旧主写入成功但尚未复制的命令可能在切换后丢失;网络分区中的少数派主节点也可能在短窗口内接收最终无法保留的写入。

cluster-node-timeout 影响故障检测与可用性,不应仅为了“切换更快”盲目调小,否则短暂抖动会增加误判和频繁 failover。


可用性与 full coverage

若某个槽没有可用主节点:

  • 开启 cluster-require-full-coverage yes 时,集群可拒绝全部 key 请求;
  • 关闭时,仍可继续服务有明确可用归属的其他槽,但应用会得到部分成功、部分失败。

这项选择体现一致性、故障隔离和业务可接受的部分可用之间的权衡。跨槽业务操作尤其要考虑部分失败和补偿。


跨槽限制与事务

节点只能原子地操作自己拥有的数据,所以多 key 命令通常要求所有 key 属于同一槽,否则返回 CROSSSLOT。

场景处理方式
MGET 多 key用 Hash Tag 放同槽,或由客户端按节点拆分并聚合
MULTI/EXEC涉及的 key 必须同槽;它也不提供跨节点事务
Lua / Function声明和访问的 key 应同槽,避免动态访问其他槽
分布式锁单锁 key 天然单槽;多资源锁需重新设计一致性边界
批处理Pipeline 按节点分组发送,不保证跨节点原子性

同槽只解决“能否在一个节点执行”,不自动解决数据库事务意义上的回滚、隔离和业务一致性。


热点、倾斜与大 key

问题原因应对
热 key单 key 必然只在一个槽和一个主节点本地缓存、请求合并、读副本(接受陈旧)、业务拆 key
热点槽一批高流量 key 碰巧同槽或共用 Hash Tag调整 key 标签、重新分散业务 key
数据倾斜少数槽包含大 key 或海量 key统计 per-slot key/内存,迁槽再平衡
大 key单 key 无法跨节点自动拆分按业务维度拆分;渐进扫描;UNLINK 异步删除

增加主节点不能自动拆散一个热 key;Cluster 的水平扩展单位是 key/slot,不是 Hash field、List 元素或 ZSet member。


诊断命令

CLUSTER INFO
CLUSTER NODES
CLUSTER SHARDS
CLUSTER KEYSLOT mykey
CLUSTER COUNTKEYSINSLOT 1234
redis-cli --cluster check host1:7000
redis-cli --cluster rebalance host1:7000

重点监控:槽是否完整覆盖、节点 PFAIL/FAIL、迁槽状态、重定向次数、复制延迟、单节点 QPS/内存、热点 key、大 key 与集群总线网络。


面试结论

Redis Cluster 把 key 空间划分为 16384 个槽,使用 CRC16 的低 14 位定位槽,再由槽路由到主节点。客户端维护槽表并直连节点;MOVED 表示永久归属变化,ASK 表示迁槽期间的一次性重定向。节点通过 Gossip 和配置纪元传播拓扑、检测故障并投票提升副本。Cluster 解决分片与高可用,但不解决跨槽事务、热 key 和异步复制窗口的数据丢失。


相关