1. Redis分布式鎖怎么玩?

        共 2888字,需瀏覽 6分鐘

         ·

        2020-12-04 15:50

        點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號(hào)”

        優(yōu)質(zhì)文章,第一時(shí)間送達(dá)

        ? 作者?|??書夢(mèng)一生

        來(lái)源 |? urlify.cn/bmIzma

        66套java從入門到精通實(shí)戰(zhàn)課程分享

        概述


        為了防止分布式系統(tǒng)中的多個(gè)進(jìn)程之間相互干擾,我們需要一種分布式協(xié)調(diào)技術(shù)來(lái)對(duì)這些進(jìn)程進(jìn)行調(diào)度。而這個(gè)分布式協(xié)調(diào)技術(shù)的核心就是來(lái)實(shí)現(xiàn)這個(gè)分布式鎖。

        為什么要使用分布式鎖



        • 成員變量 A 存在 JVM1、JVM2、JVM3 三個(gè) JVM 內(nèi)存中

        • 成員變量 A 同時(shí)都會(huì)在 JVM 分配一塊內(nèi)存,三個(gè)請(qǐng)求發(fā)過(guò)來(lái)同時(shí)對(duì)這個(gè)變量操作,顯然結(jié)果是不對(duì)的

        • 不是同時(shí)發(fā)過(guò)來(lái),三個(gè)請(qǐng)求分別操作三個(gè)不同 JVM 內(nèi)存區(qū)域的數(shù)據(jù),變量 A 之間不存在共享,也不具有可見性,處理的結(jié)果也是不對(duì)的
          注:該成員變量 A 是一個(gè)有狀態(tài)的對(duì)象

        如果我們業(yè)務(wù)中確實(shí)存在這個(gè)場(chǎng)景的話,我們就需要一種方法解決這個(gè)問(wèn)題,這就是分布式鎖要解決的問(wèn)題

        分布式鎖應(yīng)該具備哪些條件


        • 在分布式系統(tǒng)環(huán)境下,一個(gè)方法在同一時(shí)間只能被一個(gè)機(jī)器的一個(gè)線程執(zhí)行

        • 高可用的獲取鎖與釋放鎖

        • 高性能的獲取鎖與釋放鎖

        • 具備可重入特性(可理解為重新進(jìn)入,由多于一個(gè)任務(wù)并發(fā)使用,而不必?fù)?dān)心數(shù)據(jù)錯(cuò)誤)

        • 具備鎖失效機(jī)制,防止死鎖

        • 具備非阻塞鎖特性,即沒(méi)有獲取到鎖將直接返回獲取鎖失敗

        分布式鎖的實(shí)現(xiàn)有哪些


        • Memcached:利用 Memcached 的 add 命令。此命令是原子性操作,只有在 key 不存在的情況下,才能 add 成功,也就意味著線程得到了鎖。

        • Redis:和 Memcached 的方式類似,利用 Redis 的 setnx 命令。此命令同樣是原子性操作,只有在 key 不存在的情況下,才能 set成功。

        • Zookeeper:利用 Zookeeper 的順序臨時(shí)節(jié)點(diǎn),來(lái)實(shí)現(xiàn)分布式鎖和等待隊(duì)列。Zookeeper 設(shè)計(jì)的初衷,就是為了實(shí)現(xiàn)分布式鎖服務(wù)的。

        • Chubby:Google 公司實(shí)現(xiàn)的粗粒度分布式鎖服務(wù),底層利用了 Paxos 一致性算法。

        分布式鎖的Redis實(shí)現(xiàn)


        加鎖:
        ?
        String?threadId?=?Thread.currentThread().getId()
        set(key,threadId?,30,NX)、

        解鎖:
        ?
        if(threadId?.equals(redisClient.get(key))){
        ????del(key)
        }

        但是,這樣做又隱含了一個(gè)新的問(wèn)題,判斷和釋放鎖是兩個(gè)獨(dú)立操作,不是原子性。

        出現(xiàn)并發(fā)的可能性

        還是剛才第二點(diǎn)所描述的場(chǎng)景,雖然我們避免了線程 A 誤刪掉 key 的情況,但是同一時(shí)間有 A,B 兩個(gè)線程在訪問(wèn)代碼塊,仍然是不完美的。怎么辦呢?我們可以讓獲得鎖的線程開啟一個(gè)守護(hù)線程,用來(lái)給快要過(guò)期的鎖“續(xù)航”。

        ??

        當(dāng)過(guò)去了 29 秒,線程 A 還沒(méi)執(zhí)行完,這時(shí)候守護(hù)線程會(huì)執(zhí)行 expire 指令,為這把鎖“續(xù)命”20 秒。守護(hù)線程從第 29 秒開始執(zhí)行,每 20 秒執(zhí)行一次。

        ?

        ?

        當(dāng)線程 A 執(zhí)行完任務(wù),會(huì)顯式關(guān)掉守護(hù)線程。

        ??

        另一種情況,如果節(jié)點(diǎn) 1 忽然斷電,由于線程 A 和守護(hù)線程在同一個(gè)進(jìn)程,守護(hù)線程也會(huì)停下。這把鎖到了超時(shí)的時(shí)候,沒(méi)人給它續(xù)命,也就自動(dòng)釋放了。

        ??

        正確實(shí)現(xiàn)寫法如下:

        /**
        ????*?嘗試獲取分布式鎖
        ????*?@param?jedis?Redis客戶端
        ????*?@param?lockKey?鎖
        ????*?@param?requestId?請(qǐng)求標(biāo)識(shí)
        ????*?@param?expireTime?超期時(shí)間
        ????*?@return?是否獲取成功
        ????*/
        ???public?static?boolean?tryGetDistributedLock(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;
        ???}
        ?
        ???/**
        ????*?釋放分布式鎖
        ????*?@param?jedis?Redis客戶端
        ????*?@param?lockKey?鎖
        ????*?@param?requestId?請(qǐng)求標(biāo)識(shí)
        ????*?@return?是否釋放成功
        ????*/
        ???public?static?boolean?releaseDistributedLock(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;
        ???}

        對(duì)比

        數(shù)據(jù)庫(kù)分布式鎖實(shí)現(xiàn)


        缺點(diǎn):1.db操作性能較差,并且有鎖表的風(fēng)險(xiǎn)
        2.非阻塞操作失敗后,需要輪詢,占用cpu資源;
        3.長(zhǎng)時(shí)間不commit或者長(zhǎng)時(shí)間輪詢,可能會(huì)占用較多連接資源

        Redis(緩存)分布式鎖實(shí)現(xiàn)


        缺點(diǎn):1.鎖刪除失敗 過(guò)期時(shí)間不好控制
        2.非阻塞,操作失敗后,需要輪詢,占用cpu資源;

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


        缺點(diǎn):性能不如redis實(shí)現(xiàn),主要原因是寫操作(獲取鎖釋放鎖)都需要在Leader上執(zhí)行,然后同步到follower。

        總之:ZooKeeper有較好的性能和可靠性。

        從理解的難易程度角度(從低到高)數(shù)據(jù)庫(kù) > 緩存 > Zookeeper
        從實(shí)現(xiàn)的復(fù)雜性角度(從低到高)Zookeeper >= 緩存 > 數(shù)據(jù)庫(kù)
        從性能角度(從高到低)緩存 > Zookeeper >= 數(shù)據(jù)庫(kù)
        從可靠性角度(從高到低)Zookeeper > 緩存 > 數(shù)據(jù)庫(kù)




        粉絲福利:實(shí)戰(zhàn)springboot+CAS單點(diǎn)登錄系統(tǒng)視頻教程免費(fèi)領(lǐng)取

        ???

        ?長(zhǎng)按上方微信二維碼?2 秒
        即可獲取資料



        感謝點(diǎn)贊支持下哈?

        瀏覽 69
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
          
          

            1. 国产精品久久久久久久久吹潮 | 好爽,慢点,阿,好深医生 | h文一女多男从小c到大 | 男女赤裸裸真人交性视频软件 | 日韩乱伦AV |