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

Redis Cluster 多 key Lua 怎样保持同槽

来源:17golang原创

时间:2026-10-19 21:28:21 491浏览 收藏

Redis Cluster 中,多 key Lua 能否执行,关键不在 Lua 语法,而在这些 key 是否落到同一个 hash slot。实用做法是把同一个业务实体的标识放进花括号,例如 order:{42}:meta 和 order:{42}:items。Redis 只对 {42} 计算槽位,两个 key 就能被路由到同一节点。

官方文档:https://redis.io/docs/latest/

要点速览
  • hash tag 只影响花括号内的非空片段,业务前缀和后缀仍可保留。
  • EVAL 的前两个参数是脚本和 numkeys;真实 key 放入 KEYS,阈值等普通值放入 ARGV。
  • 同槽只解决集群路由前提,不代表脚本可以无限变长;Lua 仍会在 Redis 主线程中阻塞其他请求。

用 hash tag 让相关 key 落到同一个槽

Redis Cluster 把 key 空间分成 16384 个槽。普通 key 按整个 key 计算;当 key 中存在一对花括号,且中间有内容时,Redis 只对第一对有效花括号中的内容计算槽位。因此下面两组 key 的结果不同:

key 设计槽位关系适合场景
order:42:meta、order:42:items通常不同槽单 key 读写或客户端分别访问
order:{42}:meta、order:{42}:items共同使用 42需要多 key Lua、事务或多 key 命令
Redis Cluster 中 order 花括号 key、共同 hash tag、CRC16 槽计算与多 key Lua 的关系说明图
图1:Redis Cluster hash tag 与同槽关系说明图,不是运行截图或执行证据。

设计时要先找出真正需要原子处理的业务边界,再决定 tag。不要把所有用户都写成同一个固定 tag,否则虽然能避免 CROSSSLOT,却会把流量和数据集中到一个槽,形成热点。常见做法是以订单号、购物车号或租户号作为 tag,并保持同一实体的 key 模板稳定。

EVAL 的 KEYS 与 ARGV 必须分工

Redis 官方脚本约定要求:脚本会访问的所有 key 都由调用方显式传入;key 名称进入 KEYS,其他值进入 ARGV。在 Cluster 下,这个约定不仅是可读性问题,也是客户端计算路由和服务端判断同槽关系的输入。

-- 第二个参数 2 表示后面有两个 Redis key
EVAL "
  -- KEYS 只放脚本真正访问的 key
  local meta = redis.call('HGET', KEYS[1], 'status')
  local count = redis.call('INCRBY', KEYS[2], ARGV[1])
  -- 返回两个结果,便于客户端一次处理
  return {meta, count}
" 2 order:{42}:meta order:{42}:counter 1

这段调用中,order:{42}:meta 和 order:{42}:counter 是两个显式 key;1 是增量值,属于 ARGV[1]。不要在 Lua 中根据 ARGV 拼出另一个 key,也不要把 key 放进 ARGV 后再访问。这样做会让集群无法提前知道脚本涉及哪些槽。

Redis EVAL 的客户端脚本、numkeys、KEYS、ARGV 与 redis.call 参数边界说明图
图2:EVAL、KEYS 与 ARGV 的参数关系说明图,不是运行截图或执行证据。

用 CLUSTER KEYSLOT 做发布前检查

不需要先执行 Lua,就可以用 CLUSTER KEYSLOT 检查 key 的槽位。发布前至少检查同一脚本的全部 KEYS,并把检查写进测试或配置生成流程:

# 检查两个相关 key 是否使用同一个 hash tag
redis-cli -c CLUSTER KEYSLOT 'order:{42}:meta'
redis-cli -c CLUSTER KEYSLOT 'order:{42}:counter'

# 反例:没有共同 tag,两个 key 可能被分到不同槽
redis-cli -c CLUSTER KEYSLOT 'order:42:meta'
redis-cli -c CLUSTER KEYSLOT 'order:42:counter'

这里的输出只用于比对槽号,不应把某个具体槽号写死在业务代码里,因为重新分片会改变槽到节点的归属。真正稳定的是 key 的 hash tag 规则,而不是当前节点地址。

同槽之后仍要留意的边界

Lua 脚本在 Redis 中具有原子执行语义,但执行期间服务器会被脚本占用。脚本应保持短小,避免大范围遍历、不可控的集合操作和一次处理过多元素。若业务天然需要跨用户、跨租户聚合,不要为了强行同槽而使用一个全局 tag;可以改成客户端分片后汇总,或重新建模一个明确的聚合 key。

另外,hash tag 解决的是 key 的路由位置,不会替客户端库自动修正错误的参数顺序。使用 EVALSHA 时也要保持同样的 KEYS/ARGV 约定,并准备好脚本缓存丢失后的重新加载策略。

常见问题

为什么两个 key 看起来都包含订单号,仍然报 CROSSSLOT?

Redis 按完整 key 计算时,order:42:meta 与 order:42:counter 的前缀相似并不等于同槽。需要把共同标识写进同一对非空花括号。

可以把所有参数都放进 ARGV 吗?

不能。脚本实际访问的 key 必须进入 KEYS;ARGV 只适合数值、状态、内容等普通参数。

hash tag 会不会让集群失去分片效果?

只要 tag 使用订单号、用户号等分散的业务标识,仍可分布到多个槽。固定写成同一个 tag 才会制造热点。

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