<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>

          MySQL樂觀鎖扣減庫存原理圖解

          共 2955字,需瀏覽 6分鐘

           ·

          2022-01-02 13:14


          JAVA前線?


          歡迎大家關(guān)注公眾號(hào)「JAVA前線」查看更多精彩分享,主要內(nèi)容包括源碼分析、實(shí)際應(yīng)用、架構(gòu)思維、職場分享、產(chǎn)品思考等等,同時(shí)也非常歡迎大家加我微信「java_front」一起交流學(xué)習(xí)


          1 基礎(chǔ)知識(shí)

          在電商系統(tǒng)中扣減庫存是一步非常關(guān)鍵的操作,例如秒殺系統(tǒng)中一定要防止超賣情況出現(xiàn),如果商家設(shè)置了100件庫存但是最后賣出1000件,這樣就會(huì)產(chǎn)生資金損失。在扣減庫存時(shí)一般使用如下語句:

          udpate goods set stock = stock - #{acquire} 
          where sku_id = #{skuId} and stock - #{acquire} >= 0

          這條語句可以保護(hù)庫存資源防止超賣,我們不妨分析這條語句為什么生效。本文使用MySQL Innodb引擎進(jìn)行演示,隔離級(jí)別為可重復(fù)讀。


          1.1 共享鎖與排它鎖

          共享鎖(share Lock)又被稱為讀鎖,實(shí)現(xiàn)共享鎖語句如下:

          select lock in share mode

          排它鎖(exclusive Lock)又被稱為寫鎖,實(shí)現(xiàn)排它鎖語句如下:

          select for update
          update
          delete
          insert

          共享鎖與排它鎖兼容關(guān)系如下表:




          我們通過實(shí)例分析上述兼容關(guān)系,首先建一張測試表并寫入測試數(shù)據(jù):

          CREATE TABLE `test_account` (
          `id` bigint(20) NOT NULL,
          `name` varchar(20) DEFAULT NULL,
          `account` bigint(20) DEFAULT NULL,
          `version` bigint(20) DEFAULT NULL,
          PRIMARY KEY (`id`)
          ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

          insert into `test_account`(`id`,`name`,`account`,`version`) values (1,'A',100,1);
          insert into `test_account`(`id`,`name`,`account`,`version`) values (2,'B',200,1);
          insert into `test_account`(`id`,`name`,`account`,`version`) values (3,'C',300,1);


          (1) 讀讀兼容

          共享鎖與共享鎖之間兼容,在如下實(shí)例中session1在t3時(shí)刻,session2在t4時(shí)刻執(zhí)行查詢均可以獲取預(yù)期結(jié)果:




          (2) 讀寫互斥

          共享鎖與排它鎖之間互斥,在如下實(shí)例中session1在t3時(shí)刻加共享鎖,可以正確讀取結(jié)果,但是session2在t4時(shí)刻嘗試加排它鎖,但是此時(shí)鎖被session1占有,session2需要等待,當(dāng)session1長時(shí)間不釋放鎖時(shí),session2拋出鎖超時(shí)異常:




          (3) 寫寫互斥

          排它鎖與排它鎖之間互斥,在如下實(shí)例中session1在t3時(shí)刻加排它鎖,可以正確讀取結(jié)果,但是session2在t4時(shí)刻嘗試加排它鎖,但是此時(shí)鎖被session1占有,session2需要等待,當(dāng)session1長時(shí)間不釋放鎖時(shí),session2拋出鎖超時(shí)異常:




          1.2 當(dāng)前讀與快照讀

          MySQL Innodb存儲(chǔ)引擎實(shí)現(xiàn)基于多版本并發(fā)控制協(xié)議MVCC,在MVCC并發(fā)控制中讀操作可以分成快照讀與當(dāng)前讀。

          快照讀不需要加鎖,讀取的是記錄可見版本,有可能是歷史版本??梢灶惐扔唵慰煺眨脩粝聠沃笊唐穬r(jià)格發(fā)生了變化,但是訂單快照不會(huì)改變。實(shí)現(xiàn)當(dāng)前讀語句如下:

          select

          當(dāng)前讀需要加鎖,讀取的是記錄最新版本,加鎖保證了在讀取時(shí),當(dāng)前記錄不會(huì)被其它事務(wù)修改。實(shí)現(xiàn)當(dāng)前讀語句如下:

          select lock in share mode
          select for update
          update
          delete
          insert

          我們通過一個(gè)實(shí)例分析快照讀和當(dāng)前讀,session2在t4時(shí)刻修改記錄并在t5時(shí)刻提交,session1在t6時(shí)刻進(jìn)行了快照讀,讀取的是本事務(wù)開始時(shí)結(jié)果100,在t7時(shí)刻進(jìn)行了當(dāng)前讀,讀取的是記錄最新版本結(jié)果101:




          當(dāng)前讀流程是怎么樣的呢?我們以u(píng)pdate為例進(jìn)行分析當(dāng)前讀流程:




          第一次程序?qū)嵗l(fā)出當(dāng)前讀請求,存儲(chǔ)引擎返回滿足where條件的第一條記錄并加鎖,程序?qū)嵗侔l(fā)出更新請求,存儲(chǔ)引起操作完成響應(yīng)成功。依次執(zhí)行直到所有滿足where條件記錄執(zhí)行完成為止。

          這里我們做一些引申,RR級(jí)別提供了兩種機(jī)制避免幻讀問題:第一種方式是快照讀,讀取的是當(dāng)前事務(wù)開啟時(shí)的快照。第二種方式針對當(dāng)前讀,防止幻讀依賴Next-Key Lock機(jī)制。


          2 樂觀鎖原理

          我們通過一個(gè)問題將上述知識(shí)整合起來:有兩個(gè)線程在同一時(shí)刻執(zhí)行如下語句,請問id=1這條記錄account值會(huì)不會(huì)成功扣減兩次?

          update test_account set account = account - 100, version = version + 1 
          where id = 1 and version = 1

          上述語句使用了樂觀鎖,我們知道樂觀鎖就是對資源進(jìn)行保護(hù)的,所以答案是不會(huì)扣減兩次,但是不能就此止步,需要結(jié)合第一章節(jié)知識(shí)進(jìn)行進(jìn)一步分析:




          t2時(shí)刻session1和session2同時(shí)執(zhí)行update操作,由于update會(huì)加排它鎖,所以兩者只能有一個(gè)成功:session1成功,session2阻塞等待排它鎖釋放。

          t3時(shí)刻session1提交事務(wù)釋放排它鎖,此時(shí)session2獲取到鎖進(jìn)行當(dāng)前讀,但是此時(shí)id=1記錄version值已經(jīng)變成了2,執(zhí)行語句已經(jīng)查詢不到待更新數(shù)據(jù),所以沒有記錄發(fā)生更新。


          3 扣減庫存原理

          如果理解了第二章節(jié)樂觀鎖原理,那么扣減庫存原理已經(jīng)顯而易見,我們假設(shè)商品只剩下1件庫存,如果兩個(gè)線程同時(shí)執(zhí)行扣減庫存,會(huì)發(fā)生超賣的情況嗎?




          t2時(shí)刻session1和session2同時(shí)執(zhí)行updatek扣減庫存,由于update會(huì)加排它鎖,所以兩者只能有一個(gè)成功:session1成功,session2阻塞等待排它鎖釋放。

          t3時(shí)刻session1提交事務(wù)釋放排它鎖,此時(shí)session2獲取到鎖進(jìn)行當(dāng)前讀,但是此時(shí)商品1庫存已經(jīng)變?yōu)?,已經(jīng)不滿足(where stock - 1 >= 0)條件,執(zhí)行語句已經(jīng)查詢不到待更新數(shù)據(jù),所以沒有記錄發(fā)生更新。


          4 文章總結(jié)

          第一我們分析了兩組基礎(chǔ)知識(shí):共享鎖與排它鎖,快照讀與當(dāng)前讀。第二我們將兩組基礎(chǔ)知識(shí)進(jìn)行融合,分析了樂觀鎖如何生效。第三我們由樂觀鎖原理出發(fā),最終理解了扣減庫存原理,希望本文對大家有所幫助。



          JAVA前線?


          歡迎大家關(guān)注公眾號(hào)「JAVA前線」查看更多精彩分享,主要內(nèi)容包括源碼分析、實(shí)際應(yīng)用、架構(gòu)思維、職場分享、產(chǎn)品思考等等,同時(shí)也非常歡迎大家加我微信「java_front」一起交流學(xué)習(xí)


          瀏覽 26
          點(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>
                  黄色丁五月 | 午夜三级无码在线看 | 亚洲无码视频在线 | 尻屄视频网址 | 996热在线视频 |