圖解 聊聊「秒殺」

你好,我是悟空呀~

1 需求分析

“秒殺”這個詞在電商行業中出現的頻率較高,如京東或者淘寶平臺的各種 “秒殺” 活動,最典型的就是“雙 11 搶購”。

“秒殺”是指在有限的時間內對有限的商品數量進行搶購的一種行爲,這是商家以 “低價量少” 的商品來獲取用戶的一種營銷手段。

01. 功能性需求

其實,整個秒殺的業務場景並不複雜,可即查看參與秒殺的商品信息,加上購買和支付的動作,如下圖所示。

秒殺業務最大的挑戰在於 3 點:

同時,在保證高併發流量承接的前提下,爲了增強用戶的體驗和活動規則的公平性,以及防止遭到惡意破壞等,特此增加如下需求:

(1)用戶在秒殺頁面無需一直刷新 “搶購” 按鈕,待秒殺活動開始時,按鈕自動點亮。

(2)在公平以及防止惡意破壞的原則下,在下單之前增加驗證碼的錄入,或者答題的相關環節。

(3)庫存不能出現問題,即不多扣也不少扣。

(4)整個秒殺活動過程持續 10 分鐘。

02. 性能指標預估

通過秒殺的需求描述可得出,當前秒殺活動主要需要預估三塊的性能指標:存儲容量、併發量、網絡帶寬。

1)存儲容量

由於是秒殺活動,且參與的商品基本都是低價高性價比的,數量是非常有限的。所以,在訂單存儲上基本不用去過多考慮。

2)併發量

針對 5000 萬用戶平均每人訪問 2 次,則併發量爲每秒 16.7 萬左右(5000w2/1060), 在預留一部分,可以預估到每秒 25 萬左右(也可以進行 double 下)。

3)網絡帶寬

在帶寬方面,需要進行相關優化,採取數據傳輸越少越好,假設單條傳輸在 0.5KB,則根據併發量預估網絡帶寬爲:977Mb 左右(25w0.5KB=122MB8bit=977Mb)。

03. 非功能性需求

做任何系統都要考慮非功能性需求,特別是公司的核心繫統,當前秒殺業務系統非功能性需求主要體現在如下幾點:

2 概要設計

通過對秒殺業務的本身認知以及上面提到的秒殺業務需求,本次秒殺系統需要着重設計如下幾點:

(1)動靜分離:如何保證用戶在不刷新頁面的情況下,依然能進行秒殺相關數據的獲取且不會耽誤秒殺活動的開始。

(2)流量分層,針對巨大流量,如何進行有效的防控,以免造成後臺服務的不堪重負,以及如何避免前端頁面的卡死。

(3)高可用:如何確保後臺持續提供服務。

(4)扣減庫存:如何有效扣減庫存。

01. 動靜分離

動靜分離是指,將靜態頁面與動態頁面(或者靜態數據與動態數據)解耦分離,用不同系統承載對應流量。這樣可以提升整個服務訪問性能和可維護性。

商品秒殺頁面的靜態數據以及動態數據,均是不同的地方提供,如下圖所示。

靜態數據是指,頁面中幾乎不怎麼變化的數據(即不依據用戶的 Cookie、基本信息、地域,及時間等各種屬性來生成的數據),例如:

對於這種分離出來的靜態數據可以進行緩存。在緩存之後,這些靜態數據的訪問效率就提高了,系統也更快了。可以使用代理服務器進行靜態數據的緩存。

動態數據是指,依據當前用戶屬性動態生成的數據,在瀏覽淘寶首頁時,每個用戶所看到的商品都是不一樣的,這就是淘寶的 “千人千面”——針對不同用戶做不同的推薦;在百度搜索中是依據不同用戶的輸入條件,以及用戶的習慣給出不同的結果頁。這其中的數據就是動態數據。

02. 流量分層

在 “秒殺” 業務中,商品價格具有強大的吸引力,所以會受到很多用戶的關注,但是商品數量是有限的。所以,在千萬的用戶中可能只有 100 人能得到商品,對於系統來說,有 90% 以上的流量屬於無效流量。

“秒殺”業務希望有大量的用戶來關注 “秒殺” 活動,但是在用戶真正下單時又不能將這些流量全部放過,所以,需要設計一套高效的流量管控方案,來有效地控制請求流量,過濾掉沒必要的流量。

對於瞬時流量洪峯可以採用倒三角的分層級逐層控制方式,共分爲 CDN、反向代理(Nginx)、後端服務及 DB 這四個層級。接下來,就來看看每一層級是怎麼控制流量的,如下圖所示。

03. 高可用

要想在整個 “秒殺” 活動持續期間內,依然能對用戶提供良好的體驗,則秒殺系統架構在設計時不能設計成單節點的架構。

單節點是所有系統設計中的大忌,因爲單節點系統意味着系統的不穩定性較高,可能會出現不可用的情況,會給企業帶來直接的損失。在系統設計(特別是 “秒殺” 這類對高併發要求極高的系統)時,必須保證系統的高可用,如下圖所示。

04. 扣減庫存

對於 “秒殺” 活動,通常,公司是不允許商品超賣(即下單成功的數量不能大於商品存存數量)的。一旦超賣,則會給公司造成損失。如果被惡意流量利用,則損失是巨大的。

庫存對於電商平臺來說是一個重要的業務指標,所以在技術上需要合理設計扣減庫存,不能出現 “超賣” 現象。通常,扣減庫存常有以下 3 種方式:

05. 系統架構設計

根據上面討論,針對當前秒殺架構如下圖所示。

如上架構比較簡潔,主要分爲以下 5 層。

其部署架構圖如下:

3 詳細設計

01. 動靜分離設計

實施動靜分離架構可以採用 “分而治之” 的辦法,即將動態數據和靜態數據解耦,分別使用各自的架構系統來承載對應的流量:

靜態數據訪問速度很快,而動態數據訪問速度較慢。那麼試想下,可以將需要動態獲取的數據給提前生成好,然後使用靜態頁面加速技術來訪問嗎?如果這樣可以,那動態數據訪問的速度就變快了。

這樣是可以的,需要用到比較流行的 “頁面靜態化” 技術。頁面靜態化技術是指,直接緩存 HTTP 連接,而不僅是緩存數據。如下圖所示,代理服務器根據請求的 URL 直接將 HTTP 對應的響應頭及響應消息體返回,流程簡潔且高效。

02. 流量分層設計

流量分層主要體現在對於 CDN 層、反向代理層、後端服務層以及數據層流量進行控制。

1)CDN 層流量控制

由動靜分離技術可以想到:應儘量將盡可能多的數據提前生成,然後將其放入 CDN 節點緩存中(因爲 CDN 層在物理架構上離用戶比較近)。

所以,如果絕大部分的流量都在這一層獲取數據,則到達後端的流量會減少很多,如下圖所示。

2)反向代理層流量控制

在動靜分離方案中,講到通過 “頁面靜態化技術” 加速動態數據的獲取,即提前將動態數據生成好,然後對其進行靜態化處理。

所以,這裏就可以依據頁面靜態化加速技術,通過後端服務 Job 的方式定時提前生成前端需要靜態的數據;然後,將其發送到內容分發服務上;最後,分發服務會將這些靜態化頁面數據分發到所有的反向代理服務器上,如下圖所示。

在 “秒殺” 業務中,活動詳情頁上有一個倒計時的模塊,用戶可以看到當前 “秒殺” 活動還剩餘多少時間開始。

這種邏輯簡單的功能可以直接使用 Nginx 來實現:利用 nginx-lua 插件,使用 lua 腳本獲取當前 Nginx 服務器的時間進行計算倒計時。

另外,商品庫存數據也可以通過 Nginx 直接訪問分佈式緩存來獲取,如下圖所示。

“秒殺”業務中的商品價格很低,對於用戶有很大的吸引力,所以可能會有人利用 “秒殺器” 進行不公平競爭,且有可能存在競爭對手惡意刷請求的情況。

如果存在這樣的情況,那本次活動就是有風險的,萬一被惡意流量獨佔了庫存,則會導致正常用戶不能搶購商品,也有可能這種惡意的請求會對後端系統造成嚴重衝擊,甚至造成後端系統癱瘓。

對於這種惡意請求,最好有一套機制能提前感知,並將惡意請求提前封存。可以在 Nginx 層中控制;也可以在 Nginx 中配置用戶的訪問頻率(例如每分鐘只能訪問 10 次);還可以使用 Lua 腳本編寫一些簡單業務邏輯的接口,例如,通過調用接口直接封掉指定 IP 地址或 UserAgent 的請求。

3)後端服務層流量控制

對於服務層的流量控制,有以下幾點建議:

當 “到達系統中的請求數” 明顯大於 “系統能夠處理的最大請求數” 時,可以直接拒絕這些多餘的請求,直接返回 “秒殺” 活動結束的信息。例如,活動開始時的商品庫存是 100,目前庫存只剩 50 了,如果 “每臺服務器待處理的請求數” 已經超過 “商品總庫存數(100)” 了,則可以直接終止掉多餘的請求。

4)數據庫層流量控制

對於請求到數據中的流量,寫入的流量就是真正下單成功的流量,即需要扣減庫存的動作。有如下建議:

對於數據庫行鎖的優化,可以通過將商品進行拆分來實現——增加 ID,如下圖所示。對於單一的 “秒殺” 活動這會得到顯著效果。

從流量分層控制方案可看出,瞬時流量就像被漏斗過濾了似的,應儘量將數據和請求量一層一層地過濾掉。這種流量分層控制核心思想:在不同的層級中儘可能地過濾掉無效的請求,到達 “倒三角” 最末端的請求才是有效的請求。

5)高可用

在系統設計時想要做到高可用,避免單節點的一個小妙招:將服務無狀態化。如果無法完全無狀態化(如存儲系統),則可以通過冗餘多個備份節點的方案來避免單節點。

由於篇幅原因,高可用此處就不再贅述,大家可以查看**《高併發系統實戰派》**一書裏面針對高併發系統的真實設計案例,毫無保留的分享出了企業級高併發系統實戰。

03. 扣減庫存設計

由於在 “秒殺” 場景中商品一般優惠力度很大,對用戶很具有吸引力,所以,在這種場景中使用 “下單扣庫存” 方式更爲合適。

在 “秒殺” 場景中,大部分用戶抱着 “搶到就是賺到” 的想法,基本都會去付款的,但如果真有競爭對手惡意下單不付款,那我們該怎麼辦?前面在流量管控中已經說到,可以對請求日誌進行實時分析,讓風控系統選擇出惡意用戶,然後將其封停。

在 “秒殺” 場景中,通過流量分層控制可以分層管控大量的 “讀” 請求。但是,依然會有很大的流量進入真正的下單邏輯。對於這麼大的流量,除前面說的數據庫隔離外,還需要進一步優化庫存,否則數據庫讀 / 寫依然是系統的瓶頸。

接下來看看如何優化大流量 “秒殺” 場景中的庫存數量扣減操作。

1)利用緩存技術

在 “秒殺” 場景中,如果只是一個扣減庫存數量這樣的簡單流程,則可以先將庫存數量直接放在緩存中,然後用分佈式緩存(如 Redis)的超高性能去應對這種瞬時流量洪峯下的系統挑戰。

使用緩存是存在一定風險的,比如,緩存節點出現了異常,那庫存數量該怎麼算?

使用緩存,不僅要考慮分佈式緩存高可用(如何設計可以查看我的新書 “高併發系統實戰派”),還要考慮各種限流容錯機制,以確保分佈式緩存對外提供服務。

2)異步處理技術

如果是複雜的扣減庫存(如涉及商品信息本身或牽連其他系統),則建議使用數據庫進行庫存數量的扣減,可以使用異步的方式來應對這種高併發的庫存的更新。

①在用戶下單時,不立刻生成訂單,而是將所有訂單依次放入隊列。

②下單模塊依據自身的處理速度,從隊列中依次獲取訂單進行 “下單扣庫存” 操作。

③在訂單生成成功後,用戶即可進行支付操作了。

這種方式是針對 “秒殺” 場景的,依據 “先到先得” 原則來保證公平公正,所有用戶都可以搶購,然後等待訂單處理,最後生成訂單(如果庫存不足,則生成訂單失敗)。

這樣的邏輯,對用戶來說體驗不是很差。

具體排隊邏輯如下圖所示。

4 搭建千萬級流量 “秒殺” 系統需要哪些技術

前面介紹了千萬級流量 “秒殺” 系統的基本架構、“秒殺”系統的設計原則、如何做動靜分離方案和流量控制,以及扣減庫存方面內容。這些都是設計高併發 “秒殺” 系統必須要考慮的。

“秒殺”系統的流程並不複雜——只是一個 “下單扣庫存” 的動作,但由於其獨特的業務特點,所以在進行系統設計時不能大意。對於瞬時流量洪峯的高併發 “秒殺” 系統,我們需要什麼技術呢?下面來總結一下。

(1)數據的靜態化的技術

用來應對高併發讀的請求,主要涉及以下內容,這些在**《高併發系統實戰派》**一書中詳細分享了真實使用場景已經技術方案:

(2)負載均衡反向代理技術

(3)異步處理技術

(4)系統架構設計技術

(5)系統監控技術

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/m5KqZfWkfeaLpG82RRPspQ