应使用 EVALSHA 替代 EVAL 以降低网络开销,但必须先通过 SCRIPT LOAD 将脚本预加载至 Redis;若脚本内容有任一字节差异或未预加载,EVALSHA 将报错并退回到 EVAL。

用 EVALSHA 替代 EVAL,前提是脚本已加载
Redis 执行 Lua 脚本时,如果每次都用 EVAL 发送完整代码,网络开销和序列化成本会明显上升,尤其在高并发或带宽受限场景下。真正省流量的办法不是压缩脚本,而是让 Redis 记住它——靠 EVALSHA 调用已缓存的 SHA1 哈希值。
但这个机制有个硬前提:脚本必须先通过 SCRIPT LOAD 注册到服务端。没这步,EVALSHA 会直接报错 NOSCRIPT No matching script. Please use EVAL.。
- 每次启动应用时,建议在连接建立后批量执行
SCRIPT LOAD,把所有常用脚本预热进 Redis - 脚本内容只要有一字节差异(比如空格、注释、换行),SHA1 就不同,
EVALSHA就不匹配 - Redis 4.0+ 支持
SCRIPT LOAD返回的 SHA1 可直接用于后续EVALSHA;老版本需自己算 SHA1,容易出错
用 redis-cli --eval 或客户端封装自动处理加载与调用
手动管理 SCRIPT LOAD + EVALSHA 容易漏、难维护。多数生产环境其实依赖客户端库的“脚本缓存”功能,比如 Jedis 的 evalSha、redis-py 的 evalsha 方法,它们内部会自动 fallback 到 EVAL(当 SHA1 未命中时)。
本地调试时,redis-cli --eval 是更轻量的选择,它会自动计算 SHA1 并尝试 EVALSHA,失败再发 EVAL:
redis-cli --eval myscript.lua key1 key2 , arg1 arg2
- 注意逗号前后必须有空格,否则解析失败
- 脚本路径必须是本地文件,不能是远程 URL
- 该命令不会持久化脚本,只对本次连接生效;重启 redis 后仍需重新加载
避免在 Lua 脚本里拼接变量或动态构造逻辑
脚本内容一旦变动,SHA1 就失效,导致 EVALSHA 失败并退回到全量传输。常见踩坑点是把业务参数塞进 Lua 字符串里拼接:
local cmd = "redis.call('incr', '" .. KEYS[1] .. "')" -- ❌ 危险!KEYS 内容变了,脚本就变正确做法是严格分离逻辑与数据:用 KEYS 和 ARGV 传参,Lua 体内只做固定流程:
local val = redis.call('incr', KEYS[1]) -- ✅ 纯逻辑,SHA1 稳定- 不要用
loadstring或字符串拼接生成新函数 - 避免在脚本中读取外部配置、时间戳、随机数等不确定值
- 如果真需要条件分支,用
if ... then ... else,别靠拼字符串绕过
注意集群模式下 SCRIPT LOAD 的作用域限制
Redis Cluster 中,SCRIPT LOAD 只影响当前连接的节点,不会广播到整个集群。如果你的脚本操作多个 key,而这些 key 分布在不同 slot,又没提前在所有涉及节点上 SCRIPT LOAD,那么 EVALSHA 在某些节点上就会失败。
- 单 key 操作通常安全;多 key 操作务必确保所有相关节点都加载过该脚本
- 部分客户端(如 Lettuce)支持自动跨节点分发
SCRIPT LOAD,但需显式启用,不是默认行为 - 集群环境下,不如干脆用
EVAL配合连接池复用,反而比手动同步脚本更稳
脚本哈希值本身不跨实例共享,这点很容易被忽略——看起来一次 SCRIPT LOAD 全局生效,其实只是你连的那个节点记住了。


















