[轉錄]如何設計一個小而美的秒殺系統? 作者-劉鵬
如何設計一個小而美的秒殺系統
作者 劉鵬 發佈於 2017年3月7日
現如今,春節搶紅包的活動已經逐漸變成大家過年的新風俗。親朋好友的相互饋贈,微信、微博、支付寶等各大平臺種類繁多的紅包讓大家收到手軟。雞年春節,鏈家也想給15萬的全國員工包個大紅包,於是我們構建了一套旨在支撐10萬每秒請求峰值的搶紅包系統。經實踐證明,春節期間我們成功的為所有的小夥伴提供了高可靠的服務,紅包總發放量近百萬,搶紅包的峰值流量達到3萬/秒,最快的一輪搶紅包活動3秒鐘所有紅包全部搶完,系統運行0故障。
紅包系統,類似于電商平臺的秒殺系統,本質上都是在一個很短的時間內面對巨大的請求流量,將有限的庫存商品分發出去,並完成交易操作。比如12306搶票,庫存的火車票是有限的,但暫態的流量非常大,且都是在請求相同的資源,這裡面資料庫的併發讀寫衝突以及資源的鎖請求衝突非常嚴重。就我們實現這樣一個紅包系統本身來說,面臨著如下的一些挑戰:
首先,到活動整點時刻,我們有15萬員工在固定時間點同時湧入系統搶某輪紅包,瞬間的流量是很大的,而目前我們整個鏈路上的系統和服務基礎設施,都沒有承受過如此高的輸送量,要在短時間內實現業務需求,在技術上的風險較大。
其次,公司是第一次開展這樣的活動,我們很難預知大家參與活動的情況,極端情況下可能會出現某輪紅包沒搶完,需要合併到下輪接著發放。這就要求系統有一個動態的紅包發放策略和預算控制,其中涉及到的動態計算會是個較大的問題(這也是為系統高吞吐服務),實際的系統實現中我們採用了一些預處理機制。
最後,這個系統是為了春節的慶祝活動而研發的定制系統,且只上線運行一次,這意味著我們無法積累經驗去對服務做持續的優化。並且相關的配套環境沒有經過實際運行檢驗,缺少參考指標,系統的薄弱環節發現的難度大。所以必須要追求設計至簡,儘量減少對環境的依賴(資料路徑越長,出問題的環節越多),並且實現高可伸縮性,需要盡一切努力保證可靠性,即使有某環節失誤,系統依然能夠保障核心的用戶體驗正常。
系統設計
紅包本身的資訊通過預處理資源介面獲取。運行中使用者和紅包的映射關係動態生成。底層使用內部開發的DB中介軟體在MySQL資料庫集群上做紅包發放結果持久化,以供非同步支付紅包金額到使用者帳戶使用。整個系統的絕大部分模組都有性能和保活監控。系統架構圖如圖所示。所有的靜態資源提前部署在了協力廠商的CDN服務上,系統的核心功能主要劃分到接入層和核心邏輯系統中,各自部署為集群模式並且獨立。接入層主要是對用戶身份鑒權和結果緩存,核心系統重點關注紅包的分發,紅色實線的模組是核心邏輯,為了保障其可靠性,我們做了包括資料預處理、水準分庫、多級緩存、精簡RPC調用、超載保護等多項設計優化,並且在原生容器、MySQL等服務基礎設施上針對特殊的業務場景做了優化,後面將為讀者一一道來。
優化方案
優化方案中最重要的目標是保障關鍵流程在應對大量請求時穩定運行,這需要很高的系統可用性。所以,業務流程和資料流程程要儘量精簡,減少容易出錯的環節。此外,緩存、DB、網路、容器環境,任何一個部分都要假設可能會短時出現故障,要有處理預案。針對以上的目標難點,我們總結了如下的實踐經驗。
1.數據預處理
紅包本身的屬性資訊(金額,狀態,祝福語,發放策略),我們結合活動預案要求,使用一定的演算法提前生成好所有的資訊,資料總的空間不是很大。為了最大化提升性能,這些紅包資料,我們事先存儲在資料庫中,然後在容器載入服務啟動時,直接載入到本地緩存中當作唯讀資料。另外,我們的員工資訊,我們也做了一定的裁剪,最基本的資訊也和紅包資料一樣,預先生成,服務啟動時載入。
此外,我們的活動頁面,有很多視頻和圖片資源,如果這麼多的用戶從我們的閘道即時訪問,很可能我們的頻寬直接就被這些大流量的請求占滿了,用戶體驗可想而知。最後這些靜態資源,我們都部署在了CDN上,通過資料預熱的方式加速用戶端的存取速度,閘道的流量主要是來自於搶紅包期間的小資料請求。
2.精簡RPC調用
通常的服務請求流程,是在接入層訪問用戶中心進行用戶鑒權,然後轉發請求到後端服務,後端服務根據業務邏輯調用其他上游服務,並且查詢資料庫資源,再更新服務/資料庫的資料。每一次RPC調用都會有額外的開銷,所以,比如上一點所說的預載入,使得系統在運行期間每個節點都有全量的查詢資料可在本地訪問,搶紅包的核心流程就被簡化為了生成紅包和人的映射關係,以及發放紅包的後續操作。再比如,我們採用了非同步拉的方式進行紅包發放到賬,用戶搶紅包的請求不再經過發放這一步,只記錄關係,性能得到進一步提升。
實際上有些做法的可伸縮性是極強的。例如紅包資料的預生成資訊,在當時的場景下我們是能夠作為本地記憶體緩存加速訪問的。當紅包資料量很大的時候,在每個服務節點上使用本地資料庫,或者本地資料檔案,甚至是本地Redis/MC緩存服務,都是可以保證空間足夠的,並且還有額外的好處,越少的RPC,越少的服務抖動,只需要關注系統本身的健壯性即可,不需要考慮外部系統QoS。
3.搶紅包的併發請求處理
春節整點時刻,同一個紅包會被成千上萬的人同時請求,如何控制併發請求,確保紅包會且僅會被一個用戶搶到?
做法一,使用加鎖操作先佔有鎖資源,再佔有紅包。
可以使用分散式全域鎖的方式(各種分散式鎖元件或者資料庫鎖),申請lock該紅包資源成功後再做後續操作。優點是,不會出現髒資料問題,某一個時刻只有一個應用執行緒持有lock,紅包只會被至多一個使用者搶到,資料一致性有保障。缺點是,所有請求同一時刻都在搶紅包A,下一個時刻又都在搶紅包B,並且只有一個搶成功,其他都失敗,效率很低。
做法二,單獨開發請求排隊調度模組。
排隊模組接收使用者的搶紅包請求,以FIFO模式保存下來,調度模組負責FIFO佇列的動態調度,一旦有空閒資源,便從佇列頭部把使用者的訪問請求取出後交給真正提供服務的模組處理。優點是,具有中心節點的統一資源管理,對系統的可控性強,可深度定制。缺點是,所有請求流量都會有中心節點參與,效率必然會比分散式無中心系統低,並且,中心節點也很容易成為整個系統的性能瓶頸。
做法三,巧用Redis特性,使其成為分散式序號生成器。(我們最終採用的做法)。

4.系统容量评估,借助数据优化,过载保护
5.完善监控
6.服务降级
结束语