高并發秒殺方案:熱點散列,庫存分桶,你需要了解一下
在大規模秒殺活動中,針對單一商品的庫存扣減請求峰值可以輕松達到幾萬、甚至幾十萬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扣減對應緩存桶中的庫存。
熱點散列的核心思想為:在緩存中扣減庫存,以提升系統的吞吐量;緩存扣減成功后,異步向數據庫寫入庫存扣減流水并更新庫存;此外,還需要通過定時任務等機制實現緩存與數據庫的庫存總量同步。
這個方案看上去很不錯,但也會存在如下三個問題:
一、在緩存中扣減庫存如何保證冪等性呢?若冪等性防控不足,則可能出現重復扣減,進而導致少賣。
二、緩存寫操作和數據庫寫操作無法通過事務機制來保證強一致性,那么該如何有效的保證庫存數據的一致性呢?
三、用戶所見的庫存應為總庫存,即便總庫存充足,一旦分桶,如何保證用戶請求被路由到的分桶油足夠的庫存呢?
所以個人覺得,熱點散列在理論上是解決庫存熱點問題最有效的方案,但在實際應用中,需要考慮的細節非常多。基于緩存的庫存扣減的方案是比較粗糙的,它只滿足一些特定場景的需要。對于淘寶、京東這類在想商品規模達數十億的大型電商平臺而言,所面臨的問題要復雜得多,除了穩定性、可靠性、一致性,還包括庫存分配,庫存碎片,庫存擴縮容、流量傾斜、商品少賣、商品超賣等。
上次去阿里交流的時候,阿里專家唐三說:在阿里,庫存架構采用的方案是做庫存單元化架構,這種方案應該是大型電商平臺庫存系統的終極解決方案。