问题: 高可用架构之集群模式:Redis Cluster 如何通过哈希槽(16384 个 slot)实现数据分片?在集群模式下,多 key 操作(如 mget)存在什么限制?如何通过 hash tag 绕过?

👇 点击展开/折叠 面试背诵模板(建议先背这个) Redis Cluster 采用无中心架构,通过虚拟哈希槽(Hash Slot)实现数据分片,固定划分 16384 个 slot,每个 key 经 CRC16 校验后对 16384 取模确定所属 slot,集群节点各自负责一部分 slot 区间。多 key 操作(如 mget、mset 或事务)要求所有 key 必须在同一 slot 中,否则返回 CROSSSLOT 错误。通过 hash tag 可强制多个 key 落入同一 slot,即只对 key 中 {} 包裹的部分计算哈希槽(如 {user:1001}:name 和 {user:1001}:age),从而绕过跨 slot 限制。
---

哈希槽的数据分布与节点映射

  • 每个 key 的 slot 计算:HASH_SLOT = CRC16(key) & 16383(等价于 % 16384)
  • 每个节点负责一段连续的 slot 区间(如 Node A: 0-5000, Node B: 5001-10000, Node C: 10001-16383)
  • 节点扩缩容:只需将部分 slot 从旧节点迁移到新节点,无需对所有 key 进行全局 rehash(一致性哈希也类似,但 slot 机制更简单)
  • 每个节点存储其他节点的 slot 分布信息,客户端通过 CLUSTER SLOTS 命令获取集群拓扑并缓存,实现智能路由

为什么是 16384 个 slot?

  • 恰好是 2^14,位运算效率高(& 16383 比 % 16384 更快)
  • 心跳包(Gossip 消息)需携带节点所负责的 slot 位图:16384 bits = 2KB,若改用 65536 个 slot,位图为 8KB,网络开销更大
  • 官方设计权衡:满足中等规模集群(官方建议 ≤ 1000 节点)的同时保证心跳包足够小

多 key 操作的跨 slot 限制详解

Redis Cluster 要求涉及多个 key 的命令(如 MGET、MSET、DEL、事务 MULTI/EXEC、SUNION 等)所有 key 必须属于同一个 slot,否则服务端返回 CROSSSLOT 错误。

示例:

1
2
3
# 假设 key1 在 slot 1234,key2 在 slot 5678(不同节点)
MGET key1 key2
# → (error) CROSSSLOT Keys in request don't hash to the same slot

为何有此限制?

  • 不同 slot 可能分布在不同节点,多 key 操作需要同时访问多个节点,协调开销极大,破坏原子性保证
  • 分布式事务实现复杂,Redis 为保持简单,直接禁止跨 slot 的原子多 key 操作

例外情况:MGET/MSET 等在某些语言客户端中可能会被拆分为多个单 key 命令串行执行,但这只是客户端层面的补偿,不保证原子性。

Hash Tag 的使用方式与示例

Hash Tag 是一种显式指定哈希计算目标的机制:只对 key 中 {} 包裹的部分进行 CRC16 计算。

1
2
3
4
5
6
7
8
9
10
# 普通 key:对整个 "user:1001:name" 计算哈希 → slot 可能分散
SET user:1001:name "Alice" # slot A
SET user:1001:age "25" # slot B(可能不同节点)

# 使用 hash tag:只对 "user:1001" 计算哈希 → 两个 key 落入同一 slot
SET {user:1001}:name "Alice" # slot X
SET {user:1001}:age "25" # slot X(同上)

# 此时 MGET 可用
MGET {user:1001}:name {user:1001}:age # ✅ 同一 slot,可执行

多个花括号的处理规则:若 key 中包含多个 {},只取第一个花括号内的内容作为哈希标签(如 {user}1001:{profile} → 仅 user 参与哈希)。

使用场景:

  • 将用户相关数据(档案、订单、购物车)强制部署在同一节点
  • 实现跨 key 事务(需保证相关数据在同一 slot)
  • 配合 SORT、SUNION 等集合操作,确保操作合法

集群模式下客户端如何工作?

智能客户端路由:

  1. 首次连接时执行 CLUSTER SLOTS 获取 slot 到节点的映射并缓存
  2. 执行命令时本地计算 slot,直接连接对应节点
  3. 若节点返回 MOVED 错误(slot 已迁移),客户端更新缓存并重试
  4. 若返回 ASK(slot 正在迁移中),客户端向目标节点发送 ASKING 命令后重试

JDBC 类比:类似 ShardingSphere 的 SQL 解析与分片路由,底层逻辑相同。

集群模式下的数据迁移过程

当集群新增节点或删除节点时,需要进行 slot 迁移(resharding):

  • 迁移单位:slot(每次迁移一个 slot 中的全部 key)
  • 迁移指令:CLUSTER SETSLOT <slot> MIGRATING <target_node_id>
  • 迁移过程:源节点和新节点建立同步,将 key 逐个发送到目标节点(通过 MIGRATE 命令)
  • 迁移期间:客户端的请求会返回 ASK 或 MOVED 错误引导重试,保证服务不中断

故障转移与高可用机制

  • 集群中每个节点与其他节点保持 TCP 连接,通过 Gossip 协议交换状态信息(ping/pong 消息)
  • 节点可配置主从复制(cluster replicate <node_id>),为 master 节点添加 slave
  • 当主节点失联(超过 cluster-node-timeout 且多数节点确认)时,slave 发起故障转移:
    1. 从所有 slave 中选举新 master(Raft 风格投票,比较偏移量)
    2. 新 master 接管该节点负责的 slot
    3. 集群恢复正常状态