应避免反复调用redis.call(),因其每次触发完整命令流程开销大;需用局部变量缓存读结果、批量命令替代循环、禁用复杂计算,并强制使用evalsha+script load及严格keys传参。

避免在Lua脚本里反复调用 redis.call()
Redis 的 Lua 执行是单线程的,每次 redis.call() 都会触发一次命令解析、权限校验、键路由(集群下)、实际执行等完整流程。哪怕只是重复读同一个 key 几次,也等于做了多次开销。
常见错误现象:
- 脚本里写
redis.call('GET', KEYS[1])三次,而不是存到局部变量复用 - 循环里对每个元素都调一次
redis.call('HGET', KEYS[1], ARGV[i]),其实可以用redis.call('HMGET', KEYS[1], unpack(ARGV))一次拿完
正确做法:
- 所有读操作优先用局部变量缓存结果,比如
local val = redis.call('GET', KEYS[1]) - 写操作能批量就批量,
MSET、HMSET、ZADD(带多个 member-score 对)比循环调用快得多 - 不要为了“看起来清晰”把一个
INCRBY拆成GET+SET,原生命令本身就是原子且高效的
别在脚本里做客户端该干的计算
Lua 解释器在 Redis 进程内运行,CPU 资源和执行时间都受 lua-time-limit 限制(默认 5 秒)。一旦超时,脚本被强制中断,还可能触发 SCRIPT KILL 或更糟的 SHUTDOWN NOSAVE。
容易踩的坑:
- 用 Lua 实现 base64 编解码、JSON 解析、正则匹配(
string.match复杂模式)、大数组排序 - 在脚本里遍历几百个元素做条件过滤,再逐个
redis.call() - 把时间戳转成格式化字符串(
os.date())再拼接日志
应该怎么做:
- 数值计算、字符串拼接、简单逻辑判断可以留,但复杂度要可控(比如循环不超过几十次)
- JSON 处理交给客户端,只传结构化参数进
ARGV,脚本里用cjson.decode()仅限极简场景(且确认已加载 cjson 库) - 时间相关操作尽量用 Redis 自带的
TIME命令或客户端传入毫秒时间戳,避免os.time()
EVALSHA + SCRIPT LOAD 是必选项,不是可选
每次用 EVAL 发送完整脚本,等于重复传输几百字节甚至几 KB 的文本。网络带宽、序列化/反序列化、SHA 计算全白费——尤其当脚本高频执行(如计数器、限流器)时,这部分开销远超逻辑本身。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
使用场景:
- 所有上线脚本必须先
SCRIPT LOAD获取 SHA,缓存在客户端(比如用 map[string]string 存) - 后续一律走
EVALSHA,失败时自动 fallback 到EVAL并重新LOAD(注意幂等性)
注意事项:
- Redis 重启后脚本缓存清空,
EVALSHA会返回NOSCRIPT错误,必须处理这个 case - 不要用动态拼接脚本(比如字符串插值生成不同逻辑),这会让
SCRIPT LOAD失效,变成永远走EVAL - 集群环境下,
SCRIPT LOAD必须发到对应 slot 的节点,不能随便连一个 proxy 就 load
KEYS 和 ARGV 的用法直接影响集群兼容性与性能
Redis 集群靠 key 的 CRC16 值决定路由目标节点。如果脚本里硬编码 key 名(比如 redis.call('GET', 'user:123')),集群会拒绝执行并报错 CROSSSLOT;即使在单机 Redis 上跑通,一上集群就崩。
典型错误:
- 直接写死 key 字符串,没走
KEYS数组 - 把 key 放进
ARGV传进来,导致无法预判 slot 分布 - 用
redis.call('KEYS', '*')—— 不仅慢,而且禁用(生产环境通常关闭KEYS命令)
正确姿势:
- 所有涉及 Redis 数据访问的 key,必须从
KEYS[1]、KEYS[2]… 显式传入,数量用EVAL第三个参数声明 - 多 key 操作必须保证它们落在同一 slot(比如用
{user:123}这种 tag 方式),否则脚本无法执行 -
ARGV只放纯数据(数值、flag、过期时间等),绝不放 key 名
Redis 的 Lua 性能瓶颈往往不在逻辑本身,而在“怎么调用”和“调什么”。最常被忽略的是:脚本越短小、越少 call、越不碰网络和复杂计算,就越接近 Redis 原生命令的性能水平。










