商品訂單庫存一致性問題的思考


首先先確認方案

方案1:下單后減庫存;用戶下單,然后庫存加鎖,判斷庫存是否充足,用戶下單完成,減庫存,最后釋放庫存鎖。

方案2:支付才減庫存;用戶支付,然后庫存加鎖,判斷庫存是否充足,用戶支付完成,減庫存,最后釋放庫存鎖。

當然還有其他方案,這里只闡述我的思考。

(庫存加鎖的過程有個小細節請看附1

種方案的比較

方案1

1)假如100個人同時下單,只有一個人能下單成功。

2)此時訂單應該有一個過期狀態,如果訂單過期,庫存加鎖並回寫庫存后釋放鎖。

方案2

1)100個人可以同時下單,但是100個人同時付款時,只有一個人付款成功。

正常情況下,商品加入購物車的用戶>>>下單的用戶>=付款的用戶。如果從庫存加鎖的角度來說,在下單的時候加鎖,那么高並發下用戶體驗可能比較差,因為同時下單只有一個人能下單成功,而且服務器性能可能會比較差;下單的請求變多,那么請求加鎖的次數也變多了,而支付的用戶可能小於下單的用戶,請求加鎖的次數理論上會少不少。

我的建議是:

普通的電商項目我認為方案一就足夠了,因為下單的流程簡單,而支付可能涉及到很多業務,如果支付里面鎖庫存,考慮的東西會有點多。

但是在高並發下或者秒殺場景下,那可能就要在支付的時候鎖庫存。從業務角度來說,肯定是手快有,手快無;從代碼的角度來說,支付跟減庫存高度耦合,出現超賣、庫存不一致情況大大降低,如果是下單鎖庫存,萬一用戶取消訂單,那是不是庫存要加回去,這種情況下高並發出現庫存與實際消費不一致的可能性比較大。而且還有一個好處,秒殺場景在支付時候加鎖能夠保證所有的產品都賣出去,而在下單的時候加鎖,那么有可能有用戶取消訂單,到秒殺結束時有產品沒賣出去,如果是老板肯定是要全部賣出去的。

 

實現方式以及優化

商品訂單庫存這個業務不僅僅只有用戶下單這個功能,還要在管理后台提供商家修改庫存的入口。那么這樣就有兩處需要用到庫存,必須要考慮競爭問題。

單體架構的實現

單體架構實現這個業務是最簡單的,但是性能也是最差的。

 

 

單體架構中,不管是用戶操作還是管理員后台操作庫存都放在里面,然后部署到機器上。此時庫存鎖是個全局鎖,用戶下單,管理員要修改庫存都要從全局的庫存鎖拿到鎖,執行完業務代碼再釋放。

這種單體架構就會出現一個問題,耦合度太高,一旦管理后台修改庫存占用庫存鎖,那么用戶就不能下單購買商品了。如果是購買量不多的業務,單體架構是可以滿足基本需求的,這種實現成本低,易維護但不能支撐高並發。

像大部分中小型公司,一天的訂單我感覺也就1000以內,單體架構完全夠用了,並不需要改造成下面的方案,增加冪級的復雜度

 

優化方案

如果說業務增長塊訂單量增大,那么上面的單體架構就有局限性了。特別是現在互聯網公司的架構大部分都是微服務分布式。

1)首先,每次加鎖后,都需要從數據庫查詢庫存,判斷庫存,然后用戶下完單也要操作數據庫修改庫存。數據庫的操作是需要時間成本的,大流量下如果其中一個用戶下單時間太慢,其他用戶都要等待他處理完,用戶體驗太差

2)其次現在架構大部分是分布式、微服務的,用戶下單減庫存和管理后台修改庫存一般都是拆分為兩個服務--下單服務和庫存服務

 

優化的第一步。要解決下單因為操作數據庫耗時過長的問題,我們可以把庫存放到緩存中(一般是redis),然后對redis中的庫存加redis鎖,執行下單,對redis中的庫存進行減庫存。這么做的好處是提升了用戶下單的速率,加大了並發量;其次用戶下單跟管理后台的業務解耦了,為以后拆分服務做擴展。如下圖:

 

 

雖然提升了性能,但是新問題出現了,數據一致性問題。現在業務是獨立分開了,用戶下單,redis加鎖,操作redis的庫存就可以了,同樣管理后台修改庫存,加鎖操作數據庫的庫存就可以了。但是怎么保證這兩個地方的庫存一致性呢?

 

使用消息隊列讓數據庫的庫存進行減庫存

用戶下單成功后向消息隊列發送一條消息,然后在管理后台業務中消費消息,進行減庫存,如圖:

 

 

如果保證了消息的可靠性傳輸,那么即可保證用戶下單后的庫存與數據庫的庫存達到最終一致性。

 

停售同步庫存

如果管理后台要修改庫存並且同步到redis上去要怎么辦?可行的方案是讓商家停止出售商品,然后判斷redis的庫存跟數據庫的庫存是否一致,若一致,那么商家即可修改庫存,修改后更新數據庫庫存和redis庫存。如果不一致,那么你就要知道是否管理后台還沒消費完消息隊列,還是其他問題,沒消費完讓商家等一段時間就行了,是其他問題的話程序員就妥妥的背鍋吧,必須從日志系統里面查查具體是哪里出問題。

 

 

微服務分布式

其實就是把單體架構拆分,具體解決思路不變。具體要看業務把,如果訂單不多的話,沒必要拆分為訂單服務和庫存服務或者把他們解耦出來,一開始的單體架構就夠用了。至於優化的方案,我感覺獨角獸或者大型公司才會用到。也可能我技術角度或者業務角度沒達到那種高度。

 

其他問題

因為之前面試,經常就問到商品庫存這個問題,現在只是寫個自己的思考,肯定有錯誤的地方。還有很多問題就不去考慮了,畢竟我沒做過這個業務,例如生產上因為用戶點擊過快、網絡問題導致的訂單重復提交。

 

題外話

算是給點建議吧,一開始找工作盡量找穩定的公司,然后干幾年積累技術。我面試的時候因為簡歷是3年換了3家公司,每次面試都要問我為啥經常離職。而且大公司簡歷幾乎就進不了面試,中小型公司技術面過了,但是HR面因為這個問題也可能會把你刷了。還有就是要從業務上考慮,業務才能驅動技術的提升。(PS:廣州真的是一線城市么?工資是真的低,干IT別來廣州,別來廣州,別來)

 

附1:

高並發或者秒殺場景下,不管是方案1還是方案2,如果庫存為0時,是否還是每次都鎖庫存去走一遍流程,即庫存加鎖,判斷庫存是否充足,用戶下單完成,減庫存,最后釋放庫存鎖,答案肯定是否的。

想一想java實現單例模式的代碼,用了兩個判斷語句,那么這個場景我們也可以使用這種方式。外面先去查詢一次庫存,再判斷是否為0,如果為0直接返回,如果不為0 ,那么庫存加鎖,判斷庫存是否充足,用戶下單(或支付)完成,減庫存,最后釋放庫存鎖。以下單鎖庫存為例:

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM