高並發秒殺方案:熱點散列,庫存分桶,你需要了解一下

2024.01.31

在大規模秒殺活動中,針對單一商品的庫存扣減請求高峰可以輕鬆達到數萬、甚至數十萬QPS,如常見的搶茅台活動。在這種場景下再基於資料庫進行庫存扣減就顯得無能為力了,記住一個關鍵指標:在MySQL中,目前單行更新操作的表現約為500QPS。對於動輒幾萬QPS的庫存扣減來說,這個量級肯定是偏低了。

所以為了應對這種高並發場景,業界提出了一個方案叫 熱點散列,即今天群組討論的庫存分桶。

其方案如下圖所示:將同一商品的庫存提前分配至多個「桶」中,根據路由規則(隨機、UID取模)將庫存請求路由至不同的桶,從而將集中於單一實例的請求分散,此方案類似於水平擴展。

圖片圖片

至於「分桶」的技術實現,許多技術文章或解決方案都建議採用Redis來實現。具體而言,對任一秒殺活動的商品,可將其分成N份,每份對一個緩存Key,緩存Key的構成必須遵循一定的規律,便於路由,示例如下:

key: inventoryId_1,value: 庫存數量 

key: inventoryId_2, value: 庫存數量 

… 

key: inventoryId_N, value: 庫存數量

扣減庫存時,可依Key的編號區間,採用隨機演算法,UID取模等方式決定一個編號,然後組裝Key存取快取。 

舉個例子,假如inventoryId為20221821,將庫存分配至100個桶,則根據對應Key的區間為[1,100]。

服務端收到商品庫存扣減請求後,將請求中的參數UID取模,假設UID % 100 = 67,則組裝Key為20221821_67,基於20221821_67扣減對應緩存桶中的庫存。

熱點散列的核心概念為:在快取中扣減庫存,以提升系統的吞吐量;緩存扣減成功後,異步向資料庫寫入庫存扣減流水並更新庫存;此外,還需要透過定時任務等機制實現緩存與資料庫的庫存總量同步。

這個方案看起來很不錯,但也會有以下三個問題:

一、在快取中扣減庫存如何確保冪等性?若冪等性防治不足,則可能出現重複扣減,進而導致少賣。

二、快取寫入作業和資料庫寫入作業無法透過事務機制來保證強一致性,那麼該如何有效的保證庫存資料的一致性呢?

三、用戶所見的庫存應為總庫存,即便總庫存充足,一旦分桶,如何保證用戶請求被路由到的分桶油足夠的庫存呢?

所以個人覺得,熱點散列在理論上是解決庫存熱點問題最有效的方案,但在實際應用中,需要考慮的細節非常多。基於快取的庫存扣減的方案是比較粗糙的,它只滿足一些特定場景的需要。對於淘寶、京東這類在想商品規模達數十億的大型電商平台而言,所面臨的問題要複雜得多,除了穩定性、可靠性、一致性,還包括庫存分配,庫存碎片,庫存擴縮容、流量傾斜、商品少賣、商品超賣等。

上次去阿里交流的時候,阿里專家唐三說:在阿里,庫存架構採用的方案是做庫存單元化架構,這種方案應該是大型電商平台庫存系統的終極解決方案。

責任編輯:武曉燕來源: JAVA日知錄