Redis 分布式鎖的正確實(shí)現(xiàn)方式秤掌,你們公司的有弊端嗎愁铺?快來(lái)改善

在目前的大型互聯(lián)網(wǎng)公司中茵乱,常見(jiàn)的分布式鎖的實(shí)現(xiàn)方式大概分為三種孟岛?

利用數(shù)據(jù)庫(kù)條件命中索引實(shí)現(xiàn)樂(lè)觀鎖

基于redis的分布式鎖

基于zookeeper的分布式鎖的實(shí)現(xiàn)

當(dāng)然分布式鎖的實(shí)現(xiàn)方式有很多,但是不管哪個(gè)其底層的設(shè)計(jì)原則以及思想都是一致斤贰,我們分析一下設(shè)計(jì)分布式鎖的幾個(gè)重點(diǎn)要素次询。

為了保證分布式鎖的完整實(shí)現(xiàn),大概需要滿足4個(gè)核心的步驟:

互斥原則 -->不管在任何的時(shí)間段內(nèi)渗蟹,只有一個(gè)用戶線程能夠占有其鎖的資源。

避免死鎖的發(fā)生 --> 即使說(shuō)有一個(gè)用戶線程在持有鎖的時(shí)候發(fā)生異常授艰,不能影響接下來(lái)的線程獲取鎖資源世落。

容錯(cuò)率問(wèn)題 --> 這個(gè)主要針對(duì)是大型項(xiàng)目的集群節(jié)點(diǎn)問(wèn)題,意思就是只要1/2的節(jié)點(diǎn)能夠正常運(yùn)行的情況下谷朝,那么就能夠正常的加鎖和釋放鎖武花。

解鈴還須系鈴人 --> 主要的意思就是A線程獲取的鎖資源只能由線程A去釋放此鎖資源,客戶端的鎖資源不能讓別人給釋放了体箕。

java實(shí)現(xiàn)redis分布式鎖

引入java針對(duì)redis開(kāi)發(fā)依賴(lài)

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.0</version>
</dependency>

coding:實(shí)現(xiàn)細(xì)節(jié)

package com.test.redis;

import redis.clients.jedis.Jedis;

/**
 * redis實(shí)現(xiàn)分布式鎖的具體實(shí)現(xiàn)
 */
public class RedisTemple {

    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 鎖資源的key值
     * @param requestId 唯一資源用戶請(qǐng)求標(biāo)識(shí)
     * @param expireTime 鎖員超期時(shí)間
     * @return 是否獲取成功
     */
    public static boolean tryGetLock(Jedis jedis, String lockKey, String requestId, int expireTime) {

        String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);

        if (LOCK_SUCCESS.equals(result)) {
            return true;
        }
        return false;
    }
}

具體代碼分析:

String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
這行代碼是嘗試獲取redis鎖的實(shí)現(xiàn)累铅,大家可以看到有5個(gè)參數(shù)

lockKey:設(shè)置當(dāng)前redis鎖資源的key值,key值在項(xiàng)目中要缺保唯一性

requestId:這個(gè)參數(shù)可能很多的人都有些迷惑了菇民,因?yàn)槠綍r(shí)我們?cè)谄髽I(yè)中的redis鎖的實(shí)現(xiàn),知識(shí)存放一個(gè)key值就可以了阔馋,但是這里為什么要一個(gè)value值呢娇掏?這個(gè)就談到了我們開(kāi)始時(shí)說(shuō)的“解鈴還須系鈴人” requestId我們可以采用 UUID.randomUUID().toString() 去做或者采用其他的業(yè)務(wù)參數(shù)也是可以的。

SET_IF_NOT_EXIST 這個(gè)參數(shù)是我們r(jià)edis所需要的參數(shù)NX驹碍,意思就是 SET IF NOT EXIST 當(dāng)key值存在的時(shí)候就不能set 不存在就set,這個(gè)確保了互斥的原則怔球。

SET_WITH_EXPIRE_TIME 這個(gè)參數(shù)也是我們操作redis需要的浮还,我們給的是PX,意思是我們要給key設(shè)置一個(gè)超時(shí)的事件钧舌,集體事件由下一個(gè)參數(shù) expireTime 決定。

錯(cuò)誤案例:

我們經(jīng)常能見(jiàn)到的一些錯(cuò)誤的redis鎖的實(shí)現(xiàn)案例崭歧。

就是用redis的jedis.setnx()和jedis.expire()組合實(shí)現(xiàn)加鎖和解鎖的操作

public static void getRedisLock(Jedis jedis, String lockKey, String requestId, int expireTime){
        Long result = jedis.setnx(lockKey, requestId);
        if (result == 1) {
            // 此段代碼可以看到針對(duì)redis的鎖實(shí)現(xiàn)不是原子操作撞牢,如果次處發(fā)生異常,那么我們給key加時(shí)間就會(huì)失敗所宰,因此發(fā)生死鎖
            jedis.expire(lockKey, expireTime);
        }
    }

當(dāng)然還有很多的錯(cuò)誤的案例畜挥,在這里就不和大家一一的解析了,只要遵循對(duì)的設(shè)計(jì)原則就是安全的蟹但。

接下來(lái)我們看一下真正正確的操作是怎樣的!

private static final Long RELEASE_SUCCESS = 1L;

/**
 * 釋放分布式鎖
 * @param jedis 表示redis客戶端
 * @param lockKey 鎖資源key值
 * @param requestId 唯一性請(qǐng)求標(biāo)識(shí)
 * @return 是否釋放成功
 */
public static boolean redisRsleaseLock(Jedis jedis, String lockKey, String requestId) {

    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;

}

上面代碼可以分析到斟冕,redis鎖的釋放只需要兩行的代碼我們就可以搞定了缅阳,我們先定義一個(gè)Lua的腳本代碼,這個(gè)大家可以去網(wǎng)上百度一下很多的秀撇,其主要就是為了保證此步的操作是原子的操作向族。

錯(cuò)誤的解鎖案例

public static void redisRelieaseLock(Jedis jedis, String lockKey, String requestId){
        // 判斷加鎖與解鎖是不是同一個(gè)客戶端
        if (requestId.equals(jedis.get(lockKey))) {
            // 若在此時(shí),這把鎖突然不是這個(gè)客戶端的件相,則會(huì)誤解鎖
            jedis.del(lockKey);
        }
    }

好了,在此和大家分享到這里泛范,大家用到時(shí)候可以參考一下紊撕,具體的細(xì)節(jié)進(jìn)行優(yōu)化即可在正規(guī)的項(xiàng)目中去操作了!

關(guān)注:編程如此簡(jiǎn)單 一起學(xué)習(xí)一起成長(zhǎng)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末区赵,一起剝皮案震驚了整個(gè)濱河市浪南,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌络凿,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,651評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件各谚,死亡現(xiàn)場(chǎng)離奇詭異到千,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)膀息,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)了赵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人冗酿,你說(shuō)我怎么就攤上這事〔锰妫” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,931評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵襟沮,是天一觀的道長(zhǎng)昌腰。 經(jīng)常有香客問(wèn)我,道長(zhǎng)遭商,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,218評(píng)論 1 292
  • 正文 為了忘掉前任怎虫,我火速辦了婚禮困介,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘徒扶。我一直安慰自己,他們只是感情好姜骡,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,234評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布屿良。 她就那樣靜靜地躺著圈澈,像睡著了一般康栈。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上啥么,一...
    開(kāi)封第一講書(shū)人閱讀 51,198評(píng)論 1 299
  • 那天贰逾,我揣著相機(jī)與錄音,去河邊找鬼氯迂。 笑死践叠,一個(gè)胖子當(dāng)著我的面吹牛囚戚,可吹牛的內(nèi)容都是我干的轧简。 我是一名探鬼主播,決...
    沈念sama閱讀 40,084評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼拳芙,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼皮璧!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起悴务,我...
    開(kāi)封第一講書(shū)人閱讀 38,926評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎羡疗,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體叨恨,經(jīng)...
    沈念sama閱讀 45,341評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,563評(píng)論 2 333
  • 正文 我和宋清朗相戀三年送矩,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了哪替。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,731評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡夷家,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出摸袁,到底是詐尸還是另有隱情,我是刑警寧澤靠汁,帶...
    沈念sama閱讀 35,430評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站蝶怔,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏踢星。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,036評(píng)論 3 326
  • 文/蒙蒙 一成洗、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧瓶殃,春花似錦、人聲如沸遥椿。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,676評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)遏考。三九已至,卻和暖如春灌具,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背咖楣。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,829評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工诱贿, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人珠十。 一個(gè)月前我還...
    沈念sama閱讀 47,743評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像焙蹭,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子拯钻,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,629評(píng)論 2 354