本文討論內容不包含秒殺,僅僅是最普遍的庫存模型而已,重點討論如何避免超賣
,少賣
。
1、 基本概念
1.1 超賣
實際庫存已經爲0,但是依舊賣出去了。可能導致商家無貨可發。
1.2 少賣
明明有貨,但是庫存被鎖定,導致商家貨賣不出去。
2、 分佈式事務簡述
2.1 消息事務+最終一致性
所謂的消息事務就是基於消息中間件的兩階段提交,本質上是對消息中間件的一種特殊利用,
它是將本地事務和發消息放在了一個分佈式事務裏,保證要麼本地操作成功成功並且對外發消息成功,
要麼兩者都失敗,開源的RocketMQ就支持這一特性,具體原理如下:
- 1、A系統向消息中間件發送一條預備消息
- 2、消息中間件保存預備消息並返回成功
- 3、A執行本地事務
- 4、A發送提交消息給消息中間件
通過以上4步完成了一個消息事務。對於以上的4個步驟,每個步驟都可能產生錯誤,下面一一分析:
- 步驟一出錯,則整個事務失敗,不會執行A的本地操作
- 步驟二出錯,則整個事務失敗,不會執行A的本地操作
- 步驟三出錯,這時候需要回滾預備消息,怎麼回滾?答案是A系統實現一個消息中間件的回調接口,消息中間件會去不斷執行回調接口,檢查A事務執行是否執行成功,如果失敗則回滾預備消息
- 步驟四出錯,這時候A的本地事務是成功的,那麼消息中間件要回滾A嗎?答案是不需要,其實通過回調接口,消息中間件能夠檢查到A執行成功了,這時候其實不需要A發提交消息了,消息中間件可以自己對消息進行提交,從而完成整個消息事務
2.2 TCC編程模式
所謂的TCC編程模式,也是兩階段提交的一個變種。TCC提供了一個編程框架,將整個業務邏輯分爲三塊:Try、Confirm和Cancel三個操作。
以在線下單爲例,Try階段會去扣庫存,Confirm階段則是去更新訂單狀態,如果更新訂單失敗,則進入Cancel階段,會去恢復庫存。
總之,TCC就是通過代碼人爲實現了兩階段提交,不同的業務場景所寫的代碼都不一樣,複雜度也不一樣,因此,這種模式並不能很好地被複用。
3、 庫存模型
- 1.下單的時候每個訂單都會有預佔庫存。
- 2.訂單出庫或取消解鎖預佔。
- 3.可售庫存 = 現貨庫存 - 預佔庫存
具體下單操作流程是
下單 –> 預佔(預佔庫存+1) –> 支付 –> 出庫(釋放預佔並減庫存,涉及到分佈式事務) –> 完成
重點討論此方案的意義,跟出庫部分。
3.1 爲什麼設計成預佔庫存?
以下均爲個人理解
- 1.庫存和下單分離,下單了不會實際減少庫存。真正的實物庫存跟邏輯庫存分離,方便(盤庫、補貨等)
僅僅用實物庫存
的場景跟高併發
的場景(下單)分離。 - 2.預佔可以減少庫存表的操作頻率,減庫存可以在發貨的時候統一處理。(操作多次預佔表後僅在必要時操作一次庫存表,跟1有共通之處)
3.2 預佔庫存怎麼加?
- 1.可以用mysql樂觀鎖(version遞增避免aba)
- 2.可以用redis watch(同樣基於cas,無aba)
3.3 出庫部分怎麼保證事務?
1.先出庫還是先釋放預佔?
爲了避免超賣選擇先減庫存,再釋放預佔。
2.如何保證事務一致性?
採用消息機制的2pc保證分佈式事務。