<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>

          【07期】Redis中是如何實(shí)現(xiàn)分布式鎖的?

          共 3461字,需瀏覽 7分鐘

           ·

          2020-07-29 13:38

          程序員的成長(zhǎng)之路
          互聯(lián)網(wǎng)/程序員/技術(shù)/資料共享?
          關(guān)注


          閱讀本文大概需要 4.5 分鐘。

          來(lái)自:java面試題精選

          分布式鎖常見(jiàn)的三種實(shí)現(xiàn)方式:
          1. 數(shù)據(jù)庫(kù)樂(lè)觀鎖;

          2. 基于Redis的分布式鎖;

          3. 基于ZooKeeper的分布式鎖。

          本地面試考點(diǎn)是,你對(duì)Redis使用熟悉嗎?Redis中是如何實(shí)現(xiàn)分布式鎖的。

          要點(diǎn)

          Redis要實(shí)現(xiàn)分布式鎖,以下條件應(yīng)該得到滿足
          互斥性
          • 在任意時(shí)刻,只有一個(gè)客戶(hù)端能持有鎖。

          不能死鎖
          • 客戶(hù)端在持有鎖的期間崩潰而沒(méi)有主動(dòng)解鎖,也能保證后續(xù)其他客戶(hù)端能加鎖。

          容錯(cuò)性
          • 只要大部分的Redis節(jié)點(diǎn)正常運(yùn)行,客戶(hù)端就可以加鎖和解鎖。

          實(shí)現(xiàn)

          可以直接通過(guò)?set key value px milliseconds nx?命令實(shí)現(xiàn)加鎖, 通過(guò)Lua腳本實(shí)現(xiàn)解鎖。
          //獲取鎖(unique_value可以是UUID等)
          SET?resource_name?unique_value?NX?PX??30000

          //釋放鎖(lua腳本中,一定要比較value,防止誤解鎖)
          if?redis.call("get",KEYS[1])?==?ARGV[1]?then
          ????return?redis.call("del",KEYS[1])
          else
          ????return?0
          end
          代碼解釋
          • set 命令要用?set key value px milliseconds nx,替代?setnx + expire?需要分兩次執(zhí)行命令的方式,保證了原子性,

          • value 要具有唯一性,可以使用UUID.randomUUID().toString()方法生成,用來(lái)標(biāo)識(shí)這把鎖是屬于哪個(gè)請(qǐng)求加的,在解鎖的時(shí)候就可以有依據(jù);

          • 釋放鎖時(shí)要驗(yàn)證 value 值,防止誤解鎖;

          • 通過(guò) Lua 腳本來(lái)避免 Check And Set 模型的并發(fā)問(wèn)題,因?yàn)樵卺尫沛i的時(shí)候因?yàn)樯婕暗蕉鄠€(gè)Redis操作 (利用了eval命令執(zhí)行Lua腳本的原子性);

          加鎖代碼分析
          首先,set()加入了NX參數(shù),可以保證如果已有key存在,則函數(shù)不會(huì)調(diào)用成功,也就是只有一個(gè)客戶(hù)端能持有鎖,滿足互斥性。其次,由于我們對(duì)鎖設(shè)置了過(guò)期時(shí)間,即使鎖的持有者后續(xù)發(fā)生崩潰而沒(méi)有解鎖,鎖也會(huì)因?yàn)榈搅诉^(guò)期時(shí)間而自動(dòng)解鎖(即key被刪除),不會(huì)發(fā)生死鎖。最后,因?yàn)槲覀儗alue賦值為requestId,用來(lái)標(biāo)識(shí)這把鎖是屬于哪個(gè)請(qǐng)求加的,那么在客戶(hù)端在解鎖的時(shí)候就可以進(jìn)行校驗(yàn)是否是同一個(gè)客戶(hù)端。
          解鎖代碼分析
          將Lua代碼傳到j(luò)edis.eval()方法里,并使參數(shù)KEYS[1]賦值為lockKey,ARGV[1]賦值為requestId。在執(zhí)行的時(shí)候,首先會(huì)獲取鎖對(duì)應(yīng)的value值,檢查是否與requestId相等,如果相等則解鎖(刪除key)。
          存在的風(fēng)險(xiǎn)
          如果存儲(chǔ)鎖對(duì)應(yīng)key的那個(gè)節(jié)點(diǎn)掛了的話,就可能存在丟失鎖的風(fēng)險(xiǎn),導(dǎo)致出現(xiàn)多個(gè)客戶(hù)端持有鎖的情況,這樣就不能實(shí)現(xiàn)資源的獨(dú)享了。
          1. 客戶(hù)端A從master獲取到鎖

          2. 在master將鎖同步到slave之前,master宕掉了(Redis的主從同步通常是異步的)。
            主從切換,slave節(jié)點(diǎn)被晉級(jí)為master節(jié)點(diǎn)

          3. 客戶(hù)端B取得了同一個(gè)資源被客戶(hù)端A已經(jīng)獲取到的另外一個(gè)鎖。導(dǎo)致存在同一時(shí)刻存不止一個(gè)線程獲取到鎖的情況。

          redlock算法出現(xiàn)

          這個(gè)場(chǎng)景是假設(shè)有一個(gè) redis cluster,有 5 個(gè) redis master 實(shí)例。然后執(zhí)行如下步驟獲取一把鎖:
          1. 獲取當(dāng)前時(shí)間戳,單位是毫秒;

          2. 跟上面類(lèi)似,輪流嘗試在每個(gè) master 節(jié)點(diǎn)上創(chuàng)建鎖,過(guò)期時(shí)間較短,一般就幾十毫秒;

          3. 嘗試在大多數(shù)節(jié)點(diǎn)上建立一個(gè)鎖,比如 5 個(gè)節(jié)點(diǎn)就要求是 3 個(gè)節(jié)點(diǎn) n / 2 + 1;

          4. 客戶(hù)端計(jì)算建立好鎖的時(shí)間,如果建立鎖的時(shí)間小于超時(shí)時(shí)間,就算建立成功了;

          5. 要是鎖建立失敗了,那么就依次之前建立過(guò)的鎖刪除;

          6. 只要?jiǎng)e人建立了一把分布式鎖,你就得不斷輪詢(xún)?nèi)L試獲取鎖。


          Redis 官方給出了以上兩種基于 Redis 實(shí)現(xiàn)分布式鎖的方法,詳細(xì)說(shuō)明可以查看:
          https://redis.io/topics/distlock 。

          Redisson實(shí)現(xiàn)

          Redisson是一個(gè)在Redis的基礎(chǔ)上實(shí)現(xiàn)的Java駐內(nèi)存數(shù)據(jù)網(wǎng)格(In-Memory Data Grid)。它不僅提供了一系列的分布式的Java常用對(duì)象,還實(shí)現(xiàn)了可重入鎖(Reentrant Lock)、公平鎖(Fair Lock、聯(lián)鎖(MultiLock)、 紅鎖(RedLock)、 讀寫(xiě)鎖(ReadWriteLock)等,還提供了許多分布式服務(wù)。
          Redisson提供了使用Redis的最簡(jiǎn)單和最便捷的方法。Redisson的宗旨是促進(jìn)使用者對(duì)Redis的關(guān)注分離(Separation of Concern),從而讓使用者能夠?qū)⒕Ω械胤旁谔幚順I(yè)務(wù)邏輯上。
          Redisson 分布式重入鎖用法
          Redisson 支持單點(diǎn)模式、主從模式、哨兵模式、集群模式,這里以單點(diǎn)模式為例:
          //?1.構(gòu)造redisson實(shí)現(xiàn)分布式鎖必要的Config
          Config?config?=?new?Config();
          config.useSingleServer().setAddress("redis://127.0.0.1:5379").setPassword("123456").setDatabase(0);
          //?2.構(gòu)造RedissonClient
          RedissonClient?redissonClient?=?Redisson.create(config);
          //?3.獲取鎖對(duì)象實(shí)例(無(wú)法保證是按線程的順序獲取到)
          RLock?rLock?=?redissonClient.getLock(lockKey);
          try?{
          ????/**
          ?????*?4.嘗試獲取鎖
          ?????*?waitTimeout?嘗試獲取鎖的最大等待時(shí)間,超過(guò)這個(gè)值,則認(rèn)為獲取鎖失敗
          ?????*?leaseTime???鎖的持有時(shí)間,超過(guò)這個(gè)時(shí)間鎖會(huì)自動(dòng)失效(值應(yīng)設(shè)置為大于業(yè)務(wù)處理的時(shí)間,確保在鎖有效期內(nèi)業(yè)務(wù)能處理完)
          ?????*/

          ????boolean?res?=?rLock.tryLock((long)waitTimeout,?(long)leaseTime,?TimeUnit.SECONDS);
          ????if?(res)?{
          ????????//成功獲得鎖,在這里處理業(yè)務(wù)
          ????}
          }?catch?(Exception?e)?{
          ????throw?new?RuntimeException("aquire?lock?fail");
          }finally{
          ????//無(wú)論如何,?最后都要解鎖
          ????rLock.unlock();
          }
          加鎖流程圖
          解鎖流程圖
          我們可以看到,RedissonLock是可重入的,并且考慮了失敗重試,可以設(shè)置鎖的最大等待時(shí)間, 在實(shí)現(xiàn)上也做了一些優(yōu)化,減少了無(wú)效的鎖申請(qǐng),提升了資源的利用率。
          需要特別注意的是,RedissonLock 同樣沒(méi)有解決 節(jié)點(diǎn)掛掉的時(shí)候,存在丟失鎖的風(fēng)險(xiǎn)的問(wèn)題。而現(xiàn)實(shí)情況是有一些場(chǎng)景無(wú)法容忍的,所以 Redisson 提供了實(shí)現(xiàn)了redlock算法的 RedissonRedLock,RedissonRedLock 真正解決了單點(diǎn)失敗的問(wèn)題,代價(jià)是需要額外的為 RedissonRedLock 搭建Redis環(huán)境。
          所以,如果業(yè)務(wù)場(chǎng)景可以容忍這種小概率的錯(cuò)誤,則推薦使用 RedissonLock, 如果無(wú)法容忍,則推薦使用 RedissonRedLock。

          參考

          https://github.com/javazhiyin/advanced-java/
          https://crazyfzw.github.io/2019/04/15/distributed-locks-with-redis/

          推薦閱讀:

          某程序員動(dòng)了公司的祖?zhèn)鞔a“屎山”,半年后怒交辭職報(bào)告!

          有點(diǎn)牛逼,滴滴開(kāi)源的分布式id生成系統(tǒng)

          5T技術(shù)資源大放送!包括但不限于:C/C++,Linux,Python,Java,PHP,人工智能,單片機(jī),樹(shù)莓派,等等。在公眾號(hào)內(nèi)回復(fù)「2048」,即可免費(fèi)獲取!!

          微信掃描二維碼,關(guān)注我的公眾號(hào)

          寫(xiě)留言

          朕已閱?

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

          手機(jī)掃一掃分享

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

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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>
                  青青草激情在线视频 | 狠狠干干 | 91超碰在线观看 | 爆操人妖| 91n-最新地址发布页 |