商品庫存扣減方案設計

引言:在庫存的變動中,最關鍵的節(jié)點是庫存的扣減,在什么時候扣減庫存非常重要。目前通用的庫存扣減方案有以下幾種

  1. 支付后扣減庫存,缺點:成功下單的用戶,到支付時沒有庫存可用,導致交易失敗。

  2. 下單時就減庫存,訂單取消再把庫存加回來,缺點:惡意刷單不支付導致大量庫存被占用,影響商品售賣。

  3. 下單時先預減庫存(對應數(shù)據(jù)庫 占用庫存加庫存操作),支付完成時 釋放占用庫存(減操作),扣減可用庫存。 同時商品在下單時判斷商品的實際可售庫存 = 可用庫存 - 占用庫存,如果 > 0,表示可以下訂單,這樣就不會導致 下單成功,但是支付時沒有庫存導致失敗的場景。

具體方案

  • 訂單創(chuàng)建時,通過調(diào)用商品接口預占庫存,如果預占庫存成功,則創(chuàng)建訂單,如果預占庫存失敗,則提示商品庫存不足。這個接口必須是同步實時的,因為訂單要根據(jù)預占庫存的結果來判斷訂單能否創(chuàng)建。
  • 訂單支付、取消時,發(fā)送mq消息到商品,商品異步消費消息,根據(jù)消息的類別去操作庫存。如果是未支付取消訂單,則釋放預占用庫存。如果是支付后退款,則需要將可用庫存加回來。如果是成功支付,則釋放預占庫存,并同時扣減可用庫存。

需要注意的問題

  • 商品超賣問題
    正常情況下,一個訂單過來,其中A商品買了n個,那么我們操作數(shù)據(jù)庫的時候,直接 set stock = stock -n 。這種當然是有問題的,有可能會超賣導致商品庫存為負數(shù)。當然我們可以在更新db之前,判斷庫存數(shù)是否 > n,如果大于n,再去扣減庫存。這總當然可以,不過高并發(fā)下,可能依然會導致超賣。當然你可以加鎖去保證單線程,不過這樣就導致了接口的性能下降。其實sql 可以換個寫法 set stock = stock -n where stock >= n。 這樣利用了數(shù)據(jù)庫的天然寫法保證了商品不超賣。
  • 高并發(fā)下的接口性能問題
    步驟一訂單創(chuàng)建實時調(diào)用商品占用庫存接口,因為是實時調(diào)用,如果是高并發(fā)情況下,對于db占用庫存的更新操作可能就會成為性能瓶頸(訂單支付或者取消時,因為走了消息隊列異步更新數(shù)據(jù)庫,就不存在性能問題)。
    如果解決這個問題呢?業(yè)界常用的做法是,將商品的可用庫存放到redis中,當訂單創(chuàng)建調(diào)用占用庫存接口時,我們可以利用redis去抗并發(fā),并且redis的命令支持原子性。

1.創(chuàng)建訂單扣減緩存中的可用庫存

緩存中更新庫存和我們?nèi)ジ聰?shù)據(jù)庫時遇到的場景一樣,因為要判斷庫存是否大于下單購買數(shù)的邏輯要保持原子性,同時一個訂單中需要判斷多個商品的庫存也是需要原子性,可以結合lua腳本來實現(xiàn)。

  • 首先根據(jù)訂單明細id查詢扣減流水,是否已經(jīng)操作過,做冪等性校驗

  • 然后查詢sku的剩余庫存,并根據(jù)下單購買數(shù)做校驗,只要有一個sku 數(shù)量不足,則返回失敗

  • 修改緩存中的剩余庫存數(shù)

  • 緩存中插入扣減流水記錄

2.支付成功后消息隊列異步更新數(shù)據(jù)庫中的可用庫存

3.訂單未支付取消時則需要將緩存中的庫存加回來。根據(jù)訂單明細id查詢扣減流水,有扣減流水,則繼續(xù)查詢出訂單中所有sku商品的庫存,將扣減的庫存再加回來。如果沒有扣減流水,則跳過不處理。

4、訂單已支付退款時,需要同時更新緩存中的庫存和db中的庫存。

缺點:上面我們說的用lua腳本執(zhí)行命令,如果一個訂單中的多個商品,,一部分成功,一部分扣減庫存失敗,那么是無法進行回滾操作的,雖然這種可能性很小,所以這種方案我們只能盡量保證redis集群的高可用。以上方案解決了高并發(fā)下的接口性能瓶頸,但是因為其復雜性有可能會導致redis中可用庫存和數(shù)據(jù)庫中的可用庫存不一致(這里說的不一致是指沒有未支付訂單占用庫存的情況),我們可能還需要定時任務去定時維護redis中可用庫存和數(shù)據(jù)庫中可用庫存的一致性,用數(shù)據(jù)庫中的庫存 - 未支付訂單的占用庫存,然后更新到redis中

總結:以上基于緩存扣減庫存,大部分情況是對一些活動的秒殺商品可能才會有如此高的并發(fā),正常情況下也不可能將所用商戶的所有商品庫存都緩存到redis中,這樣也不現(xiàn)實。所以絕大部分流量不高的情況下,我們可以采用數(shù)據(jù)庫占用庫存的方式,這種方式簡單高效,不易出錯。對于一些活動商品,我們則可以單獨走緩存扣減可用庫存的方式。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容