【高併發寫】庫存系統設計

架構師修行手冊發表於2023-11-29


來源:JavaEdge


【高併發寫】庫存系統設計

當 DoorDash 從訂購餐飲向便利店和雜貨(CnG)業務轉型時,他們必須找到一種方法來管理每個商戶每個店鋪的線上庫存,從幾十種商品增加到數萬種商品。

為了解決這個擴充套件問題,他們的團隊構建了一個高寫入量的庫存平臺,它將能夠跟上平臺上的所有更改。

0 大綱

  1. 支援 CnG 庫存管理的挑戰
  2. 他們理想庫存平臺的技術需求
  3. 功能架構
  4. MVP 後對解決方案的增量更改 —— 將單個商品 API 更改為批次 API —— 資料庫表最佳化 —— 在一個請求中批次上傳 CockroachDB 的資料庫
  5. 結論

1 支援 CnG 庫存系統的挑戰

DoorDash 每天以三種不同方式多次重新整理 CnG 商戶的庫存:

  • 透過攝入從商戶接收的平面庫存檔案自動更新
  • 他們的運營團隊透過內部工具載入庫存資料
  • 透過在 CnG 商店購物的 Dash 運送應用中的訊號更新庫存

由於 CnG 商店的數量數萬家,且每家商店可能包含數萬種商品,重新整理可能每天涉及超過 10 億件商品。

2 期望的庫存平臺的技術需求

2.1 高可擴充套件性

隨著他們的業務增長,庫存平臺需要支援更多新增到系統中的商品。需要支援頻繁的更新,以保持庫存的新鮮度

2.2 高可靠性

流水線應該可靠,以便所有來自商戶的有效庫存更新請求最終都能成功處理

2.3 低延遲

商品資料非常敏感,特別是價格和可用性屬性。從獲取商業資料到向客戶顯示資料之間的時間間隔應儘可能小。

2.4 高可觀察性

流水線應具有大量驗證和防護欄。

3 功能架構

從他們的庫存攝入管道的高階體系結構開始。

下圖顯示他們庫存攝入流水線的頂層設計,一個非同步系統,從多個不同來源攝入庫存,對其進行處理並傳遞給下游系統,在那裡為面向客戶的實體提供檢視。

【高併發寫】庫存系統設計
  1. API controller,基於 gRPC 的 API 控制器,充當平臺上庫存資料的入口點。
  2. Raw Feed Persistence —— API 控制器之後的大部分庫存處理都是非同步的,並透過 Cadence 工作流執行的。
  3. Hydration—— 商店商品的詳細檢視涉及庫存和目錄屬性。DoorDash 的庫存攝入管道負責給(即富集)原始庫存資訊新增目錄屬性。
  4. 價格計算 —— 他們還依賴從依賴服務獲取的外部配置按需執行每件商品的價格計算。
  5. 無庫存預測分類 —— 預測模型,透過學習歷史訂單和 INF(商品未找到)資料,對商品是否可以在店內提供進行分類。
  6. Guardrails—— 沒有管道不會由於程式碼錯誤在他們自己的系統和/或上游系統中的問題而導致錯誤。當滿足某些條件時,庫存平臺需要建立盡力而為的防護欄(和報警機制)來檢測和限制更新。
  7. 可觀察性 —— 在商品層面及商店層面(聚合統計資料)都能完全看到此管道非常重要。我們需要知道是否由於管道中的某些錯誤而丟棄了某個商品,因為這直接與商品在商店頁面上不可用有關。
  8. 可靠性 —— 由於大量的計算和依賴服務,他們的庫存管道需要是非同步的。Cadence 是一種無故障和有狀態的工作流編排器,滿足了他們的這一職責。

4  MVP 後的解決方案的增量更改

4.1 將單個商品 API 更改為批次 API

  • MVP 版本,構建了一個單個商品的 API,要建立/更新一個商品,呼叫者需要呼叫他們的 API 一次。
  • 如果一個商店有 N 個商品,呼叫者將需要呼叫 N 次 API,這可並行發生
  • 讓我們再次考慮用例:當他們更新一個商店時,呼叫者已經知道完整的商品列表,他們可以透過一次 API 呼叫傳送完整的商品列表。最常見的用例將使其可以批次商品並在一次請求中將它們傳送到他們的服務。他們的服務可以將有效負載儲存到 S3 並透過 Cadence 作業非同步消耗它。
【高併發寫】庫存系統設計

改為批次 API 後,觀察到處理速度有所改進,但仍需達期望水平。

4.2 資料庫表最佳化

隨著他們在每個步驟上新增更多指標,他們發現資料庫訪問是一個重要的瓶頸:

  • 選擇自然主鍵而不是自動遞增主鍵 —自然複合鍵幫助他們更有效減少列和查詢
  • 清理資料庫索引 —為所有查詢新增缺失的索引並刪除不必要索引
  • 減少列數 —表最初有約 40 列,大多數情況,所有列都可同時更新。因此,他們決定將一些頻繁更新列放入一個 JSONB 列
  • 為快速增長的表配置TTL — 為保持資料庫容量和後續查詢負載在可控範圍,確定了一些高強度寫入的表,這些表不需要儲存太長時間資料,並在 CockroachDB 中為這些表新增TTL配置
  • 資料庫和依賴檢索邏輯從商品級別修改為商店級別 —要更新一個商品,需從商店級別和商品級別獲取大量資訊,如商店級通貨膨脹率和商品級目錄資料。選擇按需獲取該資訊,因為正在處理每個商品。透過這樣做,他們可以為下游服務和資料庫節省大量 QPS,併為他們的系統以及他們的系統改善效能
【高併發寫】庫存系統設計

4.3 在一個請求中將資料庫插入批次化到CockroachDB

每次完成商品級處理後,都透過使用單商品插入將結果儲存到資料庫中——這在資料庫中造成非常高 QPS。

與儲存團隊討論後,建議批次 SQL 請求。因此調整體系結構:

  • 在完成每個商品處理後,收集結果並將其儲存在程式的記憶體
  • 然後將查詢聚合為每批 1,000 個,並在一個 SQL 請求中傳送批處理
【高併發寫】庫存系統設計

修改查詢重寫後,觀察到應用層和儲存層的服務效能顯著提高:

  • 每件商品的處理時間減少了 75%
  • 儲存 QPS 下降 99%
  • 儲存 CPU 利用率下降

5 總結

  • 構建和擴充套件數字庫存很難,因為數字庫存的資料大小可能巨大,同時它需要準確提供正確的實時庫存檢視
  • 而且它對時間也很敏感,因為一獲得商品資訊我們就需要向客戶顯示商品的正確價格和可用性

主要:

  • 在實現的開始,努力建立一個詳盡的指標監控皮膚,以便在出現效能問題時,可輕鬆縮小系統的瓶頸。通常,從一開始就可以對實時系統具有高可見性非常有用
  • 可幫助讀寫模式的資料方式儲存資料。庫存資料可能不是扁平的資料列表 —— 它們可能具有一定級別層次結構。它們可儲存為商品級別或商店級,這完全取決於確定服務的讀寫模式
  • 儘可能設計批次 API 和 DB。大多情況下,更新庫存時,我們會更新一整個商店或地理位置的庫存。無論哪種,都有多個要更新的商品,所以最好嘗試批次更新而非每個請求或查詢更新單個商品
  • 若業務部門允許非同步處理,使計算非同步化,併為每個單元(商店或商品)建立強大SLA。單個商品處理時間包括花費在網路通訊上的時間,當有數十億商品要處理時這些時間會積累。
  • 相反,若我們透過一個請求傳送整個商店的庫存,並在伺服器端使用 blob 儲存儲存請求有效負載並非同步處理,則客戶端可節省等待時間,服務能具有高吞吐量
  • 從這角度看,還建立了內容將在近實時而非實時更新的想法。Cadence 是處理近實時作業的好工具,並具有許多內建功能來改進系統可靠性和效率。

參考:


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70027824/viewspace-2997844/,如需轉載,請註明出處,否則將追究法律責任。

相關文章