使用redis實現分散式鎖
阿新 • • 發佈:2018-12-21
大致思路:
加鎖:
利用set方法的nx,px引數:
nx引數表示當key不存在時才成功,key存在時失敗,故保證了排他性.
px引數表示設定過期時間,保證了執行緒出現異常無法釋放鎖(刪除key)
釋放鎖:
不能簡單地刪除鍵,因為可能出現這樣的情況:執行緒成功set拿到鎖,由於執行時間過長,鎖已經過期了,鎖又被另一個執行緒拿到,這時該執行緒準備釋放鎖,可是鎖以及不屬於它了,所以不能讓它隨便刪除key。只有當鎖屬於它的時候,才能讓它刪除鎖.我們可以利用value標識鎖的主人.
.我使用兩個lua指令碼實現加鎖以及釋放鎖
redis_lock.lua
local key=KEYS[1]; --獲取lock的鍵 local value=KEYS[2] --標識鎖的主人 local expireTime=tonumber(ARGV[1]); --獲取lock過期時間 local result=tonumber(redis.call("SETNX",KEYS[1],value)); if result==1 then redis.call("EXPIRE", key,expireTime); end return result
redis_unlock.lua
local key=KEYS[1]; --刪除鎖的鍵
local value=KEYS[2]; --標識誰來刪除
local owner=redis.call("GET",key); --鎖的主人
if owner ==value --當鎖屬於自己,可以刪除
then
redis.call("DEL",key);
return 1;
else
return 0;
end
RedisDistributedLock.java
package ink.toppest.secondskill.util; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.concurrent.TimeUnit; /** * 描述: * * @author HASEE * @create 2018-11-13 20:34 */ @Component public class RedisDistributedLock { @Autowired StringRedisTemplate stringRedisTemplate; //自旋獲取鎖,為了降低cpu損耗,睡眠一段時間 public void lock(String lock,String owner){ while (!ScriptUtil.readAndExecute("redis_lock.lua",stringRedisTemplate, new ArrayList<String>(){ { add(lock); add(owner); } },"10")){ try { TimeUnit.MILLISECONDS.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } //嘗試獲取鎖,超過時間退出 public boolean tryLock(String lock,String owner,long time,TimeUnit timeUnit){ final long endTime=timeUnit.toMillis(time)+System.currentTimeMillis(); boolean result=false; while(!(result=ScriptUtil.readAndExecute("redis_lock.lua",stringRedisTemplate, new ArrayList<String>(){ { add(lock); add(owner); } },"300")) && endTime-System.currentTimeMillis()>=0){ try { TimeUnit.MILLISECONDS.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } return result; } //釋放鎖 public boolean unlock(String lock,String owner){ return ScriptUtil.readAndExecute("redis_unlock.lua",stringRedisTemplate, new ArrayList<String>(){ { add(lock); add(owner); } }); } }