EVAL适合临时验证,生产环境应优先用EVALSHA;EVAL每次传输完整脚本并重复编译,而EVALSHA仅传40字节SHA1值,网络开销减少95%+、CPU编译开销减少90%+,且需配合SCRIPT LOAD预加载与NOSCRIPT错误回退机制。

直接用 EVAL 写脚本,但别在生产环境裸奔
脚本能简化流程,前提是它真被用对了。直接在代码里拼接 EVAL 字符串最省事,但上线后就容易出问题:每次传输完整脚本、Redis重启后失效、调试困难、无法复用。真实项目里,EVAL 只适合临时验证或低频逻辑。
- 脚本内容必须通过
KEYS传键名,硬编码键(比如"user:123")会导致集群模式下报错(error) MOVED -
ARGV里的值默认是字符串,数值计算前得用tonumber(ARGV[1])转换,否则redis.call('INCRBY', KEYS[1], ARGV[1])会失败 - 脚本里不能用
os.time()、math.random()等非纯函数,Redis 要求脚本可重入,否则主从复制或 AOF 重放可能出错
redis.call() 和 redis.pcall() 到底该选哪个
多数人用 redis.call(),但它一出错整个脚本就中断,返回类似 (error) ERR Error running script 的错误。而 redis.pcall() 会把错误包装成 table 返回,让你自己决定怎么处理——这才是健壮脚本的关键。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 比如查一个可能不存在的 key:
local res = redis.pcall('GET', KEYS[1]),之后检查res == false或type(res) == 'table' and res['err'] -
redis.call()更适合“必须成功”的步骤,比如锁释放时删 key,失败就该暴露问题 - 注意:
redis.pcall()不会吞掉所有异常,像语法错误、超时、内存溢出这类还是直接炸
用 SCRIPT LOAD + EVALSHA 避开重复解析开销
高频调用同一脚本时,EVAL 每次都要传输、校验、编译,实测比 EVALSHA 慢 15%–20%。真正落地必须预加载脚本,拿到 SHA1 后走 EVALSHA。
- 启动时调一次
SCRIPT LOAD "return redis.call('GET', KEYS[1])",存下返回的 SHA1 值(如"8b6e9a7f4c...") - 后续请求全用
EVALSHA 8b6e9a7f4c... 1 mykey,Redis 直接查缓存执行 - 如果 Redis 重启了,
EVALSHA会返回(error) NOSCRIPT No matching script. Please use SCRIPT LOAD.,这时要 fallback 到EVAL并重新 load
脚本里写循环或条件判断,小心超时和阻塞
Lua 脚本跑在 Redis 单线程里,一个卡住的脚本会让所有命令排队。默认 lua-time-limit 是 5 秒,超时后 Redis 会拒绝新请求,直到你手动 SCRIPT KILL(没写数据时)或 SHUTDOWN NOSAVE(已写数据时)。
- 避免
while true do ... end这类无退出条件的循环,用for i=1, #KEYS do更安全 - 批量操作别一次性处理几千个 key,拆成多个脚本调用,或者用
redis.call('SCAN')分页 -
redis.log()只能在 debug 模式下用,生产环境日志级别不够看不到,别依赖它排障

















