高併發庫存秒殺場景,阿里巴巴資料庫是這樣應對的

2022-06-06 20:27:12 字數 3078 閱讀 9464

一般來說,從資料庫層面講,庫存業務會分為兩步,第一步是插入一條記錄到扣減明細表inventory_detail,第二步是對庫存扣減表inventory的一條記錄進行扣減,這兩步往往是在乙個事務中實現的。

資料庫業務架構圖如下,所有的請求均發往同乙個database。

從上文的架構圖不難看出,所有的商品的庫存資訊都存在單一的表和庫里,當商品種類繁多或者業務併發請求暴漲時,單例項的資料庫顯然會成為容量或者效能瓶頸。該資料庫架構一般只是功能性的實現,主要用於微型庫存系統或者測試使用。

為了解決單例項存在的容量和效能上限問題,阿里巴巴所有的庫存系統在十年前就實現了分庫分表設計,主要通過資料的水平拆分實現不同商品的庫存扣減請求路由到不同的資料庫。基本資料庫架構圖如下

從上圖不難看出,庫存扣減表和扣減明細表一般都使用商品id作為片鍵,這樣可以保證滿足整個系統在高併發扣減請求的同時,同一商品的庫存扣減操作和新增明細操作在同乙個事務中實現。如果資料分布和業務請求足夠均勻,理論上經過分庫分表設計後,整個系統的吞吐量將會是線性的增長,主要取決於分例項的數量。

在電商業務中,商家活動比如秒殺不可避免。秒殺活動會給電商庫存系統帶來巨大的挑戰,尤其體現在資料庫層面。因為往往乙個商品id對應於資料庫的一行記錄,所以在db架構上按照商品維度做了分庫分表也是無效的。而更新這行記錄時必然需要給這行記錄加x鎖。熱點商品的庫存扣減本質上就是熱點行更新的能力,高併發的同行更新會造成嚴重的行鎖等待現象,從而導致資料庫的threads_running和rt飆公升,造成雪崩。在當前的官方mysql中,一般單行更新的qps在500以內,對於熱點商品的秒殺需求,這個量往往是不達標的。

阿里巴巴polardb-x資料庫團隊基於以上場景的需求,針對核心優化,引入了先進的水車模型,在識別出熱點sql後,實現了在核心層面優化處理,相比官方mysql提高了10倍以上的熱點行扣減能力,廣泛應用於集團電商庫存集群,資金平台,權益發放平台等核心資料庫集群。

其主要的核心思想是:針對應用層sql做輕量化改造,帶上"熱點行sql"的標籤,當這種sql進入核心後,在記憶體中維護乙個hash表,將主鍵或唯一鍵相同的請求(一般也就是同一商品id)hash到同乙個地方做請求的合併,經過一段時間後(預設100us)統一提交,從而實現了將序列處理變成了批處理,讓每個熱點行更新請求並不需要都去掃瞄和更新btree。

類似原理,阿里雲rds資料庫團隊同樣在核心層面針對熱點行更新做了大量的優化,核心思路為引入sql語句的排隊機制,將可能存在行鎖衝突的語句放在乙個組內排序,從而減少行鎖衝突帶來的額外系統開銷。statement queque和inventory hint可以結合使用,不過在事務中,熱點行更新必須是該事務的最後一條記錄,因為commit on success的機制存在,一旦該sql執行成功就會自動提交或自動回滾。簡單的使用範例如下

begin;

insert into inventory_detail(inventory_id,user_id) values(1,1);

update /*+ ccl_queue_value(1) commit_on_success rollback_on_fail target_affect_row(1) */ inventory set inventory_count=inventory_count+1 where inventory_id=1

冪等性實現

在庫存資料庫系統中,一般都會在更新庫存記錄後,寫入一條庫存扣減明細的流水記錄,用於後續可能存在的查詢需求。舉個例子,在集團的權益發放平台中,庫存流水記錄主要用於實現庫存扣減的冪等性,即同乙個使用者只能領取一次權益。在系統的實際執行過程中,可能因為一些網路故障等其他原因,當底層資料庫的扣減成功以後並沒有成功返回給使用者時,使用者可能會有重試操作,這時就必須避免庫存記錄的重複扣減情況。所以針對這些情況,應用在設計時會考慮先查詢一遍庫存流水記錄,如果該使用者已經領取過該權益,則不再重複扣減,直接返回。為了實現這種強冪等性的需求,庫存扣減和插入流水就必須在同乙個事務中,滿足同時成功或同時失敗。

基於快取的分桶扣減方案

在更大規模,針對單一商品的超高併發扣減的庫存集群中,可能基於資料庫核心的改造優化還無法滿足業務需求。單一商品的超高併發扣減可能會影響到同一資料庫例項上的其他商品扣減,同乙個資料庫例項上也可能存在多個熱點商品造成互相影響,這時就需要考慮在業務和資料庫架構上再做一次公升級,我們引入基於快取的分桶扣減方案。

下圖為該方案資料庫架構圖,基於快取的分桶扣減方案的主要思路為

在分桶方案詳細落地實現上,需要考慮的細節問題會多很多,比較重要的有以下幾點

1、分桶管理

為了更通俗和直觀的描述,快取集群的乙個key就對應於於乙個"分桶"。要實現乙個基於快取分桶方案的高擴充套件性的庫存系統,分桶的設計至關重要,比如乙個熱點商品應該對應多少個分桶,分桶的數量能否根據當前的業務變化做到彈性的伸縮

2、超賣問題

乙個較為簡單的處理超賣問題的思路是預留一部分庫存,當庫存數量低於之前定義的預留值時,直接返回前端庫存扣減完畢,從而避免造成超賣。

3、碎片問題

在一些類庫存系統的設計中,考慮到系統的相容性和支援的扣減種類,或許扣減的是商品的庫存數量,或許是紅包的金額(將帶小數的紅包金額轉換成整型數扣減)。所謂碎片問題,舉個例子,假如扣減的是紅包金額,假設紅包金額至少要發1塊錢,換算成整型數也就是100,在多個分桶扣減的情況下,最後部分分桶的剩餘庫存值可能低於100,而所有分桶加起來的總額又大於100。如果不做處理,就會造成資損。

應對這種極端場景,系統需要在檢測到存在碎片時,自動將存在碎片的分桶下線納入庫存總池子,由db總池子再分出少量的快取key來進行扣減,多次迴圈直到不存在碎片為止。或者針對出現這種情況後,由於庫存總量已經基本扣減完畢,在納入db總池子後直接在db側扣減。

阿里巴巴資料庫

開源資料庫 alisql alisql 是基於 mysql 官方版本的乙個分支,由阿里雲資料庫團隊維護,目前也應用於阿里巴巴集團業務以及阿里雲資料庫服務。該版本在社群版的基礎上做了大量的效能與功能的優化改進。尤其適合電商 雲計算以及金融等行業環境。分布式資料庫 oceanbase oceanbase...

阿里巴巴編碼規約學習之MYSQL資料庫

這個怎麼說呢,其實也有用y和n的,甚至有用success的,感覺有點亂,有個統一的標準還是好事,起碼乙個產品或者乙個系列的東西應該在這一點上是統一的 雖然說這個大小寫敏感的配置是可以修改的,但還是統一小寫比較好,不像oracle 注意有些 複數 表名其實是單詞的本義,並不是複數 之前也有用欄位名 u...

Druid 阿里巴巴開發的資料庫連線池

前面有資料庫連線池的一些簡單概念和c3p0的使用基本步驟,有需要的可以去看一下 資料庫連線池 c3p0 druid是由阿里巴巴提供的資料庫連線池,也是我們平時使用的資料庫連線池,其他的也不過多贅述了,基本步驟如下 匯入jar包 druid 1.0.9.jar 定義配置檔案 形式是properties...