<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ā)減庫存,怎么保證不超賣?

          共 2047字,需瀏覽 5分鐘

           ·

          2021-03-07 12:02

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

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

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

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

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

          我理解,主要的方式是加鎖。加鎖有兩個(gè)層面:一個(gè)是程序?qū)用妫硪粋€(gè)是數(shù)據(jù)庫層面。

          分布式鎖

          這種場(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ì)存在問題,請(qǐng)看下面的代碼

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

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

          關(guān)于事務(wù)隔離級(jí)別這塊兒,可在公眾號(hào)Java技術(shù)棧搜索閱讀。

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

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

          Spring聲明式事務(wù)是Spring AOP最好的例子,Spring是通過AOP代理的方式來實(shí)現(xiàn)事務(wù)的,也就是說在調(diào)用reduceStock()方法的之前就已經(jīng)開啟了事務(wù)。

          那么,在并發(fā)情況下可能會(huì)存在這樣的情況,假設(shè)線程T1和T2都執(zhí)行到這里,于是它們都開啟了事務(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更新前的庫存數(shù)據(jù)。

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

          鑒于這種情況呢,可以將庫存放到Redis中,我們直接讀寫Redis,這樣可以避免受數(shù)據(jù)庫事務(wù)的影響,當(dāng)然這也會(huì)帶來新的問題,不再討論。

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

          CAS(compare and swap)比較并交換

          在Java中,一個(gè)線程想修改某個(gè)變量的值,那么第一步是將變量的值從主內(nèi)存中讀取到自己工作內(nèi)存中,然后修改,最后寫回主內(nèi)存。這個(gè)過程可以歸結(jié)為:讀取——修改——寫入,在寫回內(nèi)存的時(shí)候可能當(dāng)前內(nèi)存中那個(gè)值已經(jīng)發(fā)生了變化,這個(gè)時(shí)候如果繼續(xù)寫則會(huì)覆蓋別人的數(shù)據(jù),只有當(dāng)內(nèi)存中的那個(gè)值和它修改之前讀到的那個(gè)值一樣,才可以寫入。這個(gè)跟數(shù)據(jù)庫是一樣的。Java中通過Unsafe中compareAndSwapObject這樣的方法類實(shí)現(xiàn)的,它直接調(diào)用CPU指令。

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

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

          數(shù)據(jù)增加一個(gè)版本標(biāo)識(shí),一般是通過為數(shù)據(jù)庫表增加一個(gè)數(shù)字類型的 “version” 字段來實(shí)現(xiàn)。當(dāng)讀取數(shù)據(jù)時(shí),將version字段的值一同讀出,數(shù)據(jù)每更新一次,對(duì)此version值加一。當(dāng)我們提交更新的時(shí)候,判斷數(shù)據(jù)庫表對(duì)應(yīng)記錄的當(dāng)前版本信息與第一次取出來的version值進(jìn)行比對(duì),如果數(shù)據(jù)庫表當(dāng)前版本號(hào)與第一次取出來的version值相等,則予以更新,否則認(rèn)為是過期數(shù)據(jù)。

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

          ABA問題

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

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

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

          產(chǎn)生原因

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

          優(yōu)化方向

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

          常見實(shí)踐

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

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

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



          來源:

          https://www.cnblogs.com/cjsblog/p/9135118.html

          瀏覽 41
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

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

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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>
                  婷婷淫淫网 | 国产精品内射 | 国产一区二区无码午夜久久久豆花av | 爱爱爱爱爱免费视频 | 国产69久久成人看精品 |