電商庫存系統設計案例詳解(上)

1 庫存扣減

圖片

多人同時買一件商品時(假設庫存充足),每個人幾乎同時下單成功,給人一種並行感覺。但真實情況, 庫存只是一個數值,無論是存在 mysql 數據庫還是 redis 緩存,減值時都要控制順序,只能串行來扣減,當然爲保證安全性,會設計一些鎖控制。

1.1 關鍵技術點

1.2 數據庫扣減

主要依賴數據庫特性,保證扣減的一致性,邏輯簡單,開發部署成本低。

1.2.1 依賴的數據庫特性

圖片

最上面會查詢當前的剩餘庫存(可能不準確,但沒關係,這裏只是第一步粗略校驗),前置校驗,如果已經沒有庫存,前置攔截生效,減少數據庫寫。畢竟讀操作不涉及加鎖,併發性能高。

2 數據庫表

2.1 庫存表

create table t_inventory
(
    sku_id        bigint null comment '商品規格 id',
    leaved_amount int    null comment '剩餘可購買數量'
);

2.2 流水錶

create table t_inventory_flow
(
    id              bigint auto_increment comment '主鍵 id'
        primary key,
    sku_id          int        null comment '商品規格 id',
    order_detail_id mediumtext null comment '訂單明細 id',
    quantity_trade  int        null comment '本次購買扣減的數量'
);

2.3 單條商品的扣減 SQL

update inventory 
set leaved_amount = leaved_amount - #{count} 
where sku_id='123' and leaved_amount >= #{count}

樂觀鎖實現原子性,在 where 條件裏判斷此次購買的數量≤剩餘的數量。在扣減服務的代碼,判斷此 SQL 的返回值,若:

2.4 扣減成功後,記錄扣減的流水,並與訂單明細記錄關聯

  1. 1. 當用戶歸還數量時,需帶回此編號,標識此次返還屬於歷史上的具體哪次扣減。

  2. 2. 進行冪等控制。當用戶調用扣減接口出現超時,因爲用戶不知道是否成功,用此編號進行重試或反查。在重試時,使用此編號進行標識防重

3 數據庫扣減方案:第一次升級

極端例子:秒殺庫存只有 5 件,活動期間峯值 QPS 10W,活動結束後,上面的流水錶最終只會插入 5 條記錄,但查詢 QPS 10W。

所以,數據庫扣減方案第一次升級主要針對 庫存前置校驗 模塊的優化,作爲前置攔截器,承載流量很大,若將流量全部壓到主庫,很容易把數據壓垮。

考慮數據庫架構升級:

圖片

採用讀寫分離,新增加一套從庫,藉助 MySQL 自帶的數據同步能力。 庫存校驗時讀從庫。

當然,數據同步有時延,從庫數據有滯後性,所以這庫存校驗結果不準確,但能攔截大部分無效流量 。最終能不能成功購買,由主庫的 樂觀扣減 SQL 控制,不會影響最終扣減的準確性。

大大減輕主庫查詢壓力。

4 數據庫扣減方案:第二次升級

引入了從庫,確實能分攤主庫很大一部分壓力,但面對秒殺萬級 QPS,MySQL 的 千級 TPS 支撐不了,需進一步升級讀性能。

圖片

該方案升級後,基本上解決在前置 【庫存校驗】 環節及 【獲取庫存數量接口】 的性能問題,提高系統整體性能。

tips

若併發量還很高,可考慮引入 緩存集羣 ,將不同的 秒殺商品 sku 儘量均勻分佈在多 redis 節點,分攤整體的峯值 QPS 壓力。

5 數據庫方案評價

優點

缺陷

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/Yh_XkOC2zdb8d-VZuYI-Rg