Lua脚本实现分布式锁加锁和解锁操作
-- 加锁操作
-- KEYS[1] 是锁的键名
-- ARGV[1] 是锁的持有标识(例如UUID)
-- ARGV[2] 是锁的过期时间(单位:秒)
if (redis.call('SETNX', KEYS[1], ARGV[1]) == 1) then
redis.call('EXPIRE', KEYS[1], ARGV[2])
return 1
else
return 0
end
-- 解锁操作
-- KEYS[1] 是锁的键名
-- ARGV[1] 是锁的持有标识(例如UUID)
if (redis.call('GET', KEYS[1]) == ARGV[1]) then
return redis.call('DEL', KEYS[1])
else
return 0
end
脚本中命令作用解释
- 加锁部分:
redis.call('SETNX', KEYS[1], ARGV[1])
:SETNX
命令是 SET if Not eXists
的缩写,即只有当指定的键不存在时,才会设置键的值。这里尝试将锁的键 KEYS[1]
设置为持有标识 ARGV[1]
,如果设置成功(返回1),表示获取到锁;如果设置失败(返回0),表示锁已被其他客户端持有。
redis.call('EXPIRE', KEYS[1], ARGV[2])
:为获取到的锁设置过期时间 ARGV[2]
(单位:秒),防止锁在持有客户端出现异常时一直不释放。
- 解锁部分:
redis.call('GET', KEYS[1])
:获取锁的键 KEYS[1]
的值,用于判断当前锁是否是自己持有。
if (redis.call('GET', KEYS[1]) == ARGV[1])
:判断当前锁的值是否与自己的持有标识 ARGV[1]
相等,如果相等,表示是自己持有的锁,可以进行解锁操作。
return redis.call('DEL', KEYS[1])
:删除锁的键,释放锁。
Lua脚本保证原子性的原因
- Redis单线程模型:Redis是单线程执行命令的,在执行Lua脚本时,Redis会将整个Lua脚本作为一个整体来执行,期间不会被其他命令打断。这就保证了Lua脚本中多条命令的执行是原子性的,要么全部执行成功,要么全部不执行。
- Lua脚本的执行机制:Redis将Lua脚本的执行过程封装成一个原子操作,在脚本执行期间,Redis不会处理其他客户端的请求,直到脚本执行完毕。这样就避免了在执行多命令操作时,因为其他客户端的干扰而导致数据不一致的问题。