基于Lua脚本的分布式锁实现方案,这里列举加锁和释放锁的过程
为什么要用Lua
Redis 在 2.6 版本中推出了脚本功能,允许开发者将 Lua 语言编写的脚本传到 Redis 中执行。使用 Lua 脚本的优点有如下几点:
1. 减少网络开销:本来需要多次请求的操作,可以一次请求完成,从而节约网络开销;
2. 原子操作:Redis 会将整个脚本作为一个整体执行,中间不会执行其它命令;
3. 复用:客户端发送的脚本会存储在 Redis 中,从而实现脚本的复用。
Lua脚本
加锁(可重入锁)
-- 加锁脚本
-- key1:要加锁的名称 argv1:当前线程或主机的地址 argv2:锁存活的时间ms
local expire_time = tonumber(ARGV[2])
if redis.call('exists', KEYS[1]) == 0 then
-- 锁不存在,创建一把锁,存入hash类型的值
redis.call('hset', KEYS[1], ARGV[1], 1)
-- 设置锁的存活时间,防止死锁
redis.call('pexpire', KEYS[1], expire_time)
return 1
end
if redis.call('hexists', KEYS[1], ARGV[1]) == 1 then
-- 表示是同一线程重入
redis.call('hincrby', KEYS[1], ARGV[1], 1)
-- 重新设置锁的过期时间
redis.call('pexpire', KEYS[1], expire_time)
return 1
end
-- 没抢到锁,返回失败
return 0
释放锁
-- 解锁脚本
-- 判断是当前线程持有锁,避免解了其他线程加的锁
if redis.call('hexists',KEYS[1],ARGV[1]) == 1 then
-- 重入次数大于1,扣减次数
--if tonumber(redis.call('hget',KEYS[1],ARGV[1])) > 1 then
-- return redis.call('hincrby', KEYS[1], ARGV[1], -1)
-- 重入次数等于1,删除该锁
--else
redis.call('del', KEYS[1]);
return 1
--end
-- 判断不是当前线程持有锁,返回解锁失败
else
return 0
end