<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Redis分布式鎖怎么玩?

          共 2888字,需瀏覽 6分鐘

           ·

          2020-12-04 15:50

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

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

          ? 作者?|??書夢一生

          來源 |? urlify.cn/bmIzma

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

          概述


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

          為什么要使用分布式鎖



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

          • 成員變量 A 同時都會在 JVM 分配一塊內(nèi)存,三個請求發(fā)過來同時對這個變量操作,顯然結(jié)果是不對的

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

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

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


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

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

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

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

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

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

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


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

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

          • Zookeeper:利用 Zookeeper 的順序臨時節(jié)點(diǎn),來實(shí)現(xiàn)分布式鎖和等待隊列。Zookeeper 設(shè)計的初衷,就是為了實(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)
          }

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

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

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

          ??

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

          ?

          ?

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

          ??

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

          ??

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

          /**
          ????*?嘗試獲取分布式鎖
          ????*?@param?jedis?Redis客戶端
          ????*?@param?lockKey?鎖
          ????*?@param?requestId?請求標(biāo)識
          ????*?@param?expireTime?超期時間
          ????*?@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?請求標(biāo)識
          ????*?@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;
          ???}

          對比

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


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

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


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

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


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

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

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




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

          ???

          ?長按上方微信二維碼?2 秒
          即可獲取資料



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

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

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  好男人WWW一区二区三区 | 久久久成人电影视频 | 国产视频一区二区三区四区 | 五月天婷婷乱伦小说 | 日日操夜夜爽 |