在使用redis分布式锁时,为了保证加锁解锁具备原子性
加锁时使用带过期时间的set指令,而不是通过setnx+expire;
解锁时为了防止误删,给锁添加了唯一标识,判断唯一标识与解锁之间也要具备原子性,使用lua脚本解决
redis中使用lua脚本的方式;EVAL script numkeys key [key...] arg [arg...]
script:脚本语言
numkeys :key的数量。必须要指定
key :key的值
arg :value的值。因为redis存储的key:value的方式
通过EVAL “lua脚本” 即可实现
例如:
EVAL "if 1>0 then return 1 else retrun 0 end"0
EVAL "if KEYS[1]>ARGV[1] then return 1 else retrun 0 end" 1 1 0
表示:keys里的第一个值是1>agev里的第一个值0
通过lua脚本操作redis:通过redis.call()
EVAL "retrun redis.call('get',KEYS[1])" 1 lock
redis.call(redis的命令,查询的key)
查询的key:不能写死,例如redis.call('get','lock'),只能通过后面的key参数指定
根据锁的唯一标识删除锁实现原子性
if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end
不可重入
加锁
if (redis.call('exists', KEYS[1]) == 0 or redis.call('hexists', KEYS[1], ARGV[1]) == 1)
then
redis.call('hincrby', KEYS[1], ARGV[1], 1);
redis.call('expire', KEYS[1], ARGV[2]);
return 1;
else
return 0;
end
redis中
exists key:判断锁是否存在,不存在返回0,存在返回1
hexists key filed:判断是不是自己的锁。hexists lock 12345 ,返回1说明这个uuid是12345的锁是当前线程自己的锁
解锁
-- 判断 hash set 可重入 key 的值是否等于 0
-- 如果为 nil 代表 自己的锁已不存在,在尝试解其他线程的锁,解锁失败
-- 如果为 0 代表 可重入次数被减 1
-- 如果为 1 代表 该可重入 key 解锁成功
if (redis.call('hexists', KEYS[1], ARGV[1]) == 0) then
return nil;
end;
-- 小于等于 0 代表可以解锁
if (redis.call('hincrby', KEYS[1], ARGV[1], -1) > 0) then
return 0;
else
redis.call('del', KEYS[1]);
return 1;
end;