redis lua脚本在分布式锁中不可替代的核心价值是原子性释放锁和避免误删:get+del存在时间窗口导致必然误删,而lua在redis单实例内存中执行校验与删除,杜绝中间插入、网络延迟及集群分片不一致问题。

Redis Lua脚本在分布式锁中不可替代的核心价值,就两点:原子性释放锁和避免误删。其他方案——比如用 SETNX + DEL 分两步、或靠客户端判断再删除——在高并发下必然出错,不是“可能”,是“一定”。
为什么 GET + DEL 组合一定会误删锁
这是最典型的错误写法,表面逻辑没问题,实际执行时存在明显时间窗口:
- 线程 A 执行
redisTemplate.opsForValue().get("lock:order"),拿到值"a123" - 此时锁因超时被 Redis 自动删除
- 线程 B 成功执行
SETNX拿到锁,写入值"b456" - 线程 A 继续执行
redisTemplate.delete("lock:order"),删掉了线程 B 的锁
整个过程没有报错,但业务已失去互斥保障。Lua 脚本把这两步压进一次 Redis 内部执行,中间不会被任何外部操作打断。
Redis 8.2.3 是一款安全优先的高性能键值存储系统。该版本紧急修复了可能引发远程代码执行(RCE)的高危漏洞(CVE-2025-62507),并解决了 HyperLogLog 及 Cuckoo Filter 等数据结构在特定场景下的崩溃问题。建议所有用户立即升级,以保障生产环境的系统稳定与数据安全。
redis.call("get", KEYS[1]) == ARGV[1] 这行判断为什么必须在服务端做
这个比较不能放在客户端,原因很直接:
- 客户端拿到的值可能是过期前的快照,不是当前真实状态
- 网络延迟、GC 停顿、JVM 线程调度都可能让判断和删除之间间隔几十毫秒
- Redis 集群模式下,
GET和DEL可能落到不同分片,根本无法保证键一致性 - 而
redis.call()在 Redis 单个实例内存上下文中执行,KEYS 和 ARGV 都是本地变量,无 IO、无调度、无跨节点
集群环境下 EVAL 与 EVALSHA 的实际影响
在 Redis Cluster 中,Lua 脚本执行有硬性约束:所有涉及的 key 必须落在同一个 slot。这意味着:
- 脚本里只能操作一个 key(如
KEYS[1]),不能同时读lock:order又写lock:pay - 使用
EVALSHA可以复用已加载脚本,但首次仍需EVAL传输完整脚本体,且脚本哈希必须提前同步到所有 master 节点 - Spring Data Redis 的
execute()在集群模式下默认禁用脚本执行,必须显式获取RedisConnection并调用原生eval()方法 - 脚本不能依赖随机数、系统时间等非确定性函数,否则会导致主从不一致
真正容易被忽略的不是语法,而是脚本生命周期管理:同一段释放锁脚本,在多个微服务里各自定义、各自加载,不仅浪费内存,还可能导致哈希冲突或版本不一致。上线前务必确认所有节点加载的是同一份脚本 SHA1。










