哨兵切换后Lua脚本不会自动同步到新主库,因脚本不参与复制,新主库缓存为空,EVALSHA会报NOSCRIPT错误;需通过重执行SCRIPT LOAD、改用EVAL或客户端容错重试来应对。

哨兵切换时Lua脚本不会自动同步到新主库
Redis 的 Lua 脚本本身不参与主从复制流程,EVAL 和 EVALSHA 命令执行后,脚本内容不会像普通写命令一样被记录进 replication buffer 或写入 RDB/AOF。这意味着:当哨兵完成主从切换,原从库升级为新主库后,它本地**没有执行过你之前在旧主库上注册过的脚本**,EVALSHA 会直接报错 NOSCRIPT No matching script. Please use EVAL.。
- 旧主库上用
EVAL执行过的脚本,只缓存在它自己的lua_scripts字典里,不传播 - 从库在全量/增量同步过程中,只同步键值数据和写命令(如
SET、INCR),不复制 Lua 脚本缓存 - 新主库(原从库)启动时是空的脚本缓存,
SCRIPT LOAD或EVAL必须重做
客户端调用 EVALSHA 失败的典型表现
你在应用里习惯性地先 SCRIPT LOAD 一次,拿到 SHA1 后反复用 EVALSHA 调用——这在单实例或稳定主从下完全 OK,但在哨兵切换后立刻崩:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 旧主库下线前,
SCRIPT LOAD "return redis.call('GET', KEYS[1])"返回"9e45a2b7c8f1..." - 切换完成后,同一客户端向新主库发
EVALSHA 9e45a2b7c8f1... KEYS[1] "foo" - 新主库查不到该 SHA,返回
NOSCRIPT,你的业务逻辑中断 - 注意:不是连接失败或 timeout,是明确的协议级错误响应
绕过失效的三种实操方案
没有银弹,只有根据场景权衡选择:
-
始终用
EVAL替代EVALSHA:放弃缓存优化,每次传完整脚本。适合脚本短( -
切换后主动预热脚本:监听哨兵的
+switch-master事件(通过redis-cli --csv -x subscribe __sentinel__:hello或程序订阅),收到通知后对所有候选新主库批量执行SCRIPT LOAD -
改用服务端托管脚本:把脚本内容存在某个固定 key(如
lua:scripts:get_user),每次执行前先GET再EVAL;虽然多一次 round-trip,但天然跨主从一致
为什么 SCRIPT FLUSH 在哨兵环境里很危险
有些同学想“一劳永逸”——在每次部署时对所有节点执行 SCRIPT FLUSH 清空缓存,再统一 LOAD。这在哨兵下极易引发雪崩:
- 旧主库还在服务写请求,
SCRIPT FLUSH会清掉它正在用的脚本缓存 - 此时客户端继续发
EVALSHA,立刻失败,而你还没来得及重新LOAD - 更糟的是:
SCRIPT FLUSH不同步,从库不会跟着清,导致主从脚本缓存状态彻底不一致 - 结论:
SCRIPT FLUSH只应在维护窗口期对全部节点顺序执行,且必须确保无流量

















