在使用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;