<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

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

          共 2355字,需瀏覽 5分鐘

           ·

          2021-01-22 10:53

          Java技術(shù)棧

          www.javastack.cn

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



          作者:廢物大師兄
          來源:www.cnblogs.com/cjsblog/p/9135118.html

          秒殺的場景有很多,比如:搶購、搶票、搶紅包等等。總之,就是在極短時間內(nèi)有大量的請求。

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

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

          用隊列的話,可以是Java自動的隊列,也可以用Redis的LPUSH RPOP

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

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

          加鎖有兩個層面:一個是程序?qū)用妫硪粋€是數(shù)據(jù)庫層面。

          分布式鎖

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

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

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

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

          可能有人會這樣寫,第一眼看起來挺好的,沒問題啊,但仔細(xì)實(shí)踐證明是由問題的。

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

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

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

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

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

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

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

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

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

          數(shù)據(jù)庫樂觀鎖

          CAS(compare and swap)比較并交換

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

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

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

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

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

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

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

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

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

          ABA問題

          這里順便多提一句,CAS中的ABA問題

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

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

          產(chǎn)生原因

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

          優(yōu)化方向

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

          常見實(shí)踐

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

          不僅要關(guān)注值,還要關(guān)注是不是原來的對象

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

          參考:

          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ù)棧看更多干貨



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

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  AAAA毛片 | 久久香蕉精品 | 天天草天天日天天干 | 操操逼逼 | 影音先锋东莞AV |