最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Redis:Redisson分布式鎖的使用方式(推薦使用)

 更新時(shí)間:2024年04月22日 09:01:01   作者:穿城大餅  
這篇文章主要介紹了Redis:Redisson分布式鎖的使用方式(推薦使用),具有很好的參考價(jià)值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教

Redis:Redisson分布式鎖的使用(生產(chǎn)環(huán)境下)(推薦使用)

關(guān)鍵詞

  • 基于NIO的Netty框架,生產(chǎn)環(huán)境使用分布式鎖
  • redisson加鎖:lua腳本加鎖(其他客戶端自旋)
  • 自動延時(shí)機(jī)制:啟動watch dog,后臺線程,每隔10秒檢查一下客戶端1還持有鎖key,會不斷的延長鎖key的生存時(shí)間
  • 可重入鎖機(jī)制:第二個if判斷 ,myLock :{“8743c9c0-0795-4907-87fd-6c719a6b4586:1”:2 }
  • 釋放鎖:無鎖直接返回;有鎖不是我加的,返回;有鎖是我加的,執(zhí)行hincrby -1,當(dāng)重入鎖減完才執(zhí)行del操作
  • Redis使用同一個Lua解釋器來執(zhí)行所有命令,Redis保證以一種原子性的方式來執(zhí)行腳本:當(dāng)lua腳本在執(zhí)行的時(shí)候,不會有其他腳本和命令同時(shí)執(zhí)行,這種語義類似于 MULTI/EXEC。從別的客戶端的視角來看,一個
  • lua腳本要么不可見,要么已經(jīng)執(zhí)行完

一、 Redisson使用

Redisson是架設(shè)在Redis基礎(chǔ)上的一個Java駐內(nèi)存數(shù)據(jù)網(wǎng)格(In-Memory Data Grid)。

Redisson在基于NIO的Netty框架上,生產(chǎn)環(huán)境使用分布式鎖。

加入jar包的依賴

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>2.7.0</version>
</dependency>

配置Redisson

public class RedissonManager {
  private static Config config = new Config();
  //聲明redisso對象
  private static Redisson redisson = null;
  
   //實(shí)例化redisson
	static{
	  config.useClusterServers()
	  // 集群狀態(tài)掃描間隔時(shí)間,單位是毫秒
	 .setScanInterval(2000)
	  //cluster方式至少6個節(jié)點(diǎn)(3主3從,3主做sharding,3從用來保證主宕機(jī)后可以高可用)
	 .addNodeAddress("redis://127.0.0.1:6379" )
	 .addNodeAddress("redis://127.0.0.1:6380")
	 .addNodeAddress("redis://127.0.0.1:6381")
	 .addNodeAddress("redis://127.0.0.1:6382")
	 .addNodeAddress("redis://127.0.0.1:6383")
	 .addNodeAddress("redis://127.0.0.1:6384");
	 
	  //得到redisson對象
	  redisson = (Redisson) Redisson.create(config);
	}
	
	  //獲取redisson對象的方法
	  public static Redisson getRedisson(){
	    return redisson;
	 }
}

鎖的獲取和釋放

public class DistributedRedisLock {
  //從配置類中獲取redisson對象
  private static Redisson redisson = RedissonManager.getRedisson();
  private static final String LOCK_TITLE = "redisLock_";
  
  //加鎖
  public static boolean acquire(String lockName){
    //聲明key對象
    String key = LOCK_TITLE + lockName;
    //獲取鎖對象
    RLock mylock = redisson.getLock(key);
    //加鎖,并且設(shè)置鎖過期時(shí)間3秒,防止死鎖的產(chǎn)生  uuid+threadId
    mylock.lock(2,3,TimeUtil.SECOND);
    //加鎖成功
    return  true;
  }
  
  //鎖的釋放
  public static void release(String lockName){
    //必須是和加鎖時(shí)的同一個key
    String key = LOCK_TITLE + lockName;
    //獲取所對象
    RLock mylock = redisson.getLock(key);
    //釋放鎖(解鎖)
    mylock.unlock();
  
 

業(yè)務(wù)邏輯中使用分布式鎖

public String discount() throws IOException{
    String key = "lock001";
    //加鎖
    DistributedRedisLock.acquire(key);
    //執(zhí)行具體業(yè)務(wù)邏輯
    dosoming
    //釋放鎖
    DistributedRedisLock.release(key);
    //返回結(jié)果
    return soming;
 }

二、Redisson分布式鎖的實(shí)現(xiàn)原理

2.1 加鎖機(jī)制

如果該客戶端面對的是一個redis cluster集群,他首先會根據(jù)hash節(jié)點(diǎn)選擇一臺機(jī)器。

發(fā)送lua腳本到redis服務(wù)器上,腳本如下:

//exists',KEYS[1])==0 不存在,沒鎖
"if (redis.call('exists',KEYS[1])==0) then "+       --看有沒有鎖
  // 命令:hset,1:第一回
	"redis.call('hset',KEYS[1],ARGV[2],1) ; "+       --無鎖 加鎖 
	// 配置鎖的生命周期 
	"redis.call('pexpire',KEYS[1],ARGV[1]) ; "+      
	"return nil; end ;" +

//可重入操作,判斷是不是我加的鎖
"if (redis.call('hexists',KEYS[1],ARGV[2]) ==1 ) then "+  --我加的鎖
   //hincrby 在原來的鎖上加1
	"redis.call('hincrby',KEYS[1],ARGV[2],1) ; "+  --重入鎖
	"redis.call('pexpire',KEYS[1],ARGV[1]) ; "+  
	"return nil; end ;" +

//否則,鎖存在,返回鎖的有效期,決定下次執(zhí)行腳本時(shí)間
"return redis.call('pttl',KEYS[1]) ;"  --不能加鎖,返回鎖的時(shí)間

lua的作用:保證這段復(fù)雜業(yè)務(wù)邏輯執(zhí)行的原子性。

lua的解釋:

  • KEYS[1]) : 加鎖的key
  • ARGV[1] : key的生存時(shí)間,默認(rèn)為30秒
  • ARGV[2] : 加鎖的客戶端ID (UUID.randomUUID()) + “:” + threadId)

第一段if判斷語句,就是用“exists myLock”命令判斷一下,如果你要加鎖的那個鎖key不存在的話,你就進(jìn)行加鎖。

如何加鎖呢?很簡單,用下面的命令:

hset myLock

8743c9c0-0795-4907-87fd-6c719a6b4586:1 1

通過這個命令設(shè)置一個hash數(shù)據(jù)結(jié)構(gòu),這行命令執(zhí)行后,會出現(xiàn)一個類似下面的數(shù)據(jù)結(jié)構(gòu):

myLock :{“8743c9c0-0795-4907-87fd-6c719a6b4586:1”:1 }

上述就代表“8743c9c0-0795-4907-87fd-6c719a6b4586:1”這個客戶端對“myLock”這個鎖key完成了加鎖。

接著會執(zhí)行“pexpire myLock 30000”命令,設(shè)置myLock這個鎖key的生存時(shí)間是30秒。

鎖互斥機(jī)制

那么在這個時(shí)候,如果客戶端2來嘗試加鎖,執(zhí)行了同樣的一段lua腳本,會咋樣呢?

很簡單,第一個if判斷會執(zhí)行“exists myLock”,發(fā)現(xiàn)myLock這個鎖key已經(jīng)存在了。

接著第二個if判斷,判斷一下,myLock鎖key的hash數(shù)據(jù)結(jié)構(gòu)中,是否包含客戶端2的ID,但是明顯不是的,因?yàn)槟抢锇氖强蛻舳?的ID。

所以,客戶端2會獲取到pttl myLock返回的一個數(shù)字,這個數(shù)字代表了myLock這個鎖key的剩余生存時(shí)間。比如還剩15000毫秒的生存時(shí)間。

此時(shí)客戶端2會進(jìn)入一個while循環(huán),不停的嘗試加鎖。

自動延時(shí)機(jī)制

只要客戶端1一旦加鎖成功,就會啟動一個watch dog看門狗,他是一個后臺線程,會每隔10秒檢查一下,如果客戶端1還持有鎖key,那么就會不斷的延長鎖key的生存時(shí)間。

可重入鎖機(jī)制

第一個if判斷 肯定不成立,“exists myLock”會顯示鎖key已經(jīng)存在了。

第二個if判斷 會成立,因?yàn)閙yLock的hash數(shù)據(jù)結(jié)構(gòu)中包含的那個ID,就是客戶端1的那個ID,也就是“8743c9c0-0795-4907-87fd-6c719a6b4586:1”

此時(shí)就會執(zhí)行可重入加鎖的邏輯,他會用:incrby myLock 8743c9c0-0795-4907-87fd-6c71a6b4586:1 1

通過這個命令,對客戶端1的加鎖次數(shù),累加1。數(shù)據(jù)結(jié)構(gòu)會變成:myLock :{“8743c9c0-0795-4907-87fd-6c719a6b4586:1”:2 }

2.2 釋放鎖機(jī)制

執(zhí)行l(wèi)ua腳本如下:

# 如果key已經(jīng)不存在,說明已經(jīng)被解鎖,直接發(fā)布(publish)redis消息(無鎖,直接返回)
"if (redis.call('exists', KEYS[1]) == 0) then " +
            "redis.call('publish', KEYS[2], ARGV[1]); " +
            "return 1; " +
          "end;" +
# key和field不匹配,說明當(dāng)前客戶端線程沒有持有鎖,不能主動解鎖。 不是我加的鎖 不能解鎖 (有鎖不是我加的,返回)
          "if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then " +
            "return nil;" +
          "end; " +
# 將value減1 (有鎖是我加的,進(jìn)行hincrby -1 )
          "local counter = redis.call('hincrby', KEYS[1], ARGV[3],-1); " +
# 如果counter>0說明鎖在重入,不能刪除key
          "if (counter > 0) then " +
            "redis.call('pexpire', KEYS[1], ARGV[2]); " +
            "return 0; " +
# 刪除key并且publish 解鎖消息
					# 可重入鎖減完了,進(jìn)行del操作
          "else " + 
            "redis.call('del', KEYS[1]); " + #刪除鎖
            "redis.call('publish', KEYS[2], ARGV[1]); " +
            "return 1; "+
             "end; " +
             "return nil;",
  • – KEYS[1] :需要加鎖的key,這里需要是字符串類型。
  • – KEYS[2] :redis消息的ChannelName,一個分布式鎖對應(yīng)唯一的一個channelName: “redisson_lockchannel{” + getName() + “}”
  • – ARGV[1] :reids消息體,這里只需要一個字節(jié)的標(biāo)記就可以,主要標(biāo)記redis的key已經(jīng)解鎖,再結(jié)合 redis的Subscribe,能喚醒其他訂閱解鎖消息的客戶端線程申請鎖。
  • – ARGV[2] :鎖的超時(shí)時(shí)間,防止死鎖
  • – ARGV[3] :鎖的唯一標(biāo)識,也就是剛才介紹的 id(UUID.randomUUID()) + “:” + threadId

如果執(zhí)行l(wèi)ock.unlock(),就可以釋放分布式鎖,此時(shí)的業(yè)務(wù)邏輯也是非常簡單的。

其實(shí)說白了,就是每次都對myLock數(shù)據(jù)結(jié)構(gòu)中的那個加鎖次數(shù)減1。

如果發(fā)現(xiàn)加鎖次數(shù)是0了,說明這個客戶端已經(jīng)不再持有鎖了,此時(shí)就會用:

  • “del myLock”命令,從redis里刪除這個key。
  • 然后呢,另外的客戶端2就可以嘗試完成加鎖了。

分布式鎖特性

  • 互斥性
  • 任意時(shí)刻,只能有一個客戶端獲取鎖,不能同時(shí)有兩個客戶端獲取到鎖。
  • 同一性
  • 鎖只能被持有該鎖的客戶端刪除,不能由其它客戶端刪除。
  • 可重入性
  • 持有某個鎖的客戶端可繼續(xù)對該鎖加鎖,實(shí)現(xiàn)鎖的續(xù)租
  • 容錯性
  • 鎖失效后(超過生命周期)自動釋放鎖(key失效),其他客戶端可以繼續(xù)獲得該鎖,防止死鎖

分布式鎖的實(shí)際應(yīng)用

  • 數(shù)據(jù)并發(fā)競爭
  • 利用分布式鎖可以將處理串行化,前面已經(jīng)講過了。
  • 防止庫存超賣

訂單1下單前會先查看庫存,庫存為10,所以下單5本可以成功;

訂單2下單前會先查看庫存,庫存為10,所以下單8本可以成功;

訂單1和訂單2 同時(shí)操作,共下單13本,但庫存只有10本,顯然庫存不夠了,這種情況稱為庫存超賣。

可以采用分布式鎖解決這個問題。

訂單1和訂單2都從Redis中獲得分布式鎖(setnx),誰能獲得鎖誰進(jìn)行下單操作,這樣就把訂單系統(tǒng)下單的順序串行化了,就不會出現(xiàn)超賣的情況了。

偽碼如下:

//加鎖并設(shè)置有效期
if(redis.lock("RDL",200)){
  //判斷庫存
  if (orderNum<getCount()){
  //加鎖成功 ,可以下單
  order(5);
  //釋放鎖
  redis,unlock("RDL");
 }  
}

注意此種方法會降低處理效率,這樣不適合秒殺的場景,秒殺可以使用CAS和Redis隊(duì)列的方式。

總結(jié)

以上為個人經(jīng)驗(yàn),希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • 查看redis占用內(nèi)存的實(shí)現(xiàn)方法

    查看redis占用內(nèi)存的實(shí)現(xiàn)方法

    這篇文章主要介紹了查看redis占用內(nèi)存的實(shí)現(xiàn)方法,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • Redisson實(shí)現(xiàn)Redis分布式鎖的幾種方式

    Redisson實(shí)現(xiàn)Redis分布式鎖的幾種方式

    本文在講解如何使用Redisson實(shí)現(xiàn)Redis普通分布式鎖,以及Redlock算法分布式鎖的幾種方式的同時(shí),也附帶解答這些同學(xué)的一些疑問,感興趣的可以了解一下
    2021-08-08
  • Redis實(shí)現(xiàn)分布式鎖的實(shí)例講解

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

    在本篇文章里小編給大家整理了一篇關(guān)于Redis實(shí)現(xiàn)分布式鎖的實(shí)例講解內(nèi)容,有興趣的朋友們可以學(xué)習(xí)參考下。
    2021-12-12
  • redis?lua限流算法實(shí)現(xiàn)示例

    redis?lua限流算法實(shí)現(xiàn)示例

    這篇文章主要為大家介紹了redis?lua限流算法實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-07-07
  • Redis動態(tài)熱點(diǎn)數(shù)據(jù)緩存策略設(shè)計(jì)

    Redis動態(tài)熱點(diǎn)數(shù)據(jù)緩存策略設(shè)計(jì)

    本文主要介紹了Redis動態(tài)熱點(diǎn)數(shù)據(jù)緩存策略設(shè)計(jì),包括熱點(diǎn)數(shù)據(jù)識別、動態(tài)緩存、多級緩存、預(yù)加載機(jī)制、更新策略以及監(jiān)控告警等,具有一定的參考價(jià)值,感興趣的可以了解一下
    2025-01-01
  • Redis 數(shù)值范圍查詢(Numeric Range Queries)的實(shí)現(xiàn)

    Redis 數(shù)值范圍查詢(Numeric Range Queries)的實(shí)現(xiàn)

    本文主要介紹了Redis 數(shù)值范圍查詢(Numeric Range Queries)的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-11-11
  • Redis數(shù)據(jù)庫的安裝配置方法

    Redis數(shù)據(jù)庫的安裝配置方法

    redis 是一個高性能的key-value數(shù)據(jù)庫。 redis的出現(xiàn),很大程度補(bǔ)償了memcached這類keyvalue存儲的不足,在部 分場合可以對關(guān)系數(shù)據(jù)庫起到很好的補(bǔ)充作用。它提供了Python,Ruby,Erlang,PHP客戶端,使用很方便
    2014-06-06
  • Deepin UOS編譯安裝Redis的實(shí)現(xiàn)步驟

    Deepin UOS編譯安裝Redis的實(shí)現(xiàn)步驟

    本文主要介紹了Deepin UOS編譯安裝Redis的實(shí)現(xiàn)步驟,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-01-01
  • Redis中的數(shù)據(jù)過期策略詳解

    Redis中的數(shù)據(jù)過期策略詳解

    這篇文章主要介紹了Redis中的數(shù)據(jù)過期策略,文中通過示例代碼介紹的很詳細(xì),相信對大家的理解和學(xué)習(xí)具有一定的參考借鑒價(jià)值,有需要的朋友可以參考借鑒,下面來一起看看吧。
    2017-01-01
  • 使用redis-plus-plus庫連接redis的實(shí)現(xiàn)方法

    使用redis-plus-plus庫連接redis的實(shí)現(xiàn)方法

    本文主要介紹了使用redis-plus-plus庫連接redis的實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-02-02

最新評論

西乌珠穆沁旗| 洪洞县| 娄底市| 从江县| 昌乐县| 清丰县| 津市市| 新平| 南漳县| 兴业县| 连平县| 五原县| 东乡族自治县| 佳木斯市| 孙吴县| 谢通门县| 瓮安县| 洮南市| 赫章县| 夏邑县| 夏河县| 沙田区| 新昌县| 柘城县| 江油市| 绥棱县| 垫江县| 高阳县| 渝中区| 隆尧县| 营口市| 孟连| 革吉县| 庄浪县| 曲麻莱县| 长武县| 安龙县| 隆安县| 马鞍山市| 五指山市| 芜湖市|