应使用 scriptLoad + evalsha 而非每次 eval 传完整脚本,因后者导致 Redis 重复解析、网络开销增大、缓存命中率低,QPS 下降 15%~20%;需启动时预加载并缓存 SHA1,执行前用 scriptExists 校验存在性。

为什么不能每次 eval 都传完整脚本字符串
直接用 jedis.eval(script, keys, args) 每次发完整 Lua 字符串,会带来三重开销:Redis 要重新解析脚本、校验语法、生成内部 AST;网络多传几百字节(尤其带注释或逻辑分支的脚本);脚本缓存命中率低,失去复用价值。实测在高并发减库存场景下,QPS 可能下降 15%~20%。
scriptLoad + evalsha 是标准复用路径
必须分两步走:先加载一次脚本获取 SHA1,后续全用 SHA1 执行。Jedis 提供了配套方法,但要注意调用时机和线程安全:
-
jedis.scriptLoad(script)返回 SHA1 字符串(如"a4c85d7..."),只须执行一次,建议在应用启动时完成 - 多个线程共用同一个
SHA1值调用jedis.evalsha(sha1, keys, args)完全安全,Redis 服务端保证原子性 - 若脚本未预加载就直接调用
evalsha,Redis 返回(error) NOSCRIPT No matching script. Please use EVAL.—— 这不是 Jedis 错误,是服务端校验失败 - 脚本内容哪怕只差一个空格,SHA1 就完全不同,务必确保部署时脚本内容与加载时完全一致
连接池里怎么安全存脚本 SHA1
不能把 SHA1 存在局部变量或每次 new Jedis 实例时重 load。推荐做法:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 在 Spring 环境中,用
@PostConstruct或InitializingBean在JedisPool初始化后,取一个连接执行scriptLoad,结果存入静态 final 字段或配置中心 - 非 Spring 场景,可封装一个
LuaScriptRegistry工具类,用ConcurrentHashMap<String, String>缓存脚本名 → SHA1映射,首次访问时加锁加载 - 避免在 pipeline 里混用
eval和evalsha—— pipeline 不支持混合命令类型,会抛JedisDataException: ERR unknown command 'evalsha'
容易被忽略的 SCRIPT EXISTS 校验点
Redis 重启后脚本缓存清空,但你的应用可能没重 load。上线前或关键操作前,建议加一层存在性检查:
jedis.scriptExists(sha1) 返回 true/false 数组,单个脚本就看第一个元素。如果为 false,必须立即 scriptLoad 再执行,否则后续所有 evalsha 都会失败。这个检查成本极低(O(1)),但能避免整批请求因 NOSCRIPT 报错而降级。

















