1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        并發(fā)減庫(kù)存,怎么保證不超賣?

        共 2355字,需瀏覽 5分鐘

         ·

        2021-01-22 10:53

        Java技術(shù)棧

        www.javastack.cn

        關(guān)注閱讀更多優(yōu)質(zhì)文章



        作者:廢物大師兄
        來(lái)源:www.cnblogs.com/cjsblog/p/9135118.html

        秒殺的場(chǎng)景有很多,比如:搶購(gòu)、搶票、搶紅包等等??傊?,就是在極短時(shí)間內(nèi)有大量的請(qǐng)求。

        我們都知道,這種系統(tǒng)設(shè)計(jì)的大方向就是限流,即通過(guò)層層過(guò)濾,最終只讓相對(duì)較少的請(qǐng)求進(jìn)入到核心業(yè)務(wù)處理層。

        這里不談秒殺設(shè)計(jì),不談使用隊(duì)列等使請(qǐng)求串行化,就談下怎么用鎖來(lái)保證數(shù)據(jù)正確,就是已經(jīng)到減庫(kù)存那一步了,在這一步中如果保證不超賣。

        用隊(duì)列的話,可以是Java自動(dòng)的隊(duì)列,也可以用Redis的LPUSH RPOP

        重點(diǎn)是扣減庫(kù)存

        我理解,主要的方式是加鎖。

        加鎖有兩個(gè)層面:一個(gè)是程序?qū)用?,另一個(gè)是數(shù)據(jù)庫(kù)層面。

        分布式鎖

        這種場(chǎng)景下應(yīng)該很少有人用Java自帶的鎖(比如:synchronized、Lock)吧,因?yàn)樗鼈冎辉谕粋€(gè)JVM內(nèi)有效,如果你的應(yīng)用部署了多臺(tái)的話,應(yīng)該用分布式鎖。

        關(guān)于Redis分布式鎖,可以閱讀這篇文章:Spring Boot Redis 實(shí)現(xiàn)分布式鎖

        其實(shí),這里加分布式鎖就是將多線程請(qǐng)求轉(zhuǎn)成單線程請(qǐng)求,因?yàn)槊看沃挥幸粋€(gè)線程獲得鎖并執(zhí)行,其余都被阻塞了。

        這里有一點(diǎn)需要注意,就是當(dāng)你應(yīng)用了事務(wù)的話可能會(huì)存在問(wèn)題,請(qǐng)看下面的代碼

        可能有人會(huì)這樣寫(xiě),第一眼看起來(lái)挺好的,沒(méi)問(wèn)題啊,但仔細(xì)實(shí)踐證明是由問(wèn)題的。

        我們知道,mysql默認(rèn)的事務(wù)隔離級(jí)別是REPEATABLE-READ

        關(guān)于事務(wù)隔離級(jí)別這塊兒,可以參考《事務(wù)隔離級(jí)別和傳播機(jī)制》這篇文章。

        在這種隔離級(jí)別下,同一個(gè)事務(wù)中多次讀取,返回的數(shù)據(jù)是一樣的

        同時(shí),Spring聲明式事務(wù)默認(rèn)的傳播特性REQUIRED

        Spring聲明式事務(wù)是Spring AOP最好的例子,Spring是通過(guò)AOP代理的方式來(lái)實(shí)現(xiàn)事務(wù)的,也就是說(shuō)在調(diào)用reduceStock()方法的之前就已經(jīng)開(kāi)啟了事務(wù)。另外,關(guān)注公眾號(hào)Java技術(shù)棧,在后臺(tái)回復(fù):面試,可以獲取我整理的 Spring 系列面試題和答案,非常齊全。

        那么,在并發(fā)情況下可能會(huì)存在這樣的情況,假設(shè)線程T1和T2都執(zhí)行到這里,于是它們都開(kāi)啟了事務(wù)S1和S2,T1先執(zhí)行,T2后執(zhí)行,

        由于T2執(zhí)行的時(shí)候事務(wù)已經(jīng)創(chuàng)建了,根據(jù)隔離級(jí)別,這個(gè)時(shí)候事務(wù)S2讀取不到S1已提交的數(shù)據(jù),于是就會(huì)出現(xiàn)T1和T2讀取到的值是一樣的,即T2讀取的是T1更新前的庫(kù)存數(shù)據(jù)。

        關(guān)于這一點(diǎn),大家可以自己寫(xiě)個(gè)代碼測(cè)試一下,下面是一段參考:

        鑒于這種情況呢,可以將庫(kù)存放到Redis中,我們直接讀寫(xiě)Redis,這樣可以避免受數(shù)據(jù)庫(kù)事務(wù)的影響,當(dāng)然這也會(huì)帶來(lái)新的問(wèn)題,不再討論。另外,關(guān)注公眾號(hào)Java技術(shù)棧,在后臺(tái)回復(fù):面試,可以獲取我整理的 Redis 系列面試題和答案,非常齊全。

        數(shù)據(jù)庫(kù)樂(lè)觀鎖

        CAS(compare and swap)比較并交換

        在Java中,一個(gè)線程想修改某個(gè)變量的值,那么第一步是將變量的值從主內(nèi)存中讀取到自己工作內(nèi)存中,然后修改,最后寫(xiě)回主內(nèi)存。

        這個(gè)過(guò)程可以歸結(jié)為:讀取——修改——寫(xiě)入,在寫(xiě)回內(nèi)存的時(shí)候可能當(dāng)前內(nèi)存中那個(gè)值已經(jīng)發(fā)生了變化,這個(gè)時(shí)候如果繼續(xù)寫(xiě)則會(huì)覆蓋別人的數(shù)據(jù),只有當(dāng)內(nèi)存中的那個(gè)值和它修改之前讀到的那個(gè)值一樣,才可以寫(xiě)入。

        這個(gè)跟數(shù)據(jù)庫(kù)是一樣的。

        Java中通過(guò)Unsafe中compareAndSwapObject這樣的方法類實(shí)現(xiàn)的,它直接調(diào)用CPU指令。

        數(shù)據(jù)庫(kù)中也有CAS,樂(lè)觀鎖就是一種CAS。

        經(jīng)典的樂(lè)觀鎖實(shí)現(xiàn):

        數(shù)據(jù)增加一個(gè)版本標(biāo)識(shí),一般是通過(guò)為數(shù)據(jù)庫(kù)表增加一個(gè)數(shù)字類型的 “version” 字段來(lái)實(shí)現(xiàn)。當(dāng)讀取數(shù)據(jù)時(shí),將version字段的值一同讀出,數(shù)據(jù)每更新一次,對(duì)此version值加一。

        當(dāng)我們提交更新的時(shí)候,判斷數(shù)據(jù)庫(kù)表對(duì)應(yīng)記錄的當(dāng)前版本信息與第一次取出來(lái)的version值進(jìn)行比對(duì),如果數(shù)據(jù)庫(kù)表當(dāng)前版本號(hào)與第一次取出來(lái)的version值相等,則予以更新,否則認(rèn)為是過(guò)期數(shù)據(jù)。

        更新的時(shí)候帶上版本號(hào),只有當(dāng)前版本號(hào)與更新之前查詢時(shí)的版本一致,才會(huì)更新

        ABA問(wèn)題

        這里順便多提一句,CAS中的ABA問(wèn)題

        假設(shè),原先的值是A,線程-1讀取到的值是A,想把它改成D,但是在此期間,有可能其他線程已經(jīng)多次修改過(guò)這個(gè)值,只不過(guò)最后當(dāng)線程-1準(zhǔn)備將A改成D的時(shí)候,它發(fā)現(xiàn)恰好還是A,以為沒(méi)有人改過(guò),其實(shí)這時(shí)候的A已經(jīng)不是原來(lái)的A了。

        也就是說(shuō),盡管修改之前做了比較,當(dāng)然,仍然會(huì)出現(xiàn)如下情況:

        產(chǎn)生原因

        ABA問(wèn)題導(dǎo)致的原因,是CAS過(guò)程中只簡(jiǎn)單進(jìn)行了“值”的校驗(yàn),有些情況下,“值”雖然相同,卻已經(jīng)不是原來(lái)的數(shù)據(jù)了。

        優(yōu)化方向

        CAS不能只比對(duì)“值”,還必須確保的是原來(lái)的數(shù)據(jù),才能修改成功。

        常見(jiàn)實(shí)踐

        “版本號(hào)”的比對(duì),一個(gè)數(shù)據(jù)一個(gè)版本,版本變化,即使值相同,也不應(yīng)該修改成功。

        不僅要關(guān)注值,還要關(guān)注是不是原來(lái)的對(duì)象

        基于“值”的CAS樂(lè)觀鎖,可能導(dǎo)致ABA問(wèn)題。CAS樂(lè)觀鎖,必須保證修改時(shí)的“此數(shù)據(jù)”就是“彼數(shù)據(jù)”,應(yīng)該由“值”比對(duì),優(yōu)化為“版本號(hào)”比對(duì)。

        參考:

        https://www.sohu.com/a/150900817_178889
        https://blog.csdn.net/zhjunjun93/article/details/78560700

        https://blog.csdn.net/rexct392358928/article/details/52230737






        關(guān)注Java技術(shù)??锤喔韶?/strong>



        戳原文,獲取精選面試題!
        瀏覽 34
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

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

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

          <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            天天操天天操天天干 | 色色V | 午夜操逼剧场 | 国产精品成人小视频 | 啊啊啊太深了 | 波多野结衣久久 | 人与野鲁毛片在线视频 | 国内自拍视频在线播放 | 日少妇的b| 猛男 大 粗 猛 爽h男人味69XX |