必须用{}哈希标签使所有KEYS落在同一slot,如user:{123}:profile和user:{123}:settings;KEYS必须显式传入,不可在脚本内拼接,否则因无法静态分析而报错。

Redis Cluster里Lua脚本报“keys must in same slot”怎么办
必须让所有涉及的 KEYS 落在同一个哈希槽,否则脚本直接被拒绝执行。Redis Cluster 不允许跨节点操作,而 Lua 脚本的原子性只存在于单节点内。
解决方法只有靠 {} hash tag 强制对齐:
-
user:{123}:profile、user:{123}:settings、user:{123}:balance会被算到同一 slot(因为只取{123}部分做 CRC16) - 不能写成
user:123:profile和order:123:log—— 这俩大概率落在不同节点,EVAL 会返回-ERR eval/evalsha command keys must in same slot - 用
CLUSTER KEYSLOT user:{123}:profile和CLUSTER KEYSLOT user:{123}:settings可验证是否一致
为什么传参必须用 KEYS/ARGV,不能拼接 key 名
Redis 在执行前要静态分析脚本依赖哪些 key,用于路由到对应节点。如果脚本里出现 redis.call("GET", "user:"..ARGV[1]),Redis 就无法提取出实际访问的 key,集群模式下直接报错:-ERR bad lua script for redis cluster, all the keys that the script uses should be passed using the KEYS array。
正确做法是:
- 所有 key 名必须作为
KEYS数组参数传入,脚本里只能用KEYS[1]、KEYS[2]等访问 - 非 key 的动态值(如金额、状态、过期时间)统一走
ARGV,避免字符串拼接引入风险 - 哪怕 key 名只差一个数字,也得提前列全 —— 比如删用户关联的 5 个 key,就得传 5 个参数进
KEYS,不能靠循环生成
怎样避免脚本执行中途写了一半就失败
Lua 脚本没有回滚机制,redis.call() 写入成功后就不可逆。常见错误是先写再判断,结果条件不满足但数据已脏。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
安全写法必须遵循“读 → 判 → 写”顺序:
- 所有
redis.call("GET", ...)或redis.call("EXISTS", ...)放最前面,把需要的状态一次性读全 - 用
if not bal or tonumber(bal) 提前退出 - 所有
SET、INCRBY、LPUSH等写操作,全部堆在所有判断之后 - 宁可多查一次,也不要为了省一次
GET把判断逻辑塞进写命令的条件里(比如用SETNX+ TTL 组合代替显式判断)
用 redis-cli --eval 执行时参数格式容易错在哪
命令行传参空格和逗号稍有不慎就会解析错位,导致 KEYS 和 ARGV 错乱。
典型错误示例:
- 写成
redis-cli --eval buy.lua key1 key2 5824742984 1—— 缺少逗号分隔符,全部被当成了KEYS,ARGV为空 - 写成
redis-cli --eval buy.lua key1 key2 , 5824742984 1—— 逗号前后没空格,redis-cli 会把, 5824742984当作一个参数 - 正确格式是:
redis-cli --eval buy.lua key1 key2 , 5824742984 1(逗号前后各一个空格) - 调试时加
--ldb启动调试器,但注意 fork 出的进程不反映主实例当前数据,仅适合逻辑验证
真正难的不是写脚本,而是预判所有可能的失败分支,并把写操作卡死在最后一步——只要有一处条件未覆盖,就可能留下不一致状态。集群环境下,hash tag 和参数隔离这两条线一旦松动,问题会直接暴露在生产流量里。

















