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

Redis分布式鎖解決超賣問(wèn)題

 更新時(shí)間:2023年12月11日 10:51:03   作者:Eliauk-_-  
超賣問(wèn)題是典型的多線程安全問(wèn)題,本文就來(lái)介紹一下Redis分布式鎖解決超賣問(wèn)題,具有一定的參考價(jià)值,感興趣的可以了解一下

一、使用redisTemplate中的setIfAbsent方法。

      // setIfAbsent就是對(duì)應(yīng)redis的setnx
        Boolean setIfAbsent = redisTemplate.opsForValue().setIfAbsent(lockKey, lockKey, 10, TimeUnit.SECONDS);
        if (Boolean.TRUE.equals(setIfAbsent)) {
            LOG.info("恭喜,搶到鎖了!lockKey:{}", lockKey);
        } else {
             LOG.info("很遺憾,沒(méi)搶到鎖!lockKey:{}", lockKey);
        }

缺點(diǎn):

  • 性能不高,可能會(huì)導(dǎo)致少買。
  • 假如業(yè)務(wù)執(zhí)行時(shí)間長(zhǎng),設(shè)置的鎖的過(guò)期時(shí)間短的話,可能出現(xiàn)超賣問(wèn)題。

二、使用Redisson解決(看門(mén)狗方式)

2.1、實(shí)現(xiàn)原理

redisson在獲取鎖之后,會(huì)開(kāi)啟一個(gè)守護(hù)線程(看門(mén)狗線程),當(dāng)鎖即將過(guò)期還沒(méi)有釋放時(shí),不斷的延長(zhǎng)鎖key的生存時(shí)間

2.2、SpringBoot集成Redisson

2.2.1、添加pom.xml依賴

            <!--至少3.18.0版本,才支持spring boot 3-->
            <!--升級(jí)到3.20.0,否則打包生產(chǎn)會(huì)報(bào)錯(cuò):Could not initialize class org.redisson.spring.data.connection.RedissonConnection-->
            <dependency>
                <groupId>org.redisson</groupId>
                <artifactId>redisson-spring-boot-starter</artifactId>
                <version>3.21.0</version>
            </dependency>

2.2.2、注入RedissonClient對(duì)象

@Autowired
private RedissonClient redissonClient;

2.2.3、使用Redisson

RLock lock = null;
   try {
            // 使用redisson,自帶看門(mén)狗
             lock = redissonClient.getLock(lockKey);
             /**
               waitTime – the maximum time to acquire the lock 等待獲取鎖時(shí)間(最大嘗試獲得鎖的時(shí)間),超時(shí)返回false
               leaseTime – lease time 鎖時(shí)長(zhǎng),即n秒后自動(dòng)釋放鎖
               time unit – time unit 時(shí)間單位
              */
             // boolean tryLock = lock.tryLock(30, 10, TimeUnit.SECONDS); // 不帶看門(mén)狗
             boolean tryLock = lock.tryLock(0, TimeUnit.SECONDS); // 帶看門(mén)狗
             if (tryLock) {
                 LOG.info("恭喜,搶到鎖了!");
             } else {
                 LOG.info("很遺憾,沒(méi)搶到鎖");
             }
   } catch (InterruptedException e) {
             LOG.error("發(fā)生異常", e);
        } finally {
             LOG.info("釋放鎖!");
            //當(dāng)lock不等于null并且lock是當(dāng)前線程的時(shí)候去釋放鎖
             if (null != lock && lock.isHeldByCurrentThread()) {
                 lock.unlock();
             }
        }

缺點(diǎn):

  • 看門(mén)狗啟動(dòng)后,對(duì)整體性能也會(huì)有一定影響
  • 當(dāng)redis宕機(jī)后,獲取不到鎖,業(yè)務(wù)中斷。
  • 正常情況下,如果加鎖成功了,那么master節(jié)點(diǎn)會(huì)異步復(fù)制給對(duì)應(yīng)的slave節(jié)點(diǎn)。但是如果在這個(gè)過(guò)程中發(fā)生master節(jié)點(diǎn)宕機(jī),主備切換,slave節(jié)點(diǎn)從變?yōu)榱?master節(jié)點(diǎn),而鎖還沒(méi)從舊master節(jié)點(diǎn)同步過(guò)來(lái),這就發(fā)生了鎖丟失,會(huì)導(dǎo)致多個(gè)客戶端可以同時(shí)持有同一把鎖的問(wèn)題

三、Redis紅鎖

2.1、Redis紅鎖名稱來(lái)源

Redis 紅鎖的名稱來(lái)源于 Redis 的logo,Redis 的 logo 是一個(gè)紅色熱氣球,而紅色的熱氣球上有一把鎖的圖案,因此這種分布式鎖解決方案也被稱為"Redlock",中文翻譯為"紅鎖"。

2.2、原理

現(xiàn)在假設(shè)有5個(gè)Redis主節(jié)點(diǎn)(大于3的奇數(shù)個(gè)),這樣基本保證他們不會(huì)同時(shí)都宕掉,獲取鎖和釋放鎖的過(guò)程中,客戶端會(huì)執(zhí)行以下操作:

  • 獲取當(dāng)前Unix時(shí)間,以毫秒為單位,并設(shè)置超時(shí)時(shí)間過(guò)期時(shí)間(過(guò)期時(shí)間要大于正常業(yè)務(wù)執(zhí)行的時(shí)間 + 獲取所有redis服務(wù)消耗時(shí)間 + 時(shí)鐘漂移)
  • 依次嘗試從5個(gè)實(shí)例,使用相同的key和具有唯一性的value獲取鎖,當(dāng)向Redis請(qǐng)求獲取鎖時(shí),客戶端應(yīng)該設(shè)置一個(gè)網(wǎng)絡(luò)連接和響應(yīng)超時(shí)時(shí)間,這個(gè)超時(shí)時(shí)間應(yīng)該小于鎖的失效時(shí)間TTL,這樣可以避免客戶端死等。比如:TTL為5s,設(shè)置獲取鎖最多用1s,所以如果一秒內(nèi)無(wú)法獲取鎖,就放棄獲取這個(gè)鎖,從而嘗試獲取下個(gè)鎖
  • 客戶端 獲取所有能獲取的鎖后的時(shí)間 減去 第(1)步的時(shí)間,就得到鎖的獲取時(shí)間。鎖的獲取時(shí)間要小于鎖失效時(shí)間TTL,并且至少?gòu)陌霐?shù)以上的Redis節(jié)點(diǎn)取到鎖,才算獲取成功鎖
  • 如果成功獲得鎖,key的真正有效時(shí)間 = TTL - 鎖的獲取時(shí)間 - 時(shí)鐘漂移。比如:TTL 是5s,獲取所有鎖用了2s,則真正鎖有效時(shí)間為3s
  • 如果因?yàn)槟承┰?,獲取鎖失?。](méi)有在半數(shù)以上實(shí)例取到鎖或者取鎖時(shí)間已經(jīng)超過(guò)了有效時(shí)間),客戶端應(yīng)該在所有的Redis實(shí)例上進(jìn)行解鎖,無(wú)論Redis實(shí)例是否加鎖成功,因?yàn)榭赡芊?wù)端響應(yīng)消息丟失了但是實(shí)際成功了。

2.3、代碼實(shí)現(xiàn)

2.3.1、注冊(cè)紅鎖的RedissonClient

@Component
public class RedisConfig {
	@Bean(name = "redissonClient1")
    @Primary
    public RedissonClient redissonRed1(){
        Config config = new Config();
        config.useSingleServer().setAddress("127.0.0.1:6379").setDatabase(0);
        return Redisson.create(config);
    }
    @Bean(name = "redissonClient2")
    public RedissonClient redissonRed2(){
        Config config = new Config();
        config.useSingleServer().setAddress("127.0.0.1:6380").setDatabase(0);
        return Redisson.create(config);
    }
    @Bean(name = "redissonClient3")
    public RedissonClient redissonRed3(){
        Config config = new Config();
        config.useSingleServer().setAddress("127.0.0.1:6381").setDatabase(0);
        return Redisson.create(config);
    }
    @Bean(name = "redissonClient4")
    public RedissonClient redissonRed4(){
        Config config = new Config();
        config.useSingleServer().setAddress("127.0.0.1:6382").setDatabase(0);
        return Redisson.create(config);
    }
    @Bean(name = "redissonClient5")
    public RedissonClient redissonRed5(){
        Config config = new Config();
        config.useSingleServer().setAddress("127.0.0.1:6383").setDatabase(0);
        return Redisson.create(config);
    }
  }

2.3.2、注入Redis紅鎖對(duì)象

    // 紅鎖
    @Autowired
    @Qualifier("redissonClient1")
    private RedissonClient redissonClient1;
    @Autowired
    @Qualifier("redissonClient2")
    private RedissonClient redissonClient2;
    @Autowired
    @Qualifier("redissonClient3")
    private RedissonClient redissonClient3;
    @Autowired
    @Qualifier("redissonClient4")
    private RedissonClient redissonClient4;
    @Autowired
    @Qualifier("redissonClient5")
    private RedissonClient redissonClient5;

2.3.3、使用Redis紅鎖

        /*
           假設(shè)有五臺(tái)redis機(jī)器, A B C D E
           線程1: A B C D E(獲取到鎖)
           線程2: C D E(獲取到鎖)
           線程3: C(未獲取到鎖)
        */
            RLock lock1 = null;
            RLock lock2 = null;
            RLock lock3 = null;
            RLock lock4 = null;
            RLock lock5 = null;
        try {
            lock1 = redissonClient1.getLock(lockKey);
            lock2 = redissonClient2.getLock(lockKey);
            lock3 = redissonClient3.getLock(lockKey);
            lock4 = redissonClient4.getLock(lockKey);
            lock5 = redissonClient5.getLock(lockKey);
            // 紅鎖的寫(xiě)法
            RedissonRedLock redissonRedLock = new RedissonRedLock(lock1, lock2, lock3, lock4, lock5);
            boolean tryLock = redissonRedLock.tryLock(0, TimeUnit.SECONDS);
            if (tryLock) {
                LOG.info("恭喜,搶到鎖了!");
            } else {
                LOG.info("很遺憾,沒(méi)搶到鎖");
            }
        } catch (InterruptedException e) {
            LOG.error("發(fā)生異常", e);
        } finally {
            LOG.info("釋放鎖!");
            //當(dāng)lock不等于null并且lock是當(dāng)前線程的時(shí)候去釋放鎖
            if (null != lock && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }

注意:

  • 按照順序獲取鎖、不然會(huì)出現(xiàn)每個(gè)線程都拿不到鎖的情況。
  • 當(dāng)redis宕機(jī)后,切換主備或者重啟時(shí)間需大于鎖的時(shí)間,不然會(huì)有線程同時(shí)獲取到鎖(比如線程1獲取到a,b,c,獲得了鎖后c宕機(jī)重啟了,如果數(shù)據(jù)沒(méi)有備份,c中沒(méi)有key,線程2有可能獲取到了c,d,e,也獲取到了鎖)。
  • 盡可能的獲取到更多redis實(shí)例的鎖。
  • 獲取鎖的時(shí)間要注意,需要設(shè)置超時(shí)時(shí)間,如果超時(shí)時(shí)間內(nèi)拿不到鎖,結(jié)束線程

到此這篇關(guān)于Redis分布式鎖解決超賣問(wèn)題的文章就介紹到這了,更多相關(guān)Redis分布式鎖超賣內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家! 

相關(guān)文章

  • 使用Redis的List實(shí)現(xiàn)緩存分頁(yè)信息

    使用Redis的List實(shí)現(xiàn)緩存分頁(yè)信息

    Redis List 是基于雙向鏈表實(shí)現(xiàn)的有序集合(按插入順序排序),借助 lrange 命令可快速實(shí)現(xiàn)分頁(yè)查詢,相比傳統(tǒng)數(shù)據(jù)庫(kù)(如 MySQL)的 limit offset 分頁(yè),Redis List 分頁(yè)在熱點(diǎn)數(shù)據(jù)、小范圍分頁(yè)場(chǎng)景下性能更穩(wěn)定,本文給大家介紹了如何使用Redis的List實(shí)現(xiàn)緩存分頁(yè)信息
    2026-02-02
  • Redis實(shí)現(xiàn)持久化的方式匯總

    Redis實(shí)現(xiàn)持久化的方式匯總

    Redis是一種高級(jí)key-value數(shù)據(jù)庫(kù)。它跟memcached類似,不過(guò)數(shù)據(jù)可以持久化,而且支持的數(shù)據(jù)類型很豐富。今天我們就來(lái)看看如何實(shí)現(xiàn)Redis持久化,需要的朋友可以參考下
    2022-10-10
  • 詳解redis緩存與數(shù)據(jù)庫(kù)一致性問(wèn)題解決

    詳解redis緩存與數(shù)據(jù)庫(kù)一致性問(wèn)題解決

    這篇文章主要介紹了詳解redis緩存與數(shù)據(jù)庫(kù)一致性問(wèn)題解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • redis中opsForList().range()的使用方法詳解

    redis中opsForList().range()的使用方法詳解

    這篇文章主要給大家介紹了關(guān)于redis中opsForList().range()的使用方法,文中通過(guò)實(shí)例代碼以及圖文介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用redis具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2023-03-03
  • Redis?的內(nèi)存淘汰策略和過(guò)期刪除策略的區(qū)別

    Redis?的內(nèi)存淘汰策略和過(guò)期刪除策略的區(qū)別

    這篇文章主要介紹了Redis?的內(nèi)存淘汰策略和過(guò)期刪除策略的區(qū)別,Redis?是可以對(duì)?key?設(shè)置過(guò)期時(shí)間的,因此需要有相應(yīng)的機(jī)制將已過(guò)期的鍵值對(duì)刪除,而做這個(gè)工作的就是過(guò)期鍵值刪除策略
    2022-07-07
  • redis搭建哨兵集群的實(shí)現(xiàn)步驟

    redis搭建哨兵集群的實(shí)現(xiàn)步驟

    本文主要介紹了redis搭建哨兵集群的實(shí)現(xiàn)步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-05-05
  • Redis哨兵改集群的方法實(shí)現(xiàn)

    Redis哨兵改集群的方法實(shí)現(xiàn)

    Redis作為一個(gè)開(kāi)源的鍵值存儲(chǔ)系統(tǒng),廣泛應(yīng)用于各種場(chǎng)景,如緩存和消息隊(duì)列,為了提高可用性和擴(kuò)展性,可以將Redis哨兵架構(gòu)改為集群架構(gòu),本文就來(lái)介紹一下,感興趣的可以了解一下
    2024-09-09
  • Linux下Redis集群搭建全過(guò)程(主從+哨兵)

    Linux下Redis集群搭建全過(guò)程(主從+哨兵)

    這篇文章主要介紹了Linux下Redis集群搭建全過(guò)程(主從+哨兵),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • 淺談Redis?中的過(guò)期刪除策略和內(nèi)存淘汰機(jī)制

    淺談Redis?中的過(guò)期刪除策略和內(nèi)存淘汰機(jī)制

    本文主要介紹了Redis?中的過(guò)期刪除策略和內(nèi)存淘汰機(jī)制,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-04-04
  • 基于redis實(shí)現(xiàn)世界杯排行榜功能項(xiàng)目實(shí)戰(zhàn)

    基于redis實(shí)現(xiàn)世界杯排行榜功能項(xiàng)目實(shí)戰(zhàn)

    前段時(shí)間,做了一個(gè)世界杯競(jìng)猜積分排行榜。對(duì)世界杯64場(chǎng)球賽勝負(fù)平進(jìn)行猜測(cè),猜對(duì)+1分,錯(cuò)誤+0分,一人一場(chǎng)只能猜一次。下面通過(guò)本文給大家分享基于redis實(shí)現(xiàn)世界杯排行榜功能項(xiàng)目實(shí)戰(zhàn),感興趣的朋友一起看看吧
    2018-10-10

最新評(píng)論

明光市| 乃东县| 泰来县| 宜州市| 涿州市| 南丰县| 内江市| 天全县| 沐川县| 桂林市| 保康县| 吉安县| 马山县| 利津县| 遂昌县| 溧水县| 手游| 姜堰市| 吴江市| 裕民县| 麻江县| 剑川县| 成都市| 从化市| 和林格尔县| 阿拉善左旗| 三江| 柞水县| 丹寨县| 义乌市| 九寨沟县| 新野县| 札达县| 英山县| 乐东| 泗洪县| 乐至县| 石河子市| 界首市| 廉江市| 中卫市|