亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

詳解redis分布式鎖(優(yōu)化redis分布式鎖的過程及Redisson使用)

 更新時間:2021年11月12日 11:37:01   作者:一個還沒入門的程序員  
在分布式的開發(fā)中,以電商庫存的更新功能進行講解,在實際的應(yīng)用中相同功能的消費者是有多個的,這篇文章主要介紹了redis分布式鎖詳解(優(yōu)化redis分布式鎖的過程及Redisson使用),需要的朋友可以參考下

1. redis在實際的應(yīng)用中

不僅可以用來緩存數(shù)據(jù),在分布式應(yīng)用開發(fā)中,經(jīng)常被用來當(dāng)作分布式鎖的使用,為什么要用到分布式鎖呢?

在分布式的開發(fā)中,以電商庫存的更新功能進行講解,在實際的應(yīng)用中相同功能的消費者是有多個的,假如多個消費者同一時刻要去消費一條數(shù)據(jù),假如業(yè)務(wù)邏輯處理邏輯是查詢出redis中的商品庫存,而如果第一個進來的消費的消費者獲取到庫存了,還沒進行減庫存操作,相對晚來的消費者就獲取了商品的庫存,這樣就導(dǎo)致數(shù)據(jù)會出錯,導(dǎo)致消費的數(shù)據(jù)變多了。

例如:消費者A和消費者B分別去消費生產(chǎn)者C1和生產(chǎn)者C2的數(shù)據(jù),而生產(chǎn)者都是使用同一個redis的數(shù)據(jù)庫的,如果生產(chǎn)者C1接收到消費者A的消息后,先進行查詢庫存,然后當(dāng)要進行減庫存的時候,因為生產(chǎn)者C2接收到消費者B的消息后,也去查詢庫存,而因為生產(chǎn)者C1還沒有進行庫存的更新,導(dǎo)致生產(chǎn)者C2獲取到的庫存數(shù)是臟數(shù)據(jù),而不是生產(chǎn)者C1更新后的數(shù)據(jù),導(dǎo)致業(yè)務(wù)出錯。

在這里插入圖片描述

如果不是分布式的應(yīng)用,可以使用synchronized進行防止庫存更新的問題的產(chǎn)生,但是synchronized只是基于JVM層面的,如果在不同的JVM中,就不能實現(xiàn)這樣的功能。

   @GetMapping("getInt0")
    public String test() {
        synchronized (this) {
            //獲取當(dāng)前商品的數(shù)量
            int productNum = Integer.parseInt(stringRedisTemplate.opsForValue().get("product"));
            //然后對商品進行出庫操作,即進行減1
            /*
             * a業(yè)務(wù)邏輯
             *
             * */
            if (productNum > 0) {
                stringRedisTemplate.opsForValue().set("product", String.valueOf(productNum - 1));
                int productNumNow = productNum - 1;
            } else {
                return "product=0";
            }
            int productNumNow = productNum - 1;
            return "success=" + productNumNow;
        }
    }

2.如何使用redis的功能進行實現(xiàn)分布式鎖

2.1 redis分布式鎖思想

如果對redis熟悉的話,我們能夠想到redis中具有setnx的命令,該命令的功能宇set功能類似,但是setnx的命令在進行存數(shù)據(jù)前,會檢查redis中是否已經(jīng)存在相同的key,如存在的話就返回false,反之則返回true,因此我們可以使用該命令的功能,設(shè)計一個分布式鎖。

2.1.1設(shè)計思想:

  • 在請求相同功能的接口時,使用redis的setnx命令,如果使用setnx命令后返回的是為true,說明此時沒有其他的調(diào)用這個接口,就相當(dāng)于獲取到鎖了,然后就可以繼續(xù)執(zhí)行接下來的業(yè)務(wù)邏輯了。當(dāng)執(zhí)行完業(yè)務(wù)邏輯后,在返回數(shù)據(jù)前,就把key刪除了,然后其他的請求就能獲取到鎖了。
  • 如果使用setnx命令,返回的是false,說明此時有其他的消費者正在調(diào)用這個接口,因此需要等待其他消費者順利消費完成后,才能獲取到分布式的鎖。

2.1.2 根據(jù)上面的設(shè)計思想進行代碼實現(xiàn)

代碼片段【1】

  @GetMapping("getInt1")
    public String fubushisuo(){
        //setIfAbsent的指令功能和redis命令中的setNx功能一樣,如果redis中已經(jīng)存在相同的key,則返回false
        String lockkey = "yigehaimeirumengdechengxuyuan";
        String lockvalue = "yigehaimeirumengdechengxuyuan";
        boolean opsForSet = stringRedisTemplate.opsForValue().setIfAbsent(lockkey,lockvalue);
        //如果能夠成功的設(shè)置lockkey,這說明當(dāng)前獲取到分布式鎖
        if (!opsForSet){
            return "false";
        }
        //獲取當(dāng)前商品的數(shù)量
        int productNum = Integer.parseInt(stringRedisTemplate.opsForValue().get("product"));
        //然后對商品進行出庫操作,即進行減1
        /*
        * a業(yè)務(wù)邏輯
        *
        * */
        if (productNum>0){
            stringRedisTemplate.opsForValue().set("product", String.valueOf(productNum - 1));
            int productNumNow = productNum - 1;
        }else {
            return "product=0";
        }
        //然后進行釋放鎖
        stringRedisTemplate.delete(lockkey);
        int productNumNow = productNum-1;
        return "success="+productNumNow;
    }


2.1.2.1反思代碼片段【1】

如果使用這種方式,會產(chǎn)生死鎖的方式:
死鎖發(fā)生的情況:
(1) 如果在a業(yè)務(wù)邏輯出現(xiàn)錯誤時,導(dǎo)致不能執(zhí)行delete()操作,使得其他的請求不能獲取到分布式鎖,業(yè)務(wù)lockkey一直存在于reids中,導(dǎo)致setnx操作一直失敗,所以不能獲取到分布式鎖
(2) 解決方法,使用對業(yè)務(wù)代碼進行try…catch操作,如果出現(xiàn)錯誤,那么使用finally對key進行刪除

優(yōu)化代碼【2】

     @GetMapping("getInt2")
    public String fubushisuo2(){
        //setIfAbsent的指令功能和redis命令中的setNx功能一樣,如果redis中已經(jīng)存在相同的key,則返回false
        String lockkey = "yigehaimeirumengdechengxuyuan";
        String lockvalue = "yigehaimeirumengdechengxuyuan";
        boolean opsForSet = stringRedisTemplate.opsForValue().setIfAbsent(lockkey,lockvalue);
        int productNumNow = 0;
        //如果能夠成功的設(shè)置lockkey,這說明當(dāng)前獲取到分布式鎖
        if (!opsForSet){
            return "false";
        }
        try {
            //獲取當(dāng)前商品的數(shù)量
            int productNum = Integer.parseInt(stringRedisTemplate.opsForValue().get("product"));
            //然后對商品進行出庫操作,即進行減1
            /*
             * b業(yè)務(wù)邏輯
             * */
            if (productNum>0){
                stringRedisTemplate.opsForValue().set("product", String.valueOf(productNum - 1));
                productNumNow = productNum-1;
            }else {
                return "product=0";
            }

        }catch (Exception e){
            System.out.println(e.getCause());
        }finally {
                //然后進行釋放鎖
            stringRedisTemplate.delete(lockkey);
        }

        return "success="+productNumNow;
    }
2.1.2.2反思代碼【2】

出現(xiàn)問題的情況:
如果這種情況也有會產(chǎn)生的情況,如果此時有多臺服務(wù)器都在運行該方法,
其中有一個方法獲取到了分布式鎖,而在運行下面的業(yè)務(wù)代碼時,此時該服務(wù)器突然宕機了,導(dǎo)致其他的不能獲取到分布式鎖,

解決方法:加上過期時間,但又服務(wù)宕機了,過了設(shè)置的時間后,redis會可以把key給刪除,這樣其他的的服務(wù)器就可以正常的進行上鎖了。

優(yōu)化代碼【3】

 @GetMapping("getInt3")
    public String fubushisuo3(){
        //setIfAbsent的指令功能和redis命令中的setNx功能一樣,如果redis中已經(jīng)存在相同的key,則返回false
        String lockkey = "yigehaimeirumengdechengxuyuan";
        String lockvalue = "yigehaimeirumengdechengxuyuan";
       //[01] boolean opsForSet = stringRedisTemplate.opsForValue().setIfAbsent(lockkey,lockvalue);
        //設(shè)置過期時間為10秒,但是如果使用該命令,沒有原子性,可能執(zhí)行expire前宕機了,而不是設(shè)置過期時間,
       //[02] stringRedisTemplate.expire(lockkey, Duration.ofSeconds(10));
        //使用setIfAbsent(lockkey,lockvalue,10,TimeUnit.SECONDS);代碼代替上面[01],[02]行代碼
        Boolean opsForSet = stringRedisTemplate.opsForValue().setIfAbsent(lockkey, lockvalue, 10, TimeUnit.SECONDS);
        int productNumNow = 0;
        //如果能夠成功的設(shè)置lockkey,這說明當(dāng)前獲取到分布式鎖
        if (!opsForSet){
            return "false";
        }
        try {
            //獲取當(dāng)前商品的數(shù)量
            int productNum = Integer.parseInt(stringRedisTemplate.opsForValue().get("product"));
            //然后對商品進行出庫操作,即進行減1
            /*
             * c業(yè)務(wù)邏輯
             * */
            if (productNum>0){
                stringRedisTemplate.opsForValue().set("product", String.valueOf(productNum - 1));
                productNumNow = productNum-1;
            }else {
                return "product=0";
            }

        }catch (Exception e){
            System.out.println(e.getCause());
        }finally {
        
        //然后進行釋放鎖
            stringRedisTemplate.delete(lockkey);
        }
        
        return "success="+productNumNow;
    }

2.1.2.3 反思優(yōu)化代碼【3】

出現(xiàn)問題的情況:
如果c業(yè)務(wù)邏輯持續(xù)超過了設(shè)置時間,導(dǎo)致redis中的lockkey過期了,
而其他的用戶此時訪問該方法時獲取到鎖了,而在此時,之前的的c業(yè)務(wù)邏輯也執(zhí)行完成了,但是他會執(zhí)行delete,把lcokkey刪除了。導(dǎo)致分布式鎖出錯。
例子:在12:01:55的時刻,有一個A來執(zhí)行該getInt3方法,并且成功獲取到鎖,但是A執(zhí)行了10秒后還不能完成業(yè)務(wù)邏輯,導(dǎo)致redis中的鎖過期了,而在11秒的時候有B來執(zhí)行g(shù)etint3方法,因為key被A刪除了,導(dǎo)致B能夠成功的獲取redis鎖,而在B獲取鎖后,A因為執(zhí)行完成了,然后把reids中的key給刪除了,但是我們注意的是,A刪除的鎖是B加上去的,而A的鎖是因為過期了,才被redis自己刪除了,因此這導(dǎo)致了C如果此時來時也能獲取redis分布式鎖

解決方法:使用UUID,產(chǎn)生一個隨機數(shù),當(dāng)要進行delete(刪除)redis中key時,判斷是不是之前自己設(shè)置的UUID

代碼優(yōu)化【4】

  @GetMapping("getInt4")
    public String fubushisuo4(){
        //setIfAbsent的指令功能和redis命令中的setNx功能一樣,如果redis中已經(jīng)存在相同的key,則返回false
        String lockkey = "yigehaimeirumengdechengxuyuan";
        //獲取UUID
        String lockvalue = UUID.randomUUID().toString();
        Boolean opsForSet = stringRedisTemplate.opsForValue().setIfAbsent(lockkey, lockvalue, 10, TimeUnit.SECONDS);
        int productNumNow = 0;
        //如果能夠成功的設(shè)置lockkey,這說明當(dāng)前獲取到分布式鎖
        if (!opsForSet){
            return "false";
        }
        try {
            //獲取當(dāng)前商品的數(shù)量
            int productNum = Integer.parseInt(stringRedisTemplate.opsForValue().get("product"));
            //然后對商品進行出庫操作,即進行減1
            /*
             * c業(yè)務(wù)邏輯
             * */

            if (productNum>0){
                stringRedisTemplate.opsForValue().set("product", String.valueOf(productNum - 1));
                productNumNow = productNum-1;
            }else {
                return "product=0";
            }

        }catch (Exception e){
            System.out.println(e.getCause());
        }finally {
        
        //進行釋放鎖
            if (lockvalue==stringRedisTemplate.opsForValue().get(lockkey)){
                stringRedisTemplate.delete(lockkey);
            }
        }
        return "success="+productNumNow;
    }
2.1.2.4 反思優(yōu)化代碼【4】

出現(xiàn)問題的情況:
此時該方法是比較完美的,一般并發(fā)不是超級大的情況下都可以進行使用,但是關(guān)于key的過期時間需要根據(jù)業(yè)務(wù)執(zhí)行的時間,進行設(shè)置,防止在業(yè)務(wù)還沒執(zhí)行完時,key就過期了.

解決方法:目前有很多redis的分布式鎖的框架,其中redisson用的是比較多的

2.2 使用redisson進行實現(xiàn)分布式鎖

先添加redisson的maven依賴

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

redisson的bean配置

@Configuration
public class RedissonConfigure {
    @Bean
    public Redisson redisson(){
        Config config = new Config();
        config.useSingleServer().setAddress("redis://27.196.106.42:6380").setDatabase(0);
        return (Redisson) Redisson.create(config);
    }
}

實現(xiàn)分布式鎖代碼如下

 @GetMapping("getInt5")
    public String fubushisuo5(){
        //setIfAbsent的指令功能和redis命令中的setNx功能一樣,如果redis中已經(jīng)存在相同的key,則返回false
        String lockkey = "yigehaimeirumengdechengxuyuan";
        //獲取UUID
        RLock lock = redisson.getLock(lockkey);
        lock.lock();
        int productNumNow = 0;
        //如果能夠成功的設(shè)置lockkey,這說明當(dāng)前獲取到分布式鎖

        try {
            //獲取當(dāng)前商品的數(shù)量
            int productNum = Integer.parseInt(stringRedisTemplate.opsForValue().get("product"));
            //然后對商品進行出庫操作,即進行減1
            /*
             * c業(yè)務(wù)邏輯
             * */
            if (productNum>0){
            stringRedisTemplate.opsForValue().set("product", String.valueOf(productNum - 1));
            productNumNow = productNum-1;
            }else {
                return "product=0";
            }
        }catch (Exception e){
            System.out.println(e.getCause());
        }finally {
           lock.unlock();
            }

        //然后進行釋放鎖
        return "success="+productNumNow;
    }

從面就能看到,redisson實現(xiàn)分布式鎖是非常簡單的,只要簡單的幾條命令就能實現(xiàn)分布式鎖的功能的。
redisson實現(xiàn)分布式鎖的只要原理如下:
redisson使用了Lua腳本語言使得命令既有原子性,redisson獲取鎖時,會給key設(shè)置30秒的過期是按,同時redisson會記錄當(dāng)前請求的線程編號,然后定時的去檢查該線程的狀態(tài),如果還處于執(zhí)行狀態(tài)的話,而且key差不多要超期過時時,redisson會修改key的過期時間,一般增加10秒。這樣就可以動態(tài)的設(shè)置key的過期時間了,彌補了優(yōu)化代碼【4】的片段

到此這篇關(guān)于redis分布式鎖詳解(優(yōu)化redis分布式鎖的過程及Redisson使用)的文章就介紹到這了,更多相關(guān)redis分布式鎖內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Redis主從復(fù)制分步講解使用

    Redis主從復(fù)制分步講解使用

    Redis因為其高性能和易用性在我們后端的服務(wù)中發(fā)揮了巨大的作用,并且很多重要功能的實現(xiàn)都會依賴redis,本篇我們來了解Redis高可用主從復(fù)制,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧
    2022-09-09
  • redis實現(xiàn)session共享的方法

    redis實現(xiàn)session共享的方法

    本文主要介紹了redis實現(xiàn)session共享的方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-04-04
  • gem install redis報錯的解決方案

    gem install redis報錯的解決方案

    今天小編就為大家分享一篇關(guān)于gem install redis報錯的解決方案,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-01-01
  • Redis實現(xiàn)排名功能的示例代碼

    Redis實現(xiàn)排名功能的示例代碼

    本文主要介紹了Redis實現(xiàn)排名功能的示例代碼,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • 解決redis批量刪除key值的問題

    解決redis批量刪除key值的問題

    在開發(fā)過程中,會遇到要批量刪除某種規(guī)則的key值,但是通常情況下沒有批量刪除某一個類的命令,遇到這種情況該如何處理呢?下面小編給大家?guī)砹藃edis批量刪除key值的問題,感興趣的朋友一起看看吧
    2022-03-03
  • Redis安裝及基本數(shù)據(jù)類型

    Redis安裝及基本數(shù)據(jù)類型

    這篇文章主要介紹了Redis安裝及基本數(shù)據(jù)類型,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-05-05
  • Redis分布式鎖的7種實現(xiàn)

    Redis分布式鎖的7種實現(xiàn)

    這篇文章主要介紹了Redis分布式鎖的實現(xiàn)
    2022-04-04
  • Redis+Lua腳本實現(xiàn)計數(shù)器接口防刷功能(升級版)

    Redis+Lua腳本實現(xiàn)計數(shù)器接口防刷功能(升級版)

    這篇文章主要介紹了Redis+Lua腳本實現(xiàn)計數(shù)器接口防刷功能,使用腳本使得set命令和expire命令一同達到Redis被執(zhí)行且不會被干擾,在很大程度上保證了原子操作,對Redis實現(xiàn)計數(shù)器接口防刷功能感興趣的朋友一起看看吧
    2022-02-02
  • 使用百度地圖api通過redis實現(xiàn)地標(biāo)存儲及范圍坐標(biāo)點查詢功能

    使用百度地圖api通過redis實現(xiàn)地標(biāo)存儲及范圍坐標(biāo)點查詢功能

    這篇文章主要介紹了使用百度地圖api通過redis實現(xiàn)地標(biāo)存儲及范圍坐標(biāo)點查詢功能,本文通過圖文實例代碼相結(jié)合給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2021-08-08
  • Redis實現(xiàn)用戶關(guān)注的項目實踐

    Redis實現(xiàn)用戶關(guān)注的項目實踐

    本文主要介紹了Redis實現(xiàn)用戶關(guān)注的項目實踐,通過使用Redis的set數(shù)據(jù)結(jié)構(gòu)來存儲關(guān)注對象,方便高效地進行添加和取消關(guān)注操作,具有一定的參考價值,感興趣的可以了解一下
    2024-02-02

最新評論