基于Redis的分布式鎖

1.基于Redis單點的分布式鎖

1.1 加鎖

  • Redis命令
#my_random_value是由客戶端生成的一個隨機字符串,它要保證在足夠長的一段時間內(nèi)在所有客戶端的所有獲取鎖的請求中都是唯一的。
#NX表示只有當resource_name對應(yīng)的key值不存在的時候才能SET成功。這保證了只有第一個請求的客戶端才能獲得鎖,而其它客戶端在鎖被釋放之前都無法獲得鎖。
#PX 30000表示這個鎖有一個30秒的自動過期時間。當然,這里30秒只是一個例子,客戶端可以選擇合適的過期時間。
SET resource_name my_random_value NX PX 30000
  • Java 版本
public class RedisTool {
 
    private static final String LOCK_SUCCESS = "OK";
    private static final String SET_IF_NOT_EXIST = "NX";
    private static final String SET_WITH_EXPIRE_TIME = "PX";
 
    /**
     * 嘗試獲取分布式鎖
     * @param jedis Redis客戶端
     * @param lockKey 鎖
     * @param requestId 請求標識
     * @param expireTime 超期時間
     * @return 是否獲取成功
     */
    public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) {
        /*
         * 第一個為key,我們使用key來當鎖,因為key是唯一的。
         * 第二個為value,我們傳的是requestId,很多童鞋可能不明白,有key作為鎖不就夠了嗎,為什么還要用到value?原因就是我們在上面講到可靠性時,分布式鎖要滿足第四個條件解鈴還須系鈴人,通過給value賦值為requestId,我們就知道這把鎖是哪個請求加的了,在解鎖的時候就可以有依據(jù)。requestId可以使用UUID.randomUUID().toString()方法生成。
         * 第三個為nxxx,這個參數(shù)我們填的是NX,意思是SET IF NOT EXIST,即當key不存在時,我們進行set操作;若key已經(jīng)存在,則不做任何操作;
         * 第四個為expx,這個參數(shù)我們傳的是PX,意思是我們要給這個key加一個過期的設(shè)置,具體時間由第五個參數(shù)決定。
         * 第五個為time,與第四個參數(shù)相呼應(yīng),代表key的過期時間。
        */
        String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
 
        if (LOCK_SUCCESS.equals(result)) {
            return true;
        }
        return false;
 
    }
 
}

1.2 釋放鎖

  • Redis命令:Redis Lua腳本
#這段Lua腳本在執(zhí)行的時候要把前面的my_random_value作為ARGV[1]的值傳進去,把resource_name作為KEYS[1]的值傳進去。
if redis.call("get",KEYS[1]) == ARGV[1] then
    return redis.call("del",KEYS[1])
else
    return 0
end
  • Java版本
public class RedisTool {
 
    private static final Long RELEASE_SUCCESS = 1L;
 
    /**
     * 釋放分布式鎖
     * @param jedis Redis客戶端
     * @param lockKey 鎖
     * @param requestId 請求標識
     * @return 是否釋放成功
     */
    public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) {
        /*
        *將Lua代碼傳到j(luò)edis.eval()方法里,并使參數(shù)KEYS[1]賦值為lockKey,ARGV[1]賦值為requestId。eval()方法是將Lua代碼交給Redis服務(wù)端執(zhí)行。
        */
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
 
        if (RELEASE_SUCCESS.equals(result)) {
            return true;
        }
        return false;
 
    }
 
}

2.基于集群的分布式鎖

使用Redis提供的RedLock,具體可以參考官方文檔

3.參考資料

Martin 與 antirez 的討論
基于Redis的分布式鎖到底安全嗎(上)
基于Redis的分布式鎖到底安全嗎(下)
Redis單機實現(xiàn)分布式鎖分析
Redis 分布式鎖的正確實現(xiàn)方式( Java 版 )

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容