記一次專案中解決 -- 併發減庫存超賣問題過程(Java)

Dkwestworld發表於2021-06-20

起因:專案中要做預約功能,首先每天的餘票都是有上限的,自然不能出現超賣的情況

基於我們專案是單體分散式的springcloud部署,我想了下?

第一種方法,直接mysql加行鎖,要update這條庫存資料時,在資料庫表層面加上行鎖,直接禁止其他執行緒讀寫,就確保了這條庫存資料是被單執行緒操作的,不會出現超賣

第二種方法,把庫存資料放Redis,需要update時對快取資料加鎖,也能保證該條庫存資料被單執行緒操作

第三種方法,是最簡單的方法,程式碼實現悲觀鎖,也是最不專業的方法,就是在最終修改庫存的方法新增同步程式碼塊,沒錯就是他,就是sync,更新庫存的方法單獨封裝成一個同步方法。但是這裡有一點謹記,在同步程式碼塊所在的方法類上千萬別新增@Transactional註解,這個坑同樣會造成超賣,因為,事務註解的操作範圍大於同步程式碼塊,你想有種情況:正在賣第十部手機鎖釋放但事務未提交,這時新執行緒進入查出老庫存,又會把第十部手機再重複賣一次,老闆會打爆你的狗頭的,記住去掉@Transactional註解。(第三個方法完全可以採用)

第四種方案:程式碼加持在資料庫層面實現樂觀鎖,聽起來很複雜,其實不然。其實就是在資料庫為每條庫存資料新增唯一版本號version欄位,查出來version是1,我更新的時候就加上這個version的 where 條件去更新(切記庫存的加減和version新增在sql層面編寫),version相同我才更新庫存,不相同不更新(這裡根據update的sql返回值是否為1判斷是否更新成功即可),再重新迴圈查一次最新version即可(第四種方案不僅適用於單機,若你們專案時叢集部署同樣是適合的,而且高效,程式碼也簡單)

 


 

選擇與思考:

第三種使用者體驗不會特別好,而且只適用於單機部署的專案

個人比較推薦第四種樂觀鎖方案,程式碼也簡單

後期如果併發特別大,有的執行緒由於等太久導致請求超時,需要考慮做做介面限流。

相關文章