Redis分布式锁为什么要使用Lua脚本?纯Java代码为什么不可以?
1 个回答
在漆黑的夜里,你需要一道光照亮夜行的路,我燃烧了我自己,但也造就了你我不能相拥。周一仙## 一、为什么 Redis 分布式锁要用 Lua 脚本? ### 1. **保证原子性(atomicity)** Lua 脚本在 Redis 中是**单线程、一次执行完成的**,因此所有命令在一条脚本中执行,**天然具备原子性**,不会被其他客户端打断。 典型场景: ```lua if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end ``` 上面这段逻辑: * 判断 key 是否属于当前客户端(避免误删别人的锁); * 若是,则删除锁; * 所有逻辑一步完成,不会被其他 Redis 命令插入打断。 这是实现正确解锁逻辑的关键。 ## 二、纯 Java 实现有什么问题? ### 1. **非原子操作** 假设你用 Java 代码解锁: ```java if (redis.get("lock") == myUUID) { redis.del("lock"); } ``` 这看起来逻辑对,但问题在于: * `get` 和 `del` 是 **两个独立命令**; * 如果两个客户端同时到达 `get`; * 第一个执行 `del` 前被挂起,第二个执行 `del` 抢在前面; * 第一个再继续执行 `del`,就可能 **错误地删除了别人的锁**! ➡️ 这就是经典的 **并发竞争问题,导致锁被误删**。 ## 三、Lua 脚本带来的额外好处 ### 保证 Redis 单线程执行的事务性 * Redis 是单线程模型; * Lua 脚本运行时不会被打断; * 保证逻辑的一致性和可靠性。 ### 执行效率高 * 所有操作在 Redis 端执行,无需多次网络交互; * 减少 RTT(网络往返时间),性能更好。 ## 四、实战建议 ### 推荐:使用 Lua + UUID 组合分布式锁 ```lua -- 解锁 Lua 脚本 if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end ``` ### Java 调用方式: ```java String luaScript = "..."; redis.eval(luaScript, Collections.singletonList("lockKey"), Collections.singletonList(myUUID)); ``` ## 总结 | 问题 | Lua 脚本 | Java 普通代码 | | ----- | --------- | ------------- | | 是否原子性 | ✅ 是 | ❌ 否,多个命令被打断 | | 是否可靠 | ✅ 删除自己加的锁 | ❌ 可能误删他人锁 | | 是否高性能 | ✅ 减少网络交互 | ❌ 多次 Redis 请求 | | 是否推荐 | ✅ 强烈推荐 | ❌ 非线程安全,不可取 | 因此,**Redis 分布式锁必须使用 Lua 脚本来保证关键操作的原子性**,而不是用纯 Java 多个命令实现,否则就会面临并发竞争问题,导致锁失效或误删,破坏分布式锁的正确性。编辑于 2025-08-07 19:00 •IP属地 北京举报评论收藏
公众号进入网站体验更佳

