溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Redis中如何實(shí)現(xiàn)分布式鎖

發(fā)布時間:2021-08-10 16:34:25 來源:億速云 閱讀:159 作者:Leah 欄目:編程語言

這篇文章給大家介紹Redis中如何實(shí)現(xiàn)分布式鎖,內(nèi)容非常詳細(xì),感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。

Redis要實(shí)現(xiàn)分布式鎖,以下條件應(yīng)該得到滿足

互斥性

  •  在任意時刻,只有一個客戶端能持有鎖。

不能死鎖

  •  客戶端在持有鎖的期間崩潰而沒有主動解鎖,也能保證后續(xù)其他客戶端能加鎖。

容錯性

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

實(shí)現(xiàn)

可以直接通過 set key value px milliseconds nx 命令實(shí)現(xiàn)加鎖, 通過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()方法生成,用來標(biāo)識這把鎖是屬于哪個請求加的,在解鎖的時候就可以有依據(jù);

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

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

加鎖代碼分析

首先,set()加入了NX參數(shù),可以保證如果已有key存在,則函數(shù)不會調(diào)用成功,也就是只有一個客戶端能持有鎖,滿足互斥性。其次,由于我們對鎖設(shè)置了過期時間,即使鎖的持有者后續(xù)發(fā)生崩潰而沒有解鎖,鎖也會因?yàn)榈搅诉^期時間而自動解鎖(即key被刪除),不會發(fā)生死鎖。最后,因?yàn)槲覀儗alue賦值為requestId,用來標(biāo)識這把鎖是屬于哪個請求加的,那么在客戶端在解鎖的時候就可以進(jìn)行校驗(yàn)是否是同一個客戶端。

解鎖代碼分析

將Lua代碼傳到j(luò)edis.eval()方法里,并使參數(shù)KEYS[1]賦值為lockKey,ARGV[1]賦值為requestId。在執(zhí)行的時候,首先會獲取鎖對應(yīng)的value值,檢查是否與requestId相等,如果相等則解鎖(刪除key)。

存在的風(fēng)險(xiǎn)

如果存儲鎖對應(yīng)key的那個節(jié)點(diǎn)掛了的話,就可能存在丟失鎖的風(fēng)險(xiǎn),導(dǎo)致出現(xiàn)多個客戶端持有鎖的情況,這樣就不能實(shí)現(xiàn)資源的獨(dú)享了。

  1. 鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)

  2.  客戶端A從master獲取到鎖

  3.  在master將鎖同步到slave之前,master宕掉了(Redis的主從同步通常是異步的)。

  主從切換,slave節(jié)點(diǎn)被晉級為master節(jié)點(diǎn)

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

redlock算法出現(xiàn)

這個場景是假設(shè)有一個 redis cluster,有 5 個 redis master 實(shí)例。然后執(zhí)行如下步驟獲取一把鎖:

  1. 鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)

  2.  獲取當(dāng)前時間戳,單位是毫秒;

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

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

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

  6.  要是鎖建立失敗了,那么就依次之前建立過的鎖刪除;

  7.  只要別人建立了一把分布式鎖,你就得不斷輪詢?nèi)L試獲取鎖。

Redis中如何實(shí)現(xiàn)分布式鎖

Redis 官方給出了以上兩種基于 Redis 實(shí)現(xiàn)分布式鎖的方法,詳細(xì)說明可以查看:

https://redis.io/topics/distlock 。

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

Redisson是一個在Redis的基礎(chǔ)上實(shí)現(xiàn)的Java駐內(nèi)存數(shù)據(jù)網(wǎng)格(In-Memory Data Grid)。它不僅提供了一系列的分布式的Java常用對象,還實(shí)現(xiàn)了可重入鎖(Reentrant Lock)、公平鎖(Fair Lock、聯(lián)鎖(MultiLock)、 紅鎖(RedLock)、 讀寫鎖(ReadWriteLock)等,還提供了許多分布式服務(wù)。

Redisson提供了使用Redis的最簡單和最便捷的方法。Redisson的宗旨是促進(jìn)使用者對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.獲取鎖對象實(shí)例(無法保證是按線程的順序獲取到)  RLock rLock = redissonClient.getLock(lockKey);  try {      /**       * 4.嘗試獲取鎖       * waitTimeout 嘗試獲取鎖的最大等待時間,超過這個值,則認(rèn)為獲取鎖失敗       * leaseTime   鎖的持有時間,超過這個時間鎖會自動失效(值應(yīng)設(shè)置為大于業(yè)務(wù)處理的時間,確保在鎖有效期內(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{      //無論如何, 最后都要解鎖      rLock.unlock();  }

加鎖流程圖

Redis中如何實(shí)現(xiàn)分布式鎖

解鎖流程圖

Redis中如何實(shí)現(xiàn)分布式鎖

我們可以看到,RedissonLock是可重入的,并且考慮了失敗重試,可以設(shè)置鎖的最大等待時間, 在實(shí)現(xiàn)上也做了一些優(yōu)化,減少了無效的鎖申請,提升了資源的利用率。

需要特別注意的是,RedissonLock 同樣沒有解決 節(jié)點(diǎn)掛掉的時候,存在丟失鎖的風(fēng)險(xiǎn)的問題。而現(xiàn)實(shí)情況是有一些場景無法容忍的,所以 Redisson 提供了實(shí)現(xiàn)了redlock算法的 RedissonRedLock,RedissonRedLock 真正解決了單點(diǎn)失敗的問題,代價(jià)是需要額外的為 RedissonRedLock 搭建Redis環(huán)境。

關(guān)于Redis中如何實(shí)現(xiàn)分布式鎖就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學(xué)到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI