Redis 分布式锁使用 Lua 脚本而非事务,核心原因是 Lua 脚本能保证分布式锁操作的 "原子性" 和 "灵活性",而 Redis 事务在某些场景下无法满足分布式锁的核心需求。
一、Redis事务的局限性
redis分布式锁的核心是先判断自己是否持有锁,然后在进行释放。
用 Redis 事务实现时,需要分两步:
java
MULTI
GET lock:key # 1. 判断锁是否归属自己
DEL lock:key # 2. 释放锁(仅当归属自己时)
EXEC
但 Redis 事务是 "批量执行,无中间判断" 的 ------GET
和 DEL
会被强制一起执行,无法根据 GET
的结果决定是否执行 DEL
。即使锁已被其他客户端抢占,DEL
仍会执行,导致 "误释放他人锁" 的严重问题。
二、lua脚本的优势
Lua脚本能够保障redis服务端原子性的执行一系列的命令,且不会被其它客户端打断,确保条件判断和后续操作的原子性
如释放锁的Lua脚本:
java
-- 释放锁:仅当锁的value是自己的标识时才删除
if redis.call("GET", KEYS[1]) == ARGV[1] then
return redis.call("DEL", KEYS[1]) -- 释放锁
else
return 0 -- 不释放他人的锁
end
这段脚本会原子性地完成 "判断锁归属" 和 "释放锁" 两个操作,避免了事务中 "判断和执行分离" 的问题。
同时Lua可以提高分布式锁的性能,
分布式锁的操作(如 "判断 + 释放")若用多条命令分步执行,需要多次网络往返(客户端→Redis→客户端)。
用 Lua 脚本可以将多步操作合并为一次网络请求,减少网络延迟,尤其在分布式环境中能显著提升性能
补充:为什么Lua脚本是单线程的?
Redis 是单线程处理命令的,
整个脚本会被当作一个不可分割的整体,在执行过程中不会被其他客户端的命令打断,直到脚本完全执行完毕,
同时Redis 明确禁止在 Lua 脚本中执行会阻塞主线程的操作 (如BRPOP
等阻塞命令、长时间循环)。这确保了 Lua 脚本的执行时间是可控的,不会因为脚本执行过久导致 Redis 无法处理其他请求。