登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  数据库 >  Redis

Redis Cluster 多键操作如何设计相同哈希槽

来源:17golang原创

时间:2026-10-09 06:43:49 320浏览 收藏

Redis Cluster 多键操作的关键不是把 key 前缀写得一样,而是让相关 key 的“哈希标签”一致。例如把用户资料、购物车和未读数分别命名为 user:{10086}:profile、user:{10086}:cart、user:{10086}:unread,三者都会按 10086 计算槽位,才能安全用于同一次 MGET、事务或 Lua 调用。

官方地址:https://redis.io/

要点速览
  • 多键命令要求涉及的 key 位于同一个 hash slot,否则会出现 CROSSSLOT。
  • Redis Cluster 只使用第一个有效 {...} 中的内容计算槽位,空标签不会生效。
  • 标签应绑定业务聚合 ID,而不是固定写成一个值;否则多键方便了,热点也集中到一个槽。

先把 CROSSSLOT 看成数据建模问题

Redis Cluster 将 key 空间拆成 16384 个槽,普通 key 按 CRC16(key) mod 16384 计算。单键 GET、SET 只需要定位一个槽;MGET、MULTI/EXEC、涉及多个 key 的 Lua 脚本则要求这些 key 能由同一个节点处理。若 user:10086:profile 与 user:10086:cart 没有标签,它们的完整字符串通常会得到不同槽位,客户端可能直接返回 CROSSSLOT Keys in request don't hash to the same slot。

因此,先列出一次操作真正需要的 key,再决定聚合边界。不要为了让任意 key 都能一起操作而使用固定标签,例如全部写成 {all};这会把写入和读取集中到一个槽,形成热点。

Redis Cluster 多键操作中业务聚合 ID、哈希标签与同一槽位的关系说明图
图1:Redis Cluster 哈希标签结构说明图,展示业务聚合 ID 如何让多个 key 指向同一槽位。

用稳定的业务 ID 设计哈希标签

标签的内容应该是一次多键操作共同拥有的业务主键:用户 ID、订单 ID、会话 ID 都可以;不要把会变化的状态、时间戳或随机数放进标签。推荐保持“资源类型 + 标签 + 资源名”的形状,让日志和排查仍然可读:

package keys

import "strconv"

// UserKeys 统一生成同一用户聚合下的 key,保证花括号内容完全一致。
func UserKeys(userID int64) (profile, cart, unread string) {
	// 标签只放稳定业务 ID;不要把状态或时间戳放进 {}。
	tag := "{" + strconv.FormatInt(userID, 10) + "}"
	return "user:" + tag + ":profile", "user:" + tag + ":cart", "user:" + tag + ":unread"
}

这段 builder 的价值在于把规则集中在一个地方。若一个服务写成 user:{10086}:cart,另一个服务读成 user:10086:unread,哪怕人眼看起来是同一用户,也已经失去同槽保证。跨团队共享时,应把 key builder 或标签约定作为接口契约。

在发送多键请求前做同槽核对

开发环境可以用 CLUSTER KEYSLOT key 对每个候选 key 取槽位;例如三个 key 都使用 {10086} 时,返回值应一致。生产代码不必每次额外请求 Redis 查询槽位,而是在本地 builder 中保证标签来源唯一,并让集群客户端负责维护槽位路由。

设计槽位结果适用场景
user:{10086}:profile + user:{10086}:cart同槽同一用户聚合读取、事务
user:10086:profile + user:10086:cart通常不同槽只能拆成单键请求或改 key 设计
{all}:user:10086 + {all}:user:20000同槽但易热点仅适合极小、低频的控制数据

注意标签算法只取第一个有效花括号内的内容:foo{user1000}.a 与 bar{user1000}.b 同槽;空的 {} 不会触发标签规则。多个花括号也不要依赖复杂嵌套,统一约定一个简单标签最容易排查。

Redis Cluster 多键请求的同槽检查、客户端路由与热点边界说明图
图2:多键请求边界结构说明图,展示 key builder、槽位检查、客户端路由和热点监控之间的关系。

压测时同时看同槽成功率和热点

同槽只是“请求可以被同一节点处理”,不等于设计一定健康。压测至少记录三组数据:多键请求的 CROSSSLOT 数量、各 master 的请求量/CPU,以及单个标签聚合的峰值访问量。若用户级标签分布均匀,通常比固定标签更容易横向扩展;若某个超级用户需要同时读写大量 key,则应拆分聚合边界,例如按 {userID}:cart 和 {userID}:feed:0 设计多个可独立访问的分片。

扩容或 resharding 期间,槽位会迁移,客户端还要正确处理 MOVED 与 ASK。不要把业务代码写成“收到错误就重试同一个节点”;应使用支持 Redis Cluster 的客户端,让它刷新槽位映射并把请求发往正确节点。哈希标签解决的是多键共槽,不会消除迁移、故障转移或热点治理的责任。

常见问题

哈希标签一定要放在 key 的中间吗?

不需要。只要存在第一个有效的 {非空内容},标签前后的业务文字都可以不同,常见做法是把资源类型放在前面、资源名放在后面。

两个 key 看起来一样却仍然 CROSSSLOT,先查什么?

先打印最终发送给 Redis 的完整字符串,重点检查花括号是否为空、标签是否含有不同前缀,以及是否有服务使用了另一套 key builder;不要只比较业务 ID。

能不能给所有 key 使用同一个标签?

技术上可以,但会牺牲集群分布和吞吐。只有确实属于同一小型控制聚合且访问量可控时才考虑,业务数据应按用户、订单或会话等稳定聚合 ID 分散。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>