秒殺庫存控制
更新時間 2023-12-20 09:50:38
最近更新時間: 2023-12-20 09:50:38
分享文章
業務場景
對某個套餐或產品進行銷售,設置待銷售品總數量。銷售時不能出現超賣情況,即銷售品剩余數量不能為負數。
可以分為以下幾個步驟:
- 用戶請求進入系統:當用戶發起請求時,請求會首先進入負載均衡服務器。
- 負載均衡:負載均衡服務器會根據一定的算法將請求分發給后端多臺服務器,以達到負載均衡的目的。
- 業務邏輯處理:后端服務器接收到請求后,進行業務邏輯處理,并根據請求的商品數量、用戶身份等信息進行校驗。
- 庫存扣減:如果庫存充足,后端服務器會進行庫存扣減操作,并生成訂單信息,返回給用戶成功的信息;如果庫存不足,則返回給用戶失敗的信息。
- 訂單處理:后端服務器會將訂單信息保存到數據庫中,并進行異步處理,例如發送消息通知用戶訂單狀態。
- 緩存更新:后端服務器會更新緩存中的商品庫存信息,以便處理下一次請求。
整個過程會多次訪問數據庫,下單通常是利用行級鎖進行訪問限制,搶到鎖才能查詢數據庫和下單。但是秒殺時的大量訂單請求,往往使數據庫訪問阻塞。
業務需求
- 系統高并發,極端熱門套餐搶購比率只有1%,比如100個銷售品在幾秒內搶購完,在前端庫存判斷需要支持上10w的庫存快速判斷
- 商品不能出現超售情況
- 多個商戶進行銷售,保證剩余商品數量的數據一致性
需求分析
- 獲取商品剩余數量進行條件判斷和更新數據操作,是兩個獨立的操作,中間有可能有其他應用修改數據從而產生沖突。在沖突的發生時,用戶需要合并最新的數據,再進行條件判斷和數據修改。將兩個獨立操作封裝成一個原子的服務,保證(剩余數量-銷售量)之后,商品剩余量不為負數;.
示例方案:使用LUA腳本實現
由于redis是單線程處理,修改數據不需要上鎖,調用LUA腳本可視為一個原子的操作,能高效的執行和計算。參考如下:
local?n?=?tonumber(ARGV[1])
if?not?n??or?n?==?0?then
????return?0
end
local?vals?=?redis.call(\"HMGET\",?KEYS[1],?\"total\",?\"booked\",?\"remain\");
local?booked?=?tonumber(vals[2])
local?remain?=?tonumber(vals[3])
if?booked?<=?remain?then
????redis.call(\"HINCRBY\",?KEYS[1],?\"booked\",?n)
????redis.call(\"HINCRBY\",?KEYS[1],?\"remain\",?-n)
????return?n;
end
return?0