evalsha在新主库报noscript,因脚本不参与复制,新主lua_scripts字典为空;script load仅内存缓存且不落盘不同步,function load则持久化并自动复制。

不会“正在执行”的脚本不会丢失,但切换后所有已缓存的脚本 SHA 都会失效。
为什么EVALSHA在新主库上直接报NOSCRIPT
哨兵完成主从切换后,原从库升为新主库,但它本地 lua_scripts 字典是空的——旧主库上通过 SCRIPT LOAD 或 EVAL 注册过的脚本,不会复制过去。这不是网络延迟或同步滞后,而是 Redis 明确不把 Lua 脚本内容纳入主从复制流程。
-
EVALSHA依赖节点内存中存在对应 SHA1 哈希,新主库查不到就立刻返回NOSCRIPT No matching script - 错误是协议级响应,不是连接失败或超时,客户端必须显式处理
- 哪怕脚本只执行过一次、没用
SCRIPT LOAD,只要没在新主库上重跑,EVALSHA就一定失败
SCRIPT LOAD 和 FUNCTION LOAD 的行为差异
根本区别不在语法,而在生命周期管理方式:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
-
SCRIPT LOAD只写当前节点内存,重启、切换、SCRIPT FLUSH都清空,且不同步到从节点 -
FUNCTION LOAD会把函数内容写入 AOF/RDB,并随主从复制自动同步到所有节点,新主库启动即自带全部函数 - Redis 7.0+ 中,
FUNCTION LIST可查全局可用函数,而SCRIPT EXISTS在新主库上对旧 SHA 总是返回 0
集群模式下脚本失效更隐蔽
Redis Cluster 不仅面临主从切换问题,还叠加了分片路由约束:
- 脚本里所有
KEYS必须落在同一 slot,否则报CROSSSLOT错误;切换后若客户端仍按旧拓扑发请求,可能路由到无脚本的节点 - 即使你对每个节点都
SCRIPT LOAD过,集群不保证脚本缓存在所有分片上长期一致——节点宕机再上线,缓存就丢了 -
FCALL比EVALSHA更可靠,但前提是函数库名(如lib:rate_limit)已在目标 slot 所在节点注册成功
最容易被忽略的实操细节
很多团队加了重试逻辑,却卡在两个地方:
- 重试时没区分错误类型:
NOSCRIPT要重SCRIPT LOAD+EVALSHA,而TRYAGAIN或CLUSTERDOWN是另一回事 - 在哨兵环境里执行
SCRIPT FLUSH清场,结果旧主还在服务流量,瞬间触发大面积NOSCRIPT,且从库状态彻底脱节 - Spring Boot 项目里用
RedisTemplate.execute(),默认不捕获NOSCRIPT并 fallback 到EVAL,得自己包装容错逻辑










