土法炼钢兴趣小组的算法知识备份

【Redis / 缓存内核】Cluster:16384 slot · MOVED/ASK · 多 key 同槽

文章导航

分类入口
databasestorage
标签入口
#redis#cluster#hash-slot#moved#ask#consistent-hashing#crossslot#cluster-spec#7.4

目录

MGET a b 在单机 Redis 上理所当然;在 Cluster 上却可能返回 CROSSSLOT Keys in request don't hash to the same slot——这不是客户端装傻,而是 服务端把 keyspace 切成 16384 个 hash slot 后,多 key 命令只能在 同槽 内保持原子语义。运维扩容时看到 MOVEDASK 混用,若当成同一种错误,客户端路由表会被永久写错。

本文是「Redis / 缓存内核」系列第 13 篇(共 16 篇)。→ 系列目录

版本锚定:Redis OSS 7.4 / 8.xRedis cluster specification;源码 src/cluster.csrc/cluster.h


一、为何是 16384 slot:服务端分片 vs 客户端哈希

Memcached 与早期 Redis 代理常用 客户端一致性哈希(见 一致性哈希:Karger et al. 1997 环上虚拟节点)。Redis Cluster 选择 服务端知晓 slot 映射:每个 master 负责 slot 子集,客户端(或智能驱动)缓存 slot → 节点 表,命令先算 slot 再直连目标节点。

维度 客户端一致性哈希(典型 Memcached) Redis Cluster hash slot
分片元数据 客户端维护环 集群 CLUSTER SLOTS / 节点 gossip
重分片 环变化 → 大量 key 迁移 按 slot 迁移;MOVED 更新映射
多 key 原子 通常 跨节点事务 同 slot 内保留 MULTI/MGET
槽数量 由虚拟节点数决定 固定 16384\(2^{14}\)

16384 的来源在 Cluster specification:用 CRC16 对 key 哈希后取低 14 位(& 0x3FFF),在 心跳包大小足够细粒度迁移 之间折中;官方建议集群规模约 千级节点 上限,而非 16384 个 master。

/* cluster.h — keyHashSlot 核心:CRC16(key) & 0x3FFF */
return crc16(key, keylen) & 0x3FFF;

二、MOVED 与 ASK:永久迁移 vs 一次性询问

Cluster 节点 不代理 请求:客户端必须直连持有 slot 的 master(读可从 replica 走 READONLY + MOVED 规则,细节见官方 spec)。

stateDiagram-v2
  [*] --> Stable: slot on node A
  Stable --> Migrating: CLUSTER SETSLOT MIGRATING
  Migrating --> Importing: dest marks IMPORTING
  Importing --> Stable: migration done SETSLOT NODE
  note right of Migrating
    key missing on A: -ASK to B
    slot owner changed: -MOVED
  end note
重定向 语义 客户端动作
-MOVED <slot> <ip>:<port> slot 永久 归新节点 更新 slot 映射,重试
-ASK <slot> <ip>:<port> 迁移进行中,仅下一跳 去问目标 向目标发 ASKING,再发原命令; 更新永久映射
-TRYAGAIN 迁移中多 key 部分存在 退避重试(spec:部分 key 已迁走)

ASKING 命令在 cluster.c 中设置客户端一次性标志,使 IMPORTING 节点可服务「尚未在源节点、或已在目标」的 key;若无 ASKING,IMPORTING 节点对非本槽请求返回 MOVED,防止陈旧映射的客户端把写打到错误节点造成 双写版本


三、hash tag 与同槽多 key

keyHashSlot() 对包含 {...} 的 key 只哈希花括号内子串(若 {} 不成对则哈希整 key)。设计意图是让用户把关联 key 钉在同一 slot:

user:{10086}:profile
user:{10086}:orders

于是 MGETMULTI/EXECSUNIONSTORE多 key 命令可在单 slot 内执行。没有 hash tag 时,(key1, key2) 落槽独立随机,CROSSSLOT 是预期行为而非配置错误。

边界:hash tag 解决的是 键共址,不解决 跨槽事务;resharding 期间同 tag key 仍可能遭遇 ASK/TRYAGAIN,应用需使用 Cluster-aware 客户端并处理重试。


四、cluster.c 路由与命令检查

getNodeByQuery()cluster.c)根据命令的 key 数量与 slot 计算结果返回 CLUSTER_REDIR_NONE / MOVED / ASK / CROSSSLOT / TRYAGAINclusterRedirectClient() 把错误码编码为客户端可见的 -MOVED / -ASK 行。

Dict 渐进 rehash 对比:单机上 rehash 是 进程内 摊销;Cluster slot 迁移是 跨节点 MIGRATE 原子搬 key,时长与 key 数量、值大小线性相关——大 key 会拖慢槽迁移(链第 15 篇)。


五、槽分配、resharding 与客户端

集群初次创建时,redis-cli --cluster create 会把 16384 slot 分到各 master;扩容用 --cluster add-node--cluster reshard,按 slot 粒度 而非单 key 随机迁移。官方建议迁移期间使用 Cluster-aware 客户端(redis-cli -c、各语言 RedisCluster 驱动),由库解析 MOVED/ASK

手动诊断命令(生产慎用 CLUSTER RESET):

redis-cli CLUSTER INFO
redis-cli CLUSTER SLOTS
redis-cli CLUSTER KEYSLOT mykey
redis-cli CLUSTER GETKEYSINSLOT 8 10
redis-cli CLUSTER NODES

CLUSTER KEYSLOT 验证 hash tag 是否把多 key 钉到同槽:对 user:{42}:auser:{42}:b,二者应返回相同 slot 编号。resharding 期间对同槽 MGET 仍可能遇 TRYAGAIN,应用需退避重试——这与单机 MULTI 永远成功不同。

一致性哈希 对照:Jump Hash / Maglev 追求 成员变更时最小 key 搬家;Redis 用 显式 slot 迁移 换取运维可暂停、可限流、可观测的 MIGRATE 进度,二者优化目标不同,不宜用「环更优雅」否定 slot 模型。


六、与复制、Sentinel 的关系

每个 master slot 持有者可有 replica第 12 篇 复制语义不变)。故障转移由 Cluster 内置 raft-like 选举与配置纪元(configEpoch)协调,与 Sentinel 模式 二选一 部署(第 16 篇决策树)。

复制 lag 与 slot 迁移叠加时:读 replica 可能同时 滞后于主遭遇 ASK;排障需分清是 路由 问题还是 复制 问题。

Sentinel 模式(第 16 篇)在 非 Cluster 部署里提供 master 监视与故障转移,客户端通过 Sentinel 发现当前 master 地址;Cluster 则自带 master 选举,不应 在同一组节点上混用两套逻辑。选型上:单分片 + 自动 failover → Sentinel;写/内存要水平切 → Cluster。官方 Cluster tutorialSentinel 文档应分开阅读,避免把 Sentinel 的「监控 master」误当成 Cluster 的「slot 所有者」。


七、常见误解

误解 事实
「Cluster 自动帮我把 key 均匀分布」 均匀性取决于 slot 分配key 哈希;热点 key 仍可打满单 slot
「MOVED 和 ASK 可以一样处理」 ASK 不能更新永久映射,否则迁移期会把流量错误钉在 IMPORTING 节点
「16384 个 slot 就要 16384 台机器」 一 master 可持多 slot;16384 是 逻辑槽 上限
「用 proxy 就能随便跨槽事务」 Proxy 需拆命令或拒绝 CROSSSLOT;原子性范围不会 magically 扩大
「一致性哈希与 Cluster 可以混用」 Cluster 客户端应用 slot 表;自行环哈希会导致 找不到数据

八、学术谱系、争论与开放问题

谱系:Karger 一致性哈希(1997)→ 客户端分片 Memcached → Redis Cluster Spec(Antirez / Redis Ltd. 文档)固定 16384 slot + MOVED/ASK 状态机 → 工业界 proxy(twemproxy、Envoy 等)在客户端与 spec 之间折中。

争论

  1. 固定 slot vs 动态环:Jump Consistent Hash、Maglev(见 algorithms/11)在 成员变化时迁移量 上更优,但 Redis Cluster 用 显式 slot 迁移 换取 可运维、可限流 的 resharding(redis-cli --cluster reshard)。
  2. 多 key 原子是否值得:强事务需求往往转向 PostgreSQL / TiKV(RocksDB 嵌入路径),Redis Cluster 坚持 同槽 而非两阶段提交,是刻意的复杂度边界。

开放问题


九、小结

三句话小结

  1. Redis Cluster 用 CRC16 mod 16384 把 key 钉到 hash slot,多 key 命令要 同槽 才保原子;{tag} 是工程上共址手段。
  2. MOVED 改永久路由,ASK 只服务迁移中单次请求;必须配合 ASKING,二者不可混为一谈。
  3. 服务端 slot 分片与 Memcached 客户端一致性哈希 是不同范式;扩容时要按 spec 更新 slot 映射,而不是只改环上虚拟节点。

下一篇对照另一套缓存范式:Memcached:多线程 · slab


参考资料

规范与源码(A 级)

  1. Redis Documentation, Redis cluster specification(16384 slots、MOVED/ASKASKING、迁移状态机)。
  2. Redis OSS 7.4 src/cluster.hCLUSTER_SLOTSkeyHashSlotCLUSTER_REDIR_*
  3. Redis OSS 7.4 src/cluster.cgetNodeByQueryclusterRedirectClient
  4. Redis Commands, CLUSTER SETSLOTASKINGMIGRATE

论文(A 级)

  1. Karger, D. et al., Consistent Hashing and Random Trees, STOC 1997(客户端分片谱系对照)。

辅助(B 级)

  1. 一致性哈希(虚拟节点、Jump Hash 与 Cluster 差异)。
  2. 复制系列 index

返回 系列目录 | 上一篇:复制 | 下一篇:Memcached 对照

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。


By .