本文來自微信團隊工程師方樂明的技術分享,由InfoQ編輯發布,下文收錄時有修訂和改動。
一、引言
每年節假日,微信紅包的收發數量都會暴漲,尤以除夕為最。如此大規模、高峰值的業務需要,背后需要怎樣的技術支撐?百億級別的紅包規模,如何保證并發性能與資金安全?
本文將為讀者介紹微信百億級別紅包背后的高并發設計實踐,內容包括微信紅包系統的技術難點、解決高并發問題通常使用的方案,以及微信紅包系統的所采用高并發解決方案。
技術交流:
二、分享者
方樂明:現任微信支付應用產品系統負責人,主要從事微信紅包、微信轉賬、微信群收款等支付應用產品的系統設計、可用性提升、高性能解決方案設計等,曾連續多年負責春節微信紅包系統的性能優化與穩定性提升,取得良好的效果。
三、系列文章
? 系列文章目錄:
? 其它相關文章:
四、微信紅包的兩大業務特點
微信紅包(尤其是發在微信群里的紅包,即群紅包),業務形態上很類似網上的普通商品“秒殺”活動。
就像下面這樣:
- 1)用戶在微信群里發一個紅包,等同于是普通商品“秒殺”活動的商品上架;
- 2)微信群里的所有用戶搶紅包的動作,等同于“秒殺”活動中的查詢庫存;
- 3)用戶搶到紅包后拆紅包的動作,則對應“秒殺”活動中用戶的“秒殺”動作。
不過除了上面的相同點之外,微信紅包在業務形態上與普通商品“秒殺”活動相比,還具備自身的特點。
首先:微信紅包業務比普通商品“秒殺”有更海量的并發要求。
微信紅包用戶在微信群里發一個紅包,等同于在網上發布一次商品“秒殺”活動。假設同一時間有 10 萬個群里的用戶同時在發紅包,那就相當于同一時間有 10 萬個“秒殺”活動發布出去。10 萬個微信群里的用戶同時搶紅包,將產生海量的并發請求。
其次:微信紅包業務要求更嚴格的安全級別。
微信紅包業務本質上是資金交易。微信紅包是微信支付的一個商戶,提供資金流轉服務。
用戶發紅包時,相當于在微信紅包這個商戶上使用微信支付購買一筆“錢”,并且收貨地址是微信群。當用戶支付成功后,紅包“發貨”到微信群里,群里的用戶拆開紅包后,微信紅包提供了將“錢”轉入折紅包用戶微信零錢的服務。
資金交易業務比普通商品“秒殺”活動有更高的安全級別要求。普通的商品“秒殺”商品由商戶提供,庫存是商戶預設的,“秒殺”時可以允許存在“超賣”(即實際被搶的商品數量比計劃的庫存多)、“少賣”(即實際被搶的商戶數量比計劃的庫存少)的情況。但是對于微信紅包,用戶發 100 元的紅包絕對不可以被拆出 101 元;用戶發 100 元只被領取 99 元時,剩下的 1 元在 24 小時過期后要精確地退還給發紅包用戶,不能多也不能少。
以上是微信紅包業務模型上的兩大特點。
五、 微信紅包系統的技術難點
在介紹微信紅包系統的技術難點之前,先介紹下簡單的、典型的商品“秒殺”系統的架構設計,如下圖所示。

該系統由接入層、邏輯服務層、存儲層與緩存構成:
- 1)Proxy 處理請求接入;
- 2)Server 承載主要的業務邏輯;
- 3)Cache 用于緩存庫存數量;
- 4)DB 則用于數據持久化。
一個“秒殺”活動,對應 DB 中的一條庫存記錄。當用戶進行商品“秒殺”時,系統的主要邏輯在于 DB 中庫存的操作上。
一般來說,對 DB 的操作流程有以下三步:
其中,鎖庫存是為了避免并發請求時出現“超賣”情況。同時要求這三步操作需要在一個事務中完成(所謂的事務,是指作為單個邏輯工作單元執行的一系列操作,要么完全地執行,要么完全地不執行)。
“秒殺”系統的設計難點就在這個事務操作上。商品庫存在 DB 中記為一行,大量用戶同時“秒殺”同一商品時,第一個到達 DB 的請求鎖住了這行庫存記錄。在第一個事務完成提交之前這個鎖一直被第一個請求占用,后面的所有請求需要排隊等待。同時參與“秒殺”的用戶越多,并發進 DB 的請求越多,請求排隊越嚴重。因此,并發請求搶鎖,是典型的商品“秒殺”系統的設計難點。
微信紅包業務相比普通商品“秒殺”活動,具有海量并發、高安全級別要求的特點。
在微信紅包系統的設計上,除了并發請求搶鎖之外,還有以下兩個突出難點:
首先,事務級操作量級大:
上文介紹微信紅包業務特點時提到,普遍情況下同時會有數以萬計的微信群在發紅包。這個業務特點映射到微信紅包系統設計上,就是有數以萬計的“并發請求搶鎖”同時在進行。這使得 DB 的壓力比普通單個商品“庫存”被鎖要大很多倍;
其次,事務性要求嚴格:
微信紅包系統本質上是一個資金交易系統,相比普通商品“秒殺”系統有更高的事務級別要求。
六、解決高并發問題通常使用的方案
普通商品“秒殺”活動系統,解決高并發問題的方案,大體有以下幾種。
6.1方案一:使用內存操作替代實時的 DB 事務操作
如圖 2 所示,將“實時扣庫存”的行為上移到內存 Cache 中操作,內存 Cache 操作成功直接給 Server 返回成功,然后異步落 DB 持久化。

這個方案的優點是用內存操作替代磁盤操作,提高了并發性能。
但是缺點也很明顯,在內存操作成功但 DB 持久化失敗,或者內存 Cache 故障的情況下,DB 持久化會丟數據,不適合微信紅包這種資金交易系統。
6.2方案二:使用樂觀鎖替代悲觀鎖
所謂悲觀鎖,是關系數據庫管理系統里的一種并發控制的方法。它可以阻止一個事務以影響其他用戶的方式來修改數據。如果一個事務執行的操作對某行數據應用了鎖,那只有當這個事務把鎖釋放,其他事務才能夠執行與該鎖沖突的操作。對應于上文分析中的“并發請求搶鎖”行為。
所謂樂觀鎖,它假設多用戶并發的事務在處理時不會彼此互相影響,各事務能夠在不產生鎖的情況下處理各自影響的那部分數據。在提交數據更新之前,每個事務會先檢查在該事務讀取數據后,有沒有其他事務又修改了該數據。如果其他事務有更新的話,正在提交的事務會進行回滾。
商品“秒殺”系統中,樂觀鎖的具體應用方法,是在 DB 的“庫存”記錄中維護一個版本號。在更新“庫存”的操作進行前,先去 DB 獲取當前版本號。在更新庫存的事務提交時,檢查該版本號是否已被其他事務修改。如果版本沒被修改,則提交事務,且版本號加 1;如果版本號已經被其他事務修改,則回滾事務,并給上層報錯。
這個方案解決了“并發請求搶鎖”的問題,可以提高 DB 的并發處理能力。
但是如果應用于微信紅包系統,則會存在下面三個問題:
- 1)如果拆紅包采用樂觀鎖:那么在并發搶到相同版本號的拆紅包請求中,只有一個能拆紅包成功,其他的請求將事務回滾并返回失敗,給用戶報錯,用戶體驗完全不可接受;
- 2)如果采用樂觀鎖:將會導致第一時間同時拆紅包的用戶有一部分直接返回失敗,反而那些“手慢”的用戶,有可能因為并發減小后拆紅包成功,這會帶來用戶體驗上的負面影響;
- 3)如果采用樂觀鎖的方式:會帶來大數量的無效更新請求、事務回滾,給 DB 造成不必要的額外壓力。
基于以上原因,微信紅包系統不能采用樂觀鎖的方式解決并發搶鎖問題。
七、微信紅包系統的高并發解決方案
綜合上面的分析,微信紅包系統針對相應的技術難點,采用了下面幾個方案,解決高并發問題。
7.1系統垂直 SET 化,分而治之
微信紅包用戶發一個紅包時,微信紅包系統生成一個 ID 作為這個紅包的唯一標識。接下來這個紅包的所有發紅包、搶紅包、拆紅包、查詢紅包詳情等操作,都根據這個 ID 關聯。
紅包系統根據這個紅包 ID,按一定的規則(如按 ID 尾號取模等),垂直上下切分。切分后,一個垂直鏈條上的邏輯 Server 服務器、DB 統稱為一個 SET。
各個 SET 之間相互獨立,互相解耦。并且同一個紅包 ID 的所有請求,包括發紅包、搶紅包、拆紅包、查詳情詳情等,垂直 stick 到同一個 SET 內處理,高度內聚。通過這樣的方式,系統將所有紅包請求這個巨大的洪流分散為多股小流,互不影響,分而治之,如下圖所示。

這個方案解決了同時存在海量事務級操作的問題,將海量化為小量。
7.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 的“并發搶鎖”情況。
7.3雙維度庫表設計,保障系統性能穩定
紅包系統的分庫表規則,初期是根據紅包 ID 的 hash 值分為多庫多表。隨著紅包數據量逐漸增大,單表數據量也逐漸增加。而 DB 的性能與單表數據量有一定相關性。當單表數據量達到一定程度時,DB 性能會有大幅度下降,影響系統性能穩定性。采用冷熱分離,將歷史冷數據與當前熱數據分開存儲,可以解決這個問題。
處理微信紅包數據的冷熱分離時,系統在以紅包 ID 維度分庫表的基礎上,增加了以循環天分表的維度,形成了雙維度分庫表的特色。
具體來說,就是分庫表規則像 db_xx.t_y_dd 設計,其中,xx/y 是紅包 ID 的 hash 值后三位,dd 的取值范圍在 01~31,代表一個月天數最多 31 天。
通過這種雙維度分庫表方式,解決了 DB 單表數據量膨脹導致性能下降的問題,保障了系統性能的穩定性。同時,在熱冷分離的問題上,又使得數據搬遷變得簡單而優雅。
綜上所述:微信紅包系統在解決高并發問題上的設計,主要采用了 SET 化分治、請求排隊、雙維度分庫表等方案,使得單組 DB 的并發性能提升了 8 倍左右,取得了很好的效果。
八、本文小結
微信紅包系統是一個高并發的資金交易系統,最大的技術挑戰是保障并發性能與資金安全。
這種全新的技術挑戰,傳統的“秒殺”系統設計方案已不能完全解決。在分析了業界“秒殺”系統解決方案的基礎上,微信紅包采用了 SET 化、請求排隊串行化、雙維度分庫表等設計,形成了獨特的高并發、資金安全系統解決方案,并在平時節假日、春節紅包雨實踐中充分證明了可行性,取得了顯著的效果。以2017 雞年除夕夜為例,微信紅包收發峰值達到 76 萬每秒,收發微信紅包 142 億個,微信紅包系統的表現穩定,實現了除夕夜系統零故障。
九、更多鵝廠技術文章匯總
《微信朋友圈千億訪問量背后的技術挑戰和實踐總結》
《騰訊技術分享:騰訊是如何大幅降低帶寬和網絡流量的(圖片壓縮篇)》
《騰訊技術分享:騰訊是如何大幅降低帶寬和網絡流量的(音視頻技術篇)》
《IM全文檢索技術專題(二):微信移動端的全文檢索多音字問題解決方案》
《騰訊技術分享:Android版手機QQ的緩存監控與優化實踐》
《微信團隊分享:iOS版微信的高性能通用key-value組件技術實踐》
《微信團隊分享:iOS版微信是如何防止特殊字符導致的炸群、APP崩潰的?》
《騰訊技術分享:Android手Q的線程死鎖監控系統技術實踐》
《微信團隊原創分享:iOS版微信的內存監控系統技術實踐》
《讓互聯網更快:新一代QUIC協議在騰訊的技術實踐分享》
(本文已同步發布于:http://www.52im.net/thread-2548-1-1.html)