女人自慰AV免费观看内涵网,日韩国产剧情在线观看网址,神马电影网特片网,最新一级电影欧美,在线观看亚洲欧美日韩,黄色视频在线播放免费观看,ABO涨奶期羡澄,第一导航fulione,美女主播操b

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

庫存扣減和鎖常見的實現方案代碼

Android編程精選 ? 來源:CSDN博客 ? 作者:北京-小北 ? 2021-11-06 14:48 ? 次閱讀

先說場景:

物品W現在庫存剩余1個, 用戶P1,P2同時購買.則只有1人能購買成功.(前提是不允許超賣)

秒殺也是類似的情況, 只有1件商品,N個用戶同時搶購,只有1人能搶到..

這里不談秒殺設計,不談使用隊列等使請求串行化,就談下怎么用鎖來保證數據正確.

常見的實現方案有以下幾種:

  1. 代碼同步, 例如使用 synchronized ,lock 等同步方法
  2. 不查詢,直接更新 update table set surplus = (surplus - buyQuantity) where id = xx and (surplus - buyQuantity) > 0
  3. 使用CAS, update table set surplus = aa where id = xx and version = y
  4. 使用數據庫鎖, select xx for update
  5. 使用分布式鎖(zookeeper,redis等)

下面就針對這幾種方案來分析下;

1.代碼同步, 例如使用 synchronized ,lock 等同步方法

面試的時候,我經常會問這個問題,很大一部分人都會回答用這個方案來實現.

偽代碼如下:

publicsynchronizedvoidbuy(StringproductName,IntegerbuyQuantity){
//其他校驗...
//校驗剩余數量
Productproduct=從數據庫查詢出記錄;
if(product.getSurplusreturn"庫存不足";
}

//set新的剩余數量
product.setSurplus(product.getSurplus()-quantity);
//更新數據庫
update(product);
//記錄日志...
//其他業務...
}

在方法聲明加上synchronized關鍵字,實現同步,這樣2個用戶同時購買,到buy方法時候同步執行,第2個用戶執行的時候,會庫存不足.

嗯.. 看著挺合理的,以前我也是這么干的. 所以現在碰到別人這樣回答,我就會在心里默默的想.小伙子你是沒踩過這坑啊.

先說下這個方案的前提配置:

  • 使用spring 聲明式事務管理
  • 事務傳播機制使用默認的(PROPAGATION_REQUIRED)
  • 項目分層為controller-service-dao 3層, 事務管理在service層

這個方案不可行,主要是因為以下幾點:

1).synchronized 作用范圍是單個jvm實例, 如果做了集群,分布式等,就沒用了

2).synchronized是作用在對象實例上的,如果不是單例,則多個實例間不會同步(這個一般用spring管理bean,默認就是單例)

3).單個jvm時,synchronized也不能保證多個數據庫事務的隔離性. 這與代碼中的事務傳播級別,數據庫的事務隔離級別,加鎖時機等相關.

3-1).先說隔離級別,常用的是 Read Committed 和 Repeatable Read ,另外2種不常用就不說了

3-1-1)RR(Repeatable Read)級別.mysql默認的是RR,事務開啟后,不會讀取到其他事務提交的數據

根據前面的前提,我們知道在buy方法時會開啟事務.

假設現在有線程T1,T2同時執行buy方法.假設T1先執行,T2等待.

spring的事務開啟和提交等是通過aop(代理)實現的,所以執行buy方法前,就會開啟事務.

這時候T1,T2是兩個事務,當T1執行完后,T2執行,讀取不到T1提交的數據,所以會出問題.

3-1-2).RC(Read Committed)級別.事務開啟后,可以讀取到其他事務提交的數據

看起來這個級別可以解決上面的問題.T2執行時,可以讀取到T1提交的結果.

但是問題是,T2執行的時候, T1的事務提交了嗎?

事務和鎖的流程如下

  1. 開啟事務(aop)
  2. 加鎖(進入synchronized方法)
  3. 釋放鎖(退出synchronized方法)
  4. 提交事務(aop)

可以看出是先釋放鎖,再提交事務.所以T2執行查詢,可能還是未讀到T1提交的數據,還會出問題

3-2).根據3-1中的問題,發現主要矛盾是事務開啟和提交的時機與加鎖解鎖時機不一致.有小伙伴們可能就想到了解決方案.

3-2-1).在事務開啟前加鎖,事務提交后解鎖.

確實是可以,這相當于事務串行化.拋開性能不談,來談談怎么實現.

如果使用默認的事務傳播機制,那么要保證事務開啟前加鎖,事務提交后解鎖,就需要把加鎖,解鎖放在controller層.

這樣就有個潛在問題,所有操作庫存的方法,都要加鎖,而且要是同一把鎖,寫起來挺累的.

而且這樣還是不能跨jvm.

3-2-2).將查詢庫存,扣減庫存這2步操作,單獨提取個方法,單獨使用事務,并且事務隔離級別設置為RC.

這個其實和上面的3-2-1異曲同工,最終都是講加解鎖放在了事務開啟提交外層.

比較而言優點是入口少了. controller不用處理.

缺點除了上面的不能跨jvm,還有就是 單獨的這個方法,需要放到另外的service類中.

因為使用spring,同一個bean的內部方法調用,是不會被再次代理的,所以配置的單獨事務等需要放到另外的service bean 中

2.不查詢,直接更新

看完第一種方案,有小伙伴就說了. 你說的那么復雜,那么多問題,不就是因為查詢的數據不是最新的嗎?

我們不查詢,直接更新不就行啦.

偽代碼如下:

publicsynchronizedvoidbuy(StringproductName,IntegerbuyQuantity){
//其他校驗...
int影響行數=updatetablesetsurplus=(surplus-buyQuantity)whereid=1;
if(result0){
return"庫存不足";
}
//記錄日志...
//其他業務...
}

測試后發現庫存變成-1了, 繼續完善下

publicsynchronizedvoidbuy(StringproductName,IntegerbuyQuantity){
//其他校驗...
int影響行數=updatetablesetsurplus=(surplus-buyQuantity)whereid=1and(surplus-buyQuantity)>0;
if(result0){
return"庫存不足";
}
//記錄日志...
//其他業務...
}

測試后,功能OK;

這樣確實可以實現,不過有一些其他問題:

  • 不具備通用性,例如add操作
  • 庫存操作一般要記錄操作前后的數量等,這樣沒法記錄
  • 其他...

但是根據這個方案,可以引出方案3.

3.使用CAS, update table set surplus = aa where id = xx and yy = y

CAS是指compare/check and swap/set 意思都差不多,不必太糾結是哪個單詞

我們將上面的sql修改一下:

int影響行數=updatetablesetsurplus=newQuantitywhereid=1andsurplus=oldQuantity;

這樣,線程T1執行完后,線程T2去更新,影響行數=0,則說明數據被更新, 重新查詢判斷執行.偽代碼如下:

publicvoidbuy(StringproductName,IntegerbuyQuantity){
//其他校驗...
Productproduct=getByDB(productName);
int影響行數=updatetablesetsurplus=(surplus-buyQuantity)whereid=1andsurplus=查詢的剩余數量;
while(result==0){
product=getByDB(productName);
if(查詢的剩余數量>buyQuantity){
影響行數=updatetablesetsurplus=(surplus-buyQuantity)whereid=1andsurplus=查詢的剩余數量;
}else{
return"庫存不足";
}
}

//記錄日志...
//其他業務...
}

看到重新查詢幾個字,小伙伴們應該就又想到事務隔離級別問題了.

沒錯,所以上面代碼中的getByDB方法,必須單獨事務(注意同一個bean內單獨事務不生效哦),而且數據庫的事務隔離級別必須是RC,

否則上面的代碼就會是死循環了.

上面的方案,可能會出現一個CAS中經典問題. ABA的問題.

ABA是指:

  • 線程T1 查詢,庫存剩余 100
  • 線程T2 查詢,庫存剩余 100
  • 線程T1 執行 sub update t set surplus = 90 where id = x and surplus = 100;
  • 線程T3 查詢, 庫存剩余 90
  • 線程T3 執行add update t set surplus = 100 where id = x and surplus = 90;
  • 線程T2 執行sub update t set surplus = 90 where id = x and surplus = 100;

這里線程T2執行的時候,庫存的100已經不是查詢到的100了,但是對于這個業務是不影響的.

一般的設計中CAS會使用version來控制.

updatetsetsurplus=90,version=version+1whereid=xandversion=oldVersion;

這樣,每次更新version在原基礎上+1,就可以了.

使用CAS要注意幾點,

  • 失敗重試次數,是否需要限制
  • 失敗重試對用戶是透明的

4.使用數據庫鎖, select xx for update

方案3種的cas,是樂觀鎖的實現, 而select for udpate 則是悲觀鎖. 在查詢數據的時候,就將數據鎖住.

偽代碼如下:

publicvoidbuy(StringproductName,IntegerbuyQuantity){
//其他校驗...
Productproduct=select*fromtablewherename=productNameforupdate;
if(查詢的剩余數量>buyQuantity){
影響行數=updatetablesetsurplus=(surplus-buyQuantity)wherename=productName;
}else{
return"庫存不足";
}

//記錄日志...
//其他業務...
}

線程T1 進行sub , 查詢庫存剩余 100

線程T2 進行sub , 這時候,線程T1事務還未提交,線程T2阻塞,直到線程T1事務提交或回滾才能查詢出結果.

所以線程T2查詢出的一定是最新的數據.相當于事務串行化了,就解決了數據一致性問題.

對于select for update,需要注意的有2點.

  1. 統一入口:所有庫存操作都需要統一使用 select for update ,這樣才會阻塞, 如果另外一個方法還是普通的select, 是不會被阻塞的

  2. 加鎖順序:如果有多個鎖,那么加鎖順序要一致,否則會出現死鎖.

5.使用分布式鎖(zookeeper,redis等)

使用分布式鎖,原理和方案1種的synchronized是一樣的.只不過synchronized的flag只有jvm進程內可見,而分布式鎖的flag則是全局可見.方案4種的select for update 的flag 也是全局可見.

分布式鎖的實現方案有很多:基于redis,基于zookeeper,基于數據庫等等.

需要注意,使用分布式鎖和synchronized鎖有同樣的問題,就是鎖和事務的順序,這個在方案1里面已經講過.不再重復.

做個簡單總結:

  • 方案1: synchronized等jvm內部鎖不適合用來保證數據庫數據一致性,不能跨jvm
  • 方案2: 不具備通用性,不能記錄操作前后日志
  • 方案3: 推薦使用.但是如果數據競爭激烈,則自動重試次數會急劇上升,需要注意.
  • 方案4: 推薦使用.最簡單的方案,但是如果事務過大,會有性能問題.操作不當,會有死鎖問題
  • 方案5: 和方案1類似,只是能跨jvm
責任編輯:haq
聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 數據
    +關注

    關注

    8

    文章

    7241

    瀏覽量

    91020
  • 源代碼
    +關注

    關注

    96

    文章

    2950

    瀏覽量

    67881

原文標題:實踐角度,談談庫存扣減和鎖

文章出處:【微信號:AndroidPush,微信公眾號:Android編程精選】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦
    熱點推薦

    RFID在服裝庫存中的應用

    RFID是一種通過無線電波進行數據讀寫和物體識別的技術。它由電子標簽、讀寫器和天線組成,能夠實現非接觸式的數據交互。相比傳統的條形碼技術,RFID具有更高的效率、更強的抗污染能力和更遠的讀取距離
    的頭像 發表于 04-09 16:18 ?246次閱讀
    RFID在服裝<b class='flag-5'>庫存</b>中的應用

    電路常見故障及解決方法

    繼電器、按鈕、限位開關等組成。當電路中的某個條件被滿足時,繼電器的常閉觸點會斷開,而常開觸點會閉合,從而保持電路的狀態,即使初始條件不再滿足。 常見故障 1. 電路無法自 故障原因 繼電器損壞或觸點粘連。 按鈕或限位開關
    的頭像 發表于 01-18 10:05 ?1726次閱讀

    電路怎么實現自動控制

    在現代電子技術中,自動控制是實現智能化和自動化的關鍵。自電路作為一種基本的自動控制電路,因其簡單、可靠和易于實現的特點,被廣泛應用于各種自動控制系統中。 1. 自電路的工作原理 自
    的頭像 發表于 01-18 10:04 ?866次閱讀

    電路的類型和特點

    在電子工程領域,自電路是一種常見的設計,它能夠使電路在沒有持續的觸發信號的情況下保持其狀態。這種電路的設計對于實現自動化控制和減少人為干預至關重要。 一、自電路的類型 自
    的頭像 發表于 01-18 10:03 ?735次閱讀

    庫存平臺穩定性建設實踐

    作者:京東物流 尹昊喆 前言 本文總結庫存平臺穩定性建設中遇到的問題以及解決方案。感謝【金鵬】、【孫靜】、【陳瑞】同學在本文撰寫中提供的內容及幫助! 庫存平臺面臨的穩定性挑戰 庫存平臺
    的頭像 發表于 12-11 09:50 ?500次閱讀
    <b class='flag-5'>庫存</b>平臺穩定性建設實踐

    SQL錯誤代碼及解決方案

    在SQL數據庫開發和管理中,常見的錯誤代碼及其解決方案可以歸納如下: 一、語法錯誤(Syntax Errors) 錯誤代碼 :無特定代碼,但
    的頭像 發表于 11-19 10:21 ?6101次閱讀

    基于圖遍歷的Flink任務畫布模式下零代碼開發實現方案

    的過程。以下是利用Flink的 StreamGraph 通過低代碼的方式,來實現StreamGraph的生成,并最終實現 Flink 程序零代碼開發的解決
    的頭像 發表于 11-05 10:35 ?1038次閱讀
    基于圖遍歷的Flink任務畫布模式下零<b class='flag-5'>代碼</b>開發<b class='flag-5'>實現</b><b class='flag-5'>方案</b>

    Karmak攜手Syncron實現庫存規劃流程自動化

    全新Syncron融合平臺助力Karmak客戶實現最大限度地優化配件供應并提高配件銷售額 伊利諾伊州卡林維爾2024年11月2日?/美通社/ -- 重型卡車運輸行業企業管理解決方案領先企業
    的頭像 發表于 11-03 11:48 ?387次閱讀

    D存器的基本實現

    在Verilog HDL中實現存器(Latch)通常涉及對硬件描述語言的基本理解,特別是關于信號如何根據控制信號的變化而保持或更新其值。存器與觸發器(Flip-Flop)的主要區別在于,
    的頭像 發表于 08-30 10:45 ?1525次閱讀

    d存器解決了sr存器的什么問題

    D存器(Data Latch)和SR存器(Set-Reset Latch)是數字電路中常見的兩種存儲元件。它們在數字系統中扮演著重要的角色,用于存儲和傳遞信息。然而,這兩種存器在
    的頭像 發表于 08-28 09:16 ?1043次閱讀

    半自動和全自動智能的功放,電源設計方案

    智能電源功放設計需考慮電池類型、電壓、功耗、成本及可靠性。本文探討了半自動與全自動智能鎖在不同電源配置下的設計方案,包括IC選型,以提升的性能與安全性,延長使用壽命。
    的頭像 發表于 08-21 16:00 ?2127次閱讀
    半自動和全自動智能<b class='flag-5'>鎖</b>的功放,電源設計<b class='flag-5'>方案</b>

    存器工作時是什么觸發方式

    存器(Latch)是一種存儲電路,用于存儲一位二進制信息。存器在數字電路設計中非常常見,它可以用來保持數據狀態、實現同步等功能。存器的
    的頭像 發表于 07-23 10:17 ?839次閱讀

    互斥和自旋實現原理

    保護共享資源不被多個線程同時訪問。它的實現原理主要包括以下幾個方面: 1. 的初始化 互斥鎖在創建時需要進行初始化,通常包括設置的狀態為“未鎖定”。在某些實現中,還需要初始化
    的頭像 發表于 07-10 10:07 ?947次閱讀

    自旋和互斥的使用場景是什么

    自旋和互斥是兩種常見的同步機制,它們在多線程編程中被廣泛使用。在本文中,我們將介紹自旋和互斥的使用場景,以及它們在不同場景下的優勢和
    的頭像 發表于 07-10 10:05 ?1403次閱讀

    變頻器自實現方法介紹

    在工業自動化和電機控制領域,變頻器作為一種重要的電力控制設備,廣泛應用于各種設備的轉速和電機輸出功率的調節。在特定情況下,如緊急停機或故障發生時,確保設備和人員的安全,實現變頻器的自功能顯得尤為重要。本文將詳細介紹變頻器自
    的頭像 發表于 06-20 11:35 ?1984次閱讀