Redis Lua脚本在多线程环境下本质是串行执行的,因其依赖Redis单线程事件循环强制排队,所有客户端EVAL请求按FIFO顺序逐一执行,保证脚本内GET+IF+DECRBY等操作原子完成,期间无其他命令插入,原子性源于调度机制而非Lua自身线程安全。

Redis Lua 脚本在多线程环境下本质是串行执行的,不是靠 Lua 本身线程安全,而是靠 Redis 单线程事件循环强制排队。
为什么多个客户端并发调用 EVAL 不会交错执行
Redis 服务端只有一个主线程处理命令(包括 Lua 脚本),所有客户端请求按接收顺序进入队列。当你在 Java 或 Python 中开 100 个线程同时发 EVAL,Redis 并不会并行执行这 100 个脚本——它一个接一个地取、解析、运行、返回,中间不插入任何其他命令。
这意味着:哪怕脚本里有 GET + IF + DECRBY 三步,对 Redis 来说就是“一整个不可分割的动作”,不存在“线程 A 执行完 GET、还没 DECRBY 就被线程 B 插入”的情况。
- 脚本执行期间,整个 Redis 实例阻塞(所以别写死循环或耗时操作)
- 不同客户端的脚本互不影响,但严格 FIFO 排队
- 原子性来自调度机制,不是 Lua 解释器的并发控制能力
KEYS 和 ARGV 的传参方式直接影响集群兼容性
脚本里所有涉及的 key 必须显式通过 KEYS 数组传入,不能硬编码字符串(比如写死 "stock:123")。否则在 Redis Cluster 模式下,Redis 无法判断该脚本应该路由到哪个节点,直接报 (error) CROSSSLOT Keys in request don't hash to the same slot。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
ARGV 是纯字符串数组,数值比较前必须用 tonumber() 转换,否则 "10" > "2" 在 Lua 里结果是 false(字典序比较)。
- 正确:
if redis.call("GET", KEYS[1]) >= tonumber(ARGV[1]) then - 错误:
if redis.call("GET", KEYS[1]) >= ARGV[1] then(字符串比较) - 集群下必须确保所有
KEYS落在同一哈希槽,否则脚本无法执行
EVALSHA 缓存失效的常见原因
用 SCRIPT LOAD 得到 SHA1 后,后续用 EVALSHA 调用更高效,但线上常遇到 (error) NOSCRIPT No matching script。这不是调用姿势错,而是缓存没真正落地:
- Redis 实例重启后,所有已加载脚本清空(
SCRIPT LOAD不写 AOF/RDB) - 主从切换时,从库可能没同步到脚本(尤其 Redis 6 及更早版本)
- 集群模式下,每个分片节点都得单独
SCRIPT LOAD,漏掉任意一个就报错 - 应用启动时没做脚本预热,首次
EVALSHA必然失败,需 fallback 到EVAL
真正容易被忽略的是:脚本逻辑本身没问题,但只要部署环境稍有变化(比如从单机切到集群、主从角色变更、实例重启),EVALSHA 就可能突然失效——它依赖的是服务端内存状态,不是持久化配置。

















