大家好,我是小?,一個漂泊江湖多年的 985 非科班程序員,曾混跡于國企、互聯網大廠和創業公司的后臺開發攻城獅。
當我那天拿著手機,正在和朋友們的微信群里暢聊著八卦新聞和即將到來的周末計劃時,忽然一條帶著喜意的消息撲面而來,消息正中間赫然寫著八個大字:恭喜發財,大吉大利。
圖片
搶紅包!!相信大部分人對此都不陌生,自 2015 年春節以來,微信就新增了各類型搶紅包功能,吸引了數以億萬級的用戶參與體驗,今天,我們就來聊一聊這個奇妙有趣的紅包系統。
圖片
搶紅包系統從功能拆分,可以分為包紅包、發紅包、搶紅包和拆紅包 4 個功能。
對于系統特性來說,搶紅包系統和秒殺系統類似。
圖片
每次發紅包都是一次商品秒殺流程,包括商品準備,商品上架,查庫存、減庫存,以及秒殺開始,最終的用戶轉賬就是紅包到賬的過程。
相比秒殺活動,微信發紅包系統的用戶量更大,設計更加復雜,需要重視的點更多,主要包括以下幾點。
海量并發請求,秒殺只有一次活動,但紅包可能同一時刻有幾十萬個秒殺活動。
比如 2017 雞年除夕,微信紅包搶紅包用戶數高達 3.42 億,收發峰值 76 萬/秒,發紅包 37.77 億 個。
紅包業務涉及資金交易,所以一定不能出現超賣、少賣的情況。
參與用戶越多,并發 DB 請求越大,數據越容易出現事務問題,所以系統得做好事務一致性。
這也是一般秒殺活動的難點所在,而且搶紅包系統涉及金錢交易,所以事務級別要求更高,不能出現臟數據。
搶紅包功能允許用戶在群聊中發送任意個數和金額的紅包,群成員可以搶到隨機金額的紅包,但要保證每個用戶的紅包金額不小于 0.01 元。
圖片
搶紅包的詳細交互流程如下:
紅包表 redpack 的字段如下:
該表用來記錄用戶發了多少紅包,以及需要維護的剩余金額。
紅包記錄表 redpack_record 如下:
記錄表用來存放用戶具體搶到的紅包信息,也是紅包表的副表。
從 2015 年起,微信紅包的搶紅包和拆紅包就分離了,用戶點擊搶紅包后需要進行兩次操作。
這也是為什么明明有時候搶到了紅包,點開后卻發現該紅包已經被領取完了。
圖片
搶紅包的交互步驟如下:
上述流程,在一般的秒殺活動中隨處可見,但是,紅包系統真的有這么簡單嗎?
當用戶量過大時,高并發下的事務一致性怎么保證,數據分流如何處理,紅包的數額分配又是怎么做的,接下來我們一一探討。
由于是秒殺類設計,以及 money 分發,所以我們重點關注搶紅包時的高并發解決方案和紅包分配算法。
首先,搶紅包系統的用戶量很大,如果幾千萬甚至億萬用戶同時在線發搶紅包,請求直接打到數據庫,必然會導致后端服務過載甚至崩潰。
而在這種業務量下,簡單地對數據庫進行擴容不僅會讓成本消耗劇增,另一方面由于存在磁盤的性能瓶頸,所以大概率解決不了問題。
所以,我們將解決方案集中在 減輕系統壓力、提升響應速度 上,接下來會從緩存、加鎖、異步分治等方案來探討可行性。
和大多數秒殺系統設計相似,由于搶紅包時并發很高,如果直接操作 DB 里的數據表,可能觸發 DB 鎖的邏輯,導致響應不及時。
圖片
所以,我們可以在 DB 落盤之前加一層緩存,先限制住流量,再處理紅包訂單的數據更新。
這樣做的優點是用緩存操作替代了磁盤操作,提升了并發性能,這在一般的小型秒殺活動中非常有效!
但是,隨著微信使用發&搶紅包的用戶量增多,系統壓力增大,各種連鎖反應產生后,數據一致性的問題逐漸暴露出來:
而且在幾十萬的并發下,直接對業務加鎖也是不現實的,即便是樂觀鎖。
在關系型 DB 里,有兩種并發控制方法:分為樂觀鎖(又叫樂觀并發控制,Optimistic Concurrency Control,縮寫 “OCC”)和悲觀鎖(又叫悲觀并發,Pessimistic Concurrency Control,縮寫“PCC”)。
圖片
悲觀鎖在操作數據時比較悲觀,認為別的事務可能會同時修改數據,所以每次操作數據時會先把數據鎖住,直到操作完成。
樂觀鎖正好相反,這種策略主打一個“信任”的思想,認為事務之間的數據競爭很小,所以在操作數據時不會加鎖,直到所有操作都完成到提交時才去檢查是否有事務更新(通常是通過版本號來判斷),如果沒有則提交,否則進行回滾。
在高并發場景下,由于數據操作的請求很多,所以樂觀鎖的吞吐量更大一些。但是從業務來看,可能會帶來一些額外的問題:
總的來說,樂觀鎖適用于數據競爭小,沖突較少的業務場景,而悲觀鎖也不適用于高并發場景的數據更新。
因此對于搶紅包系統來說,加鎖是非常不適合的。
綜上所述,搶紅包時不僅要解決高并發問題、還得保障并發的順序性,所以我們考慮從隊列的角度來設計。
我們知道,每次包紅包、發紅包、搶紅包時,也有先后依賴關系,因此我們可以將紅包 ID 作為一個唯一 Key,將發一次紅包看作一個單獨的 set,各個 set 相互獨立處理。
圖片
這樣,我們就把海量的搶紅包系統分成一個個的小型秒殺系統,在調度處理中,通過對紅包 ID 哈希取模,將一個個請求打到多臺服務器上解耦處理。
然后,為了保證每個用戶搶紅包的先后順序,我們把一個紅包相關的操作串行起來,放到一個隊列里面,依次消費。
從上述 set 分流我們可以看出,一臺服務器可能會同時處理多個紅包的操作,所以,為了保證消費者處理 DB 不被高并發打崩,我們還需要在消費隊列時用緩存來限制并發消費數量。
搶紅包業務消費時由于不存儲數據,只是用緩存來控制并發。所以我們可以選用大數據量下性能更好的 Memcached。
除此之外,在數據存儲上,我們可以用紅包 ID 進行哈希分表,用時間維度對 DB 進行冷熱分離,以此來提升單 set 的處理性能。
綜上所述,搶紅包系統在解決高并發問題上采用了 set 分治、串行化隊列、雙維度分庫分表 等方案,使得單組 DB 的并發性能得到了有效提升,在應對數億級用戶請求時取得了良好的效果。
搶紅包后,我們需要進行拆紅包,接下來我們討論一下紅包系統的紅包分配算法。
紅包金額分配時,由于是隨機分配,所以有兩種實現方案:實時拆分和預先生成。
實時拆分,指的是在搶紅包時實時計算每個紅包的金額,以實現紅包的拆分過程。
這個對系統性能和拆分算法要求較高,例如拆分過程要一直保證后續待拆分紅包的金額不能為空,不容易做到拆分的紅包金額服從正態分布規律。
預先生成,指的是在紅包開搶之前已經完成了紅包的金額拆分,搶紅包時只是依次取出拆分好的紅包金額。
這種方式對拆分算法要求較低,可以拆分出隨機性很好的紅包金額,但通常需要結合隊列使用。
綜合上述優缺點考慮,以及微信群聊中的人數不多(目前最高 500 人),所以我們采用實時拆分的方式,用二倍均值法來生成隨機紅包,只滿足隨機即可,不需要正態分布。
故可能出現很大的紅包差額,但這更刺激不是嗎
本文鏈接:http://www.www897cc.com/showinfo-26-46358-0.html聽說你會架構設計?來,弄一個紅包系統
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com
上一篇: 關于響應式布局,你需要了解的知識點