微信高併發資金交易系統設計方案——百億紅包背後的技術支撐

FrankYou發表於2017-02-15
 
 
每年節假日,微信紅包的收發數量都會暴漲,尤以除夕為最。如此大規模、高峰值的業務需要,背後需要怎樣的技術支撐?百億級別的紅包規模,如何保證併發效能與資金安全?背景介紹

2017年1月28日,正月初一,微信公佈了使用者在除夕當天收發微信紅包的數量——142億個,而其收發峰值也已達到76萬每秒。百億級別的紅包,如何保障併發效能與資金安全?這給微信帶來了超級挑戰。面對挑戰,微信紅包在分析了業界“秒殺”系統解決方案的基礎上,採用了SET化、請求排隊序列化、雙維度分庫表等設計,形成了獨特的高併發、資金安全系統解決方案。實踐證明,該方案表現穩定,且實現了除夕夜系統零故障執行。

本文將為讀者介紹百億級別紅包背後的系統高併發設計方案,包括微信紅包的兩大業務特點、微信紅包系統的技術難點、解決高併發問題通常使用的方案,以及微信紅包系統的高併發解決方案。

微信紅包的兩大業務特點

微信紅包(尤其是發在微信群裡的紅包,即群紅包)業務形態上很類似網上的普通商品“秒殺”活動。

使用者在微信群裡發一個紅包,等同於是普通商品“秒殺”活動的商品上架;微信群裡的所有使用者搶紅包的動作,等同於“秒殺”活動中的查詢庫存;使用者搶到紅包後拆紅包的動作,則對應“秒殺”活動中使用者的“秒殺”動作。

不過除了上面的相同點之外,微信紅包在業務形態上與普通商品“秒殺”活動相比,還具備自身的特點:

首先,微信紅包業務比普通商品“秒殺”有更海量的併發要求。

微信紅包使用者在微信群裡發一個紅包,等同於在網上釋出一次商品“秒殺”活動。假設同一時間有10萬個群裡的使用者同時在發紅包,那就相當於同一時間有10萬個“秒殺”活動釋出出去。10萬個微信群裡的使用者同時搶紅包,將產生海量的併發請求。

其次,微信紅包業務要求更嚴格的安全級別。

微信紅包業務本質上是資金交易。微信紅包是微信支付的一個商戶,提供資金流轉服務。

使用者發紅包時,相當於在微信紅包這個商戶上使用微信支付購買一筆“錢”,並且收貨地址是微信群。當使用者支付成功後,紅包“發貨”到微信群裡,群裡的使用者拆開紅包後,微信紅包提供了將“錢”轉入折紅包使用者微信零錢的服務。

資金交易業務比普通商品“秒殺”活動有更高的安全級別要求。普通的商品“秒殺”商品由商戶提供,庫存是商戶預設的,“秒殺”時可以允許存在“超賣”(即實際被搶的商品數量比計劃的庫存多)、“少賣”(即實際被搶的商戶數量比計劃的庫存少)的情況。但是對於微信紅包,使用者發100元的紅包絕對不可以被拆出101元;使用者發100元只被領取99元時,剩下的1元在24小時過期後要精確地退還給發紅包使用者,不能多也不能少。

以上是微信紅包業務模型上的兩大特點。

微信紅包系統的技術難點

在介紹微信紅包系統的技術難點之前,先介紹下簡單的、典型的商品“秒殺”系統的架構設計,如下圖所示。

該系統由接入層、邏輯服務層、儲存層與快取構成。Proxy處理請求接入,Server承載主要的業務邏輯,Cache用於快取庫存數量、DB則用於資料持久化。

一個“秒殺”活動,對應DB中的一條庫存記錄。當使用者進行商品“秒殺”時,系統的主要邏輯在於DB中庫存的操作上。一般來說,對DB的操作流程有以下三步:

  1. 鎖庫存

  2. 插入“秒殺”記錄

  3. 更新庫存

其中,鎖庫存是為了避免併發請求時出現“超賣”情況。同時要求這三步操作需要在一個事務中完成(所謂的事務,是指作為單個邏輯工作單元執行的一系列操作,要麼完全地執行,要麼完全地不執行)。

“秒殺”系統的設計難點就在這個事務操作上。商品庫存在DB中記為一行,大量使用者同時“秒殺”同一商品時,第一個到達DB的請求鎖住了這行庫存記錄。在第一個事務完成提交之前這個鎖一直被第一個請求佔用,後面的所有請求需要排隊等待。同時參與“秒殺”的使用者越多,併發進DB的請求越多,請求排隊越嚴重。因此,併發請求搶鎖,是典型的商品“秒殺”系統的設計難點。

微信紅包業務相比普通商品“秒殺”活動,具有海量併發、高安全級別要求的特點。在微信紅包系統的設計上,除了併發請求搶鎖之外,還有以下兩個突出難點:

首先,事務級操作量級大。上文介紹微信紅包業務特點時提到,普遍情況下同時會有數以萬計的微信群在發紅包。這個業務特點對映到微信紅包系統設計上,就是有數以萬計的“併發請求搶鎖”同時在進行。這使得DB的壓力比普通單個商品“庫存”被鎖要大很多倍。

其次,事務性要求嚴格。微信紅包系統本質上是一個資金交易系統,相比普通商品“秒殺”系統有更高的事務級別要求。

解決高併發問題常用方案

普通商品“秒殺”活動系統,解決高併發問題的方案,大體有以下幾種:

 方案一,使用記憶體操作替代實時的DB事務操作。

如圖2所示,將“實時扣庫存”的行為上移到記憶體Cache中操作,記憶體Cache操作成功直接給Server返回成功,然後非同步落DB持久化。

這個方案的優點是用記憶體操作替代磁碟操作,提高了併發效能。

但是缺點也很明顯,在記憶體操作成功但DB持久化失敗,或者記憶體Cache故障的情況下,DB持久化會丟資料,不適合微信紅包這種資金交易系統。

 方案二,使用樂觀鎖替代悲觀鎖。

所謂悲觀鎖,是關聯式資料庫管理系統裡的一種併發控制的方法。它可以阻止一個事務以影響其他使用者的方式來修改資料。如果一個事務執行的操作對某行資料應用了鎖,那只有當這個事務把鎖釋放,其他事務才能夠執行與該鎖衝突的操作。對應於上文分析中的“併發請求搶鎖”行為。

所謂樂觀鎖,它假設多使用者併發的事務在處理時不會彼此互相影響,各事務能夠在不產生鎖的情況下處理各自影響的那部分資料。在提交資料更新之前,每個事務會先檢查在該事務讀取資料後,有沒有其他事務又修改了該資料。如果其他事務有更新的話,正在提交的事務會進行回滾。

商品“秒殺”系統中,樂觀鎖的具體應用方法,是在DB的“庫存”記錄中維護一個版本號。在更新“庫存”的操作進行前,先去DB獲取當前版本號。在更新庫存的事務提交時,檢查該版本號是否已被其他事務修改。如果版本沒被修改,則提交事務,且版本號加1;如果版本號已經被其他事務修改,則回滾事務,並給上層報錯。

這個方案解決了“併發請求搶鎖”的問題,可以提高DB的併發處理能力。

但是如果應用於微信紅包系統,則會存在下面三個問題:

  1. 如果拆紅包採用樂觀鎖,那麼在併發搶到相同版本號的拆紅包請求中,只有一個能拆紅包成功,其他的請求將事務回滾並返回失敗,給使用者報錯,使用者體驗完全不可接受。

  2. 如果採用樂觀鎖,將會導致第一時間同時拆紅包的使用者有一部分直接返回失敗,反而那些“手慢”的使用者,有可能因為併發減小後拆紅包成功,這會帶來使用者體驗上的負面影響。

  3. 如果採用樂觀鎖的方式,會帶來大數量的無效更新請求、事務回滾,給DB造成不必要的額外壓力。

基於以上原因,微信紅包系統不能採用樂觀鎖的方式解決併發搶鎖問題。

微信紅包系統的高併發解決方案

綜合上面的分析,微信紅包系統針對相應的技術難點,採用了下面幾個方案,解決高併發問題。

 1.系統垂直SET化,分而治之。

微信紅包使用者發一個紅包時,微信紅包系統生成一個ID作為這個紅包的唯一標識。接下來這個紅包的所有發紅包、搶紅包、拆紅包、查詢紅包詳情等操作,都根據這個ID關聯。

紅包系統根據這個紅包ID,按一定的規則(如按ID尾號取模等),垂直上下切分。切分後,一個垂直鏈條上的邏輯Server伺服器、DB統稱為一個SET。

各個SET之間相互獨立,互相解耦。並且同一個紅包ID的所有請求,包括髮紅包、搶紅包、拆紅包、查詳情詳情等,垂直stick到同一個SET內處理,高度內聚。通過這樣的方式,系統將所有紅包請求這個巨大的洪流分散為多股小流,互不影響,分而治之,如下圖所示。

這個方案解決了同時存在海量事務級操作的問題,將海量化為小量。

 2.邏輯Server層將請求排隊,解決DB併發問題。

紅包系統是資金交易系統,DB操作的事務性無法避免,所以會存在“併發搶鎖”問題。但是如果到達DB的事務操作(也即拆紅包行為)不是併發的,而是序列的,就不會存在“併發搶鎖”的問題了。

按這個思路,為了使拆紅包的事務操作序列地進入DB,只需要將請求在Server層以FIFO(先進先出)的方式排隊,就可以達到這個效果。從而問題就集中到Server的FIFO佇列設計上。

微信紅包系統設計了分散式的、輕巧的、靈活的FIFO佇列方案。其具體實現如下:

首先,將同一個紅包ID的所有請求stick到同一臺Server。

上面SET化方案已經介紹,同個紅包ID的所有請求,按紅包ID stick到同個SET中。不過在同個SET中,會存在多臺Server伺服器同時連線同一臺DB(基於容災、效能考慮,需要多臺Server互備、均衡壓力)。

為了使同一個紅包ID的所有請求,stick到同一臺Server伺服器上,在SET化的設計之外,微信紅包系統新增了一層基於紅包ID hash值的分流,如下圖所示。

其次,設計單機請求排隊方案。

將stick到同一臺Server上的所有請求在被接收程式接收後,按紅包ID進行排隊。然後序列地進入worker程式(執行業務邏輯)進行處理,從而達到排隊的效果,如下圖所示。

最後,增加memcached控制併發。

為了防止Server中的請求佇列過載導致佇列被降級,從而所有請求擁進DB,系統增加了與Server伺服器同機部署的memcached,用於控制拆同一個紅包的請求併發數。

具體來說,利用memcached的CAS原子累增操作,控制同時進入DB執行拆紅包事務的請求數,超過預先設定數值則直接拒絕服務。用於DB負載升高時的降級體驗。

通過以上三個措施,系統有效地控制了DB的“併發搶鎖”情況。

 3.雙維度庫表設計,保障系統效能穩定

紅包系統的分庫表規則,初期是根據紅包ID的hash值分為多庫多表。隨著紅包資料量逐漸增大,單表資料量也逐漸增加。而DB的效能與單表資料量有一定相關性。當單表資料量達到一定程度時,DB效能會有大幅度下降,影響系統效能穩定性。採用冷熱分離,將歷史冷資料與當前熱資料分開儲存,可以解決這個問題。

處理微信紅包資料的冷熱分離時,系統在以紅包ID維度分庫表的基礎上,增加了以迴圈天分表的維度,形成了雙維度分庫表的特色。

具體來說,就是分庫表規則像db_xx.t_y_dd設計,其中,xx/y是紅包ID的hash值後三位,dd的取值範圍在01~31,代表一個月天數最多31天。

通過這種雙維度分庫表方式,解決了DB單表資料量膨脹導致效能下降的問題,保障了系統效能的穩定性。同時,在熱冷分離的問題上,又使得資料搬遷變得簡單而優雅。

綜上所述,微信紅包系統在解決高併發問題上的設計,主要採用了SET化分治、請求排隊、雙維度分庫表等方案,使得單組DB的併發效能提升了8倍左右,取得了很好的效果。

最後總結

微信紅包系統是一個高併發的資金交易系統,最大的技術挑戰是保障併發效能與資金安全。這種全新的技術挑戰,傳統的“秒殺”系統設計方案已不能完全解決。在分析了業界“秒殺”系統解決方案的基礎上,微信紅包採用了SET化、請求排隊序列化、雙維度分庫表等設計,形成了獨特的高併發、資金安全系統解決方案,並在平時節假日、2015和2016春節實踐中充分證明了可行性,取得了顯著的效果。在剛剛過去的2017雞年除夕夜,微信紅包收發峰值達到76萬每秒,收發微信紅包142億個,微信紅包系統的表現穩定,實現了除夕夜系統零故障。

 

相關文章