在 Redis Cluster 中,keyspace 会被切分到 16384 个 hash slot,每个 slot 在任意时刻只由一个节点负责;集群通过 master/replica 复制实现高可用。(Redis)
slot 的计算基于 CRC16,并对 16384 取模。(Redis)
同时,Cluster 设计目标之一就是 无代理(no proxies)、不做服务端 merge 操作,客户端通过重定向(如 MOVED / ASK)连接到正确节点执行。(Redis)
这套设计带来一个典型现象:单机里很快的批量读(MGET / pipeline)到 Cluster 上,P99 抖动明显变大,甚至超时。
一、问题根因:跨 slot 的 fan-out 放大尾延迟
Redis Cluster 对“复杂多 key 操作”有明确边界:只有当涉及的 keys 都 hash 到同一 slot 时才支持;否则 multi-key 能力会不可用。(Redis)
因此,业务上一批相关 key 如果分散到多个 slot:
-
MGET/MSET这类 variadic multi-key 命令会受限(常见表现是CROSSSLOT)。 - 即便不使用
MGET,而是改成多次GET或 pipeline,本质上仍会变成 多节点请求 + 合并结果(scatter-gather),整体耗时由最慢节点拖累。
二、核心技术:Hash Tag 的作用是把 M 个节点收敛到 1 个节点
Cluster 通过 hash tag 提供“数据亲和性”:强制一组相关 key 落在同一 slot,从而允许 multi-key 操作并显著降低跨节点 fan-out。(Redis)
Hash Tag 规则(严格按 cluster-spec)
当且仅当满足以下条件时启用 {...}:(Redis)
- key 包含
{ -
{右侧存在} - 第一对
{与其右侧第一个}之间至少有 1 个字符
满足时:只对这段子串做 CRC16 以计算 slot。(Redis)
空
{}不生效:例如foo{}{bar}会对整串 key 正常哈希,而不是对空串哈希。(Redis)
三、收益来自哪里:减少跨节点 fan-out,而不是“Redis 执行更快”
示例(收藏场景):
优化前:
favorites:123:news001…favorites:123:news010
key 全名不同 → slot 大概率不同 → 原生MGET会因跨 slot 报错,而客户端模拟的批量操作会因抖动导致延迟剧增。优化后:
favorites:{...}:news001…favorites:{...}:news010
tag 相同 → 同一 slot → 同一节点 → 从“多节点并发 + 合并”收敛为“单节点一次返回”。
四、相关问题(工程边界与常见误解)
1){...} 内容相同会不会“值乱/冲突”?
不会。
Hash Tag 只影响 落在哪个 slot/节点,不会影响 key 的唯一性;是否覆盖只取决于 完整 key 字符串是否相同。
真正需要防的是:不同业务/不同服务没做命名空间隔离导致完整 key 撞名,而不是 tag 重复。
2)Tag 是否只能放一个 userId?
从正确性角度:只放 userId 完全可行。(Redis)
从负载与“亲和边界”角度:tag 的内容应表达“哪些 key 必须放在一起”。
如果多个业务域都用 {userId},同一用户的跨业务访问会更倾向集中到同一 slot(不乱值,但可能叠加热点)。更稳的做法是使用 复合 tag 限定亲和边界,例如:
favorites:{fav:123}:news001orders:{ord:123}:order8899
这样仍可保证“服务内/业务内” multi-key 能力,同时减少“跨业务不必要绑死在同一 slot”的概率。
3)即便用了 Hash Tag,混用不同 Tag 仍会失败
例如一次 MGET 混合 {u1} 与 {u2},本质上仍是跨 slot,multi-key 约束依然不满足。Redis Cluster 的 multi-key 能力边界是“同 slot”,hash tag 只是让“同 slot”变得可控。(Redis)
五、Key 命名规范与 Tag 设计准则(落地规范)
下面这段可以直接作为团队规范的“统一模板”。
1)命名空间(必须)
保证不同服务/不同模块不会撞 key。
推荐前缀结构:
<env>:<service>:<module>:...
示例:
prod:news:favorites:{fav:123}:news:001test:uc:session:{sess:uid123}:token
2)Tag 放置与内容(核心)
推荐结构:
<prefix>:{<affinity>}:<suffix>
其中 <affinity> 用于定义“亲和边界”,建议采用 业务域 + 主维度:
-
{fav:<userId>}:收藏业务按用户聚合 -
{ord:<orderId>}:订单业务按订单聚合 -
{cart:<userId>}:购物车按用户聚合
这样做的效果是:
- 同业务内可 multi-key(同 slot)
- 跨业务不会因为同一个 userId 产生不必要的 slot 绑定
注意:Cluster 只会使用第一对有效
{...}参与哈希;空{}会被当作无效,整串 key 哈希。(Redis)
3)字段与层级(可读性 + 可扩展)
建议用固定分隔符(常用 :),并把“对象类型/字段”显式写出来:
-
prod:news:favorites:{fav:123}:news:001(对象:news,id:001) -
prod:news:favorites:{fav:123}:meta(聚合元信息) -
prod:uc:profile:{uc:123}:base(用户基础资料)
4)版本号(建议)
当 value 结构可能升级时,加版本避免回滚/灰度混乱:
prod:news:favorites:v1:{fav:123}:news:001prod:news:favorites:v2:{fav:123}:news:001
5)热点与倾斜(必须评估)
Hash Tag 会把一组 key 固定到同一 slot;tag 粒度过粗会造成倾斜。Cluster-spec 也明确 hash tag 的用途是为了 multi-key,同样需要避免把大量不相关 key 强行塞到一个 slot。(Redis)
禁止示例:
-
{common}、{config}这类全局 tag - 用一个 tag 聚合“全站数据”
Top comments (0)