揭祕微信紅包架構、搶紅包演算法和高併發和降級方案

五柳-先生發表於2016-05-25

轉自:

http://mp.weixin.qq.com/s?__biz=MzIxMTIwNDcxOA==&mid=2649786999&idx=1&sn=76695da2aae04bff6e746100fe81701d與傳統意義上的紅包相比,近兩年火起來的“紅包”,似乎才是如今春節的一大重頭戲。歷經上千年時代傳承與變遷,春節發紅包早已成為歷史沉澱的文化習俗,融入了民族的血脈。按照各家公佈的資料,除夕全天微信使用者紅包總髮送量達到80.8億個,紅包峰值收發量為40.9萬個/秒。春晚直播期間討論春晚的微博達到5191萬條,網友互動量達到1.15億,網友搶微博紅包的總次數超過8億次。

微信紅包在經過15年春晚搖一搖之後,2015年上半年業務量一度呈指數級增長。尤其是微信紅包活躍使用者數的大量增長,使得2016除夕跨年紅包成為極大挑戰。為了應對16年春節可預知的紅包海量業務,紅包系統在架構上進行了一系列調整和優化。主要包括異地架構、cache系統優化、拆紅包併發策略優化、儲存優化一系列措施,為迎接2016春節紅包挑戰做好準備。 下面介紹最主要的一些思路。

架構

微信使用者在國內有深圳、上海兩個接入點,習慣性稱之為南、北(即深圳為南,上海為北)。使用者請求接入後,不同業務根據業務特性選擇部署方式。微信紅包在資訊流上可以分為訂單緯度與使用者緯度。其中訂單是貫穿紅包發、搶、拆、詳情列表等業務的關鍵資訊,屬於交易類資訊;而使用者緯度指的是紅包使用者的收紅包列表、發紅包列表,屬於展示類資訊。紅包系統在架構上,有以下幾個方面:

南北分佈

1、訂單層南北獨立體系,資料不同步

使用者就近接入,請求發紅包時分配訂單南北,並在單號打上南北標識。搶紅包、拆紅包、查紅包詳情列表時,接入層根據紅包單號上的南北標識將流量分別引到南北系統閉環。根據發紅包使用者和搶紅包使用者的所屬地不同,有以下四種情況:

1) 深圳使用者發紅包,深圳使用者搶

訂單落在深圳,深圳使用者搶紅包時不需要跨城,在深圳完成閉環。

2) 深圳使用者發紅包,上海使用者搶

訂單落在深圳,上海使用者搶紅包,在上海接入後通過專線跨城到深圳,最後在深圳閉環完成搶紅包。

3) 上海使用者發紅包,上海使用者搶

訂單落在上海,上海使用者搶紅包時不需要跨城,在上海完成閉環。

4) 上海使用者發紅包,深圳使用者搶

訂單落在上海,深圳使用者搶紅包,從深圳接入後通過專線跨城到上海,最後在上海閉環完成搶紅包。

系統這樣設計,好處是南北系統分攤流量,降低系統風險。


2、使用者資料寫多讀少,全量存深圳,非同步佇列寫入,查時一邊跨城

使用者資料的查詢入口,在微信錢包中,隱藏的很深。這決定了使用者資料的訪問量不會太大,而且也被視為可旁路的非關鍵資訊,實時性要求不高。因此,只需要在發紅包、拆紅包時,從訂單緯度拆分出使用者資料寫入請求,由MQ非同步寫入深圳。後臺將訂單與使用者進行定時對賬保證資料完整性即可。

3、支援南北流量靈活調控

紅包系統南北分佈後,訂單落地到深圳還是上海,是可以靈活分配的,只需要在接入層上做邏輯。例如,可以在接入層中,實現讓所有紅包請求,都落地到深圳(無論使用者從上海接入,還是深圳接入),這樣上海的紅包業務系統將不會有請求量。提升了紅包系統的容災能力。同時,實現了接入層上的後臺管理系統,實現了秒級容量調控能力。可根據南北請求量的實時監控,做出對應的調配。

4、DB故障時流量轉移能力
基於南北流量的調控能力,當發現DB故障時,可將紅包業務流量調到另外一邊,實現DB故障的容災。

預訂單

支付前訂單落cache,同時利用cache的原子incr操作順序生成紅包訂單號。優點是cache的輕量操作,以及減少DB廢單。在使用者請求發紅包與真正支付之間,存在一定的轉化率,部分使用者請求發紅包後,並不會真正去付款。

拆紅包入賬非同步化

資訊流與資金流分離。拆紅包時,DB中記下拆紅包憑證,然後非同步佇列請求入賬。入賬失敗通過補償佇列補償,最終通過紅包憑證與使用者賬戶入賬流水對賬,保證最終一致性。


這個架構設計,理論基礎是快慢分離。紅包的入賬是一個分佈事務,屬於慢介面。而拆紅包憑證落地則速度快。實際應用場景中,使用者搶完紅包,只關心詳情列表中誰是“最佳手氣”,很少關心搶到的零是否已經到賬。因為只需要展示使用者的拆紅包憑證即可。

發拆落地,其他操作雙層cache

1、Cache住所有查詢,兩層cache

除了使用ckv做全量快取,還在資料訪問層dao中增加本機記憶體cache做二級快取,cache住所有讀請求。

查詢失敗或者查詢不存在時,降級記憶體cache;記憶體cache查詢失敗或記錄不存在時降級DB。

DB本身不做讀寫分離。

2、DB寫同步cache,容忍少量不一致
DB寫操作完成後,dao中同步記憶體cache,業務服務層同步ckv,失敗由非同步佇列補償,定時的ckv與DB備機對賬,保證最終資料一致。

高併發

微信紅包的併發挑戰,主要在於微信大群,多人同時搶同一個紅包。這種情況,存在競爭MySQL行鎖。為了控制這種併發,團隊做了以下一些事情:

1、請求按紅包訂單路由,邏輯塊垂直sticky,事務隔離

按紅包訂單劃分邏輯單元,單元內業務閉環。服務rpc呼叫時,使用紅包訂單號的hash值為key尋找下一跳地址。對同一個紅包的所有拆請求、查詢請求,都路由到同一臺邏輯機器、同一臺DB中處理。


2、Dao搭建本機Memcache記憶體cache,控制同一紅包併發個數

在DB的接入機dao中,搭建本機記憶體cache。以紅包訂單號為key,對同一個紅包的拆請求做原子計數,控制同一時刻能進DB中拆紅包的併發請求數。

這個策略的實施,依賴於請求路由按紅包訂單hash值走,確保同一紅包的所有請求路由到同一邏輯層機器。

3、多層級併發量控制
1) 發紅包控制

發紅包是業務流程的入口,控制了這裡的併發量,代表著控制了紅包業務整體的併發量。在發紅包的業務鏈路裡,做了多層的流量控制,確保產生的有效紅包量級在可控範圍。

2) 搶紅包控制

微信紅包領取時分為兩個步驟,搶和拆。搶紅包這個動作本身就有控制拆併發的作用。因為搶紅包時,只需要查cache中的資料,不需要請求DB。對於紅包已經領完、使用者已經領過、紅包已經過期等流量可以直接攔截。而對於有資格進入拆紅包的請求量,也做流量控制。通過這些處理,最後可進入拆環節的流量大大減少,並且都是有效請求。

3) 拆時記憶體cache控制

針對同一個紅包併發拆的控制,上文已經介紹。

4、DB簡化和拆分

DB的併發能力,有很多影響因素。紅包系統結合紅包使用情境,進行了一些優化。比較有借鑑意義的,主要有以下兩點:

1) 訂單表只存關鍵欄位,其他欄位只在cache中儲存,可柔性。

紅包詳情的展示中,除了訂單關鍵資訊(使用者、單號、金額、時間、狀態)外,還有使用者頭像、暱稱、祝福語等欄位。這些欄位對交易來說不是關鍵資訊,卻佔據大量的儲存空間。

將這些非關鍵資訊拆出來,只存在cache,使用者查詢展示,而訂單中不落地。這樣可以維持訂單的輕量高效,同時cache不命中時,又可從實時介面中查詢補償,達到優化訂單DB容量的效果。

2) DB雙重緯度分庫表,冷熱分離

使用訂單hash、訂單日期,兩個緯度分庫表,也即db_xxx.t_x_dd這樣的格式。其中,x表示訂單hash值,dd表示01-31迴圈日。訂單hash緯度,是為了將訂單打散到不同的DB伺服器中,均衡壓力。訂單日期迴圈日緯度,是為了避免單表資料無限擴張,使每天都是一張空表。

另外,紅包的訂單訪問熱度,是非常典型的冷熱型。熱資料集中在一兩天內,且隨時間急劇消減。線上熱資料庫只需要存幾天的資料,其他資料可以定時移到成本低的冷資料庫中。迴圈日表也使得歷史資料的遷移變得方便。

紅包演算法

首先,如果紅包只有一個,本輪直接使用全部金額,確保紅包發完。

然後,計算出本輪紅包最少要領取多少,才能保證紅包領完,即本輪下水位;輪最多領取多少,才能保證每個人都領到,即本輪上水位。主要方式如下:

計算本輪紅包金額下水位:假設本輪領到最小值1分,那接下來每次都領到200元紅包能領完,那下水位為1分;如果不能領完,那按接下來每次都領200元,剩下的本輪應全部領走,是本輪的下水位。

計算本輪紅包上水位:假設本輪領200元,剩下的錢還足夠接下來每輪領1分錢,那本輪上水位為200元;如果已經不夠領,那按接下來每輪領1分,計算本輪的上水位。

為了使紅包金額不要太懸殊,使用紅包均值調整上水位。如果上水位金額大於兩倍紅包均值,那麼使用兩倍紅包均值作為上水位。換句話說,每一輪搶到的紅包金額,最高為兩倍剩下紅包的均值。

最後,獲取隨機數並用上水位取餘,如果結果比下水位還小,則直接使用下水位,否則使用隨機金額為本輪拆到金額。

柔性降級方案

系統到處存在發生異常的可能,需要對所有的環節做好應對的預案。下面列舉微信紅包對系統異常的主要降級考慮。

1、 下單cache故障降級DB

下單cache有兩個作用,生成紅包訂單與訂單快取。快取故障情況下,降級為直接落地DB,並使用id生成器獨立生成訂單號。

2、 搶時cache故障降級DB

搶紅包時,查詢cache,攔截紅包已經搶完、使用者已經搶過、紅包已經過期等無效請求。當cache故障時,降級DB查詢,同時開啟DB限流保護開關,防止DB壓力過大導致服務不可用。

另外,cache故障降級DB時,DB不儲存使用者頭像、使用者暱稱等(上文提到的優化),此時一併降級為實時介面查詢。查詢失敗,繼續降級為展示預設頭像與暱稱。

3、 拆時資金入賬多級柔性

拆紅包時,DB記錄拆紅包單據,然後執行資金轉賬。單據需要實時落地,而資金轉賬,這裡做了多個層級的柔性降級方案:

大額紅包實時轉賬,小額紅包入佇列非同步轉賬 所有紅包進佇列非同步轉賬 實時流程不執行轉賬,事後憑單據批量入賬。

總之,單據落地後,真實入賬可實時、可非同步,最終保證一致即可。

4、 使用者列表降級

使用者列表資料在微信紅包系統中,屬於非關鍵路徑資訊,屬於可被降級部分。

首先,寫入時通過MQ非同步寫,通過定時對賬保證一致性。

其次,cache中只快取兩屏,使用者查詢超過兩屏則查使用者列表DB。在系統壓力大的情況下,可以限制使用者只查兩屏。

調整後的系統經過了16年春節的實踐檢驗,平穩地度過了除夕業務高峰,保障了紅包使用者的體驗。

相關文章