從全局角度,如何設計一個秒殺系統?

大家好,我是樹哥。

秒殺系統的設計是高級職位面試中非常高頻的一道題目,它可以較好地考察候選人的知識體系情況。對於我們來說,學習秒殺系統的設計,能夠讓我們學以致用,設計系統的時候考慮得更加全面。今天就讓樹哥帶你一起來看看怎麼設計一個秒殺系統!

活動一般出現在電商的促銷活動中,一般是指定了很少數量的商品,以極低的價格,讓大量的用戶參與,從而造成大量用戶在極短的時間內參與活動,進而造成系統在極短的時間內有極高的流量。系統設計的目的是使系統能夠穩定地支撐活動的進行,因此其穩定性、高可用是我們考慮的第一位。

要知道如何進行秒殺系統的優化,那我們需要先對請求的整個流程有個全局的認識。一般來說,秒殺活動請求以公網爲劃分點,可以分爲:前端部分、後端部分。 前端部分指的是從用戶端到進入後端服務前的部分,包括了移動端的處理、DNS 解析、公網的數據傳遞等。後端部分指的是經公網進入了後端的服務器網絡裏,包括了前置的負載均衡(Nginx 等)、應用服務器、數據庫層等。秒殺活動的整個流程可以用下圖來表示。

the-process-of-network-request

我們要去設計一個秒殺系統,那自然也是從這兩大部分來進行優化。整體思路是儘量將流量擋在前面,讓儘量少的流量留到後端部分。因爲越往後端,我們的處理邏輯就越重,其處理能力也越弱。

前端優化

對於前端部分來說,常見的優化手段有:頁面靜態化 + CDN、請求頻率限制。

頁面靜態化 + CDN

一般來說,活動頁面是流量最大的地方。活動頁面上絕大部分內容都是固定的,比如:商品描述、圖片等。這時候沒有必要每次都去請求服務端,而是將這些靜態的內容放到 CDN 上。每次打開頁面的時候,直接去請求 CDN 服務器,能極大地減少後端的請求流量。加入了 CDN 之後,其請求過程如下:

CDN 優化靜態數據

所謂的 CDN 就是內容分發網絡,它由非常多臺分佈在世界各地的緩存服務器組成。每次用戶請求特定域名的時候,會轉發到對應 CDN 的 DNS 解析服務器,隨後會返回一臺離用戶地理位置最近的一臺 CDN 服務器。隨後,用戶直接請求這臺 CDN 服務器獲取數據,從而極大地減少了長途網絡傳輸的時間,並且也減少了後端服務器的壓力。

因此,對於秒殺活動設計來說,我們可以將所有可以靜態化的內容全部靜態化,然後將其配置在 CDN 服務器上。這樣既提高了用戶打開頁面的時間,又減少了後端服務器的壓力。

請求頻率限制

請求頻率限制,指的是根據業務的特點,在前端做一些流量攔截,減少後端服務器的壓力。常見的攔截方式有:

  1. 設定一個請求概率,只允許 30% 的概率向後端發送接口請求。
  2. 設定一個請求頻率,例如 10 秒鐘只能請求 1 次,隨後按鈕置灰。

通過這種方式,我們可以減少很大一部分流量。但在具體實現的時候,可能需要考慮安全問題,預防某些用戶直接調用後臺接口,繞過前端的頻率檢查。常見的方法是在頻率檢查時生成一個參數,隨後請求後端服務時攜帶上該參數。沒有該參數的請求,都視爲非法請求,直接拒絕該請求。

後端優化

無論我們做多大的努力,始終還是會有不少流量會來到後端服務器這裏。一般來說,後端的優化有如下幾種方式:

  1. 增加緩存層 + 預熱數據
  2. MQ 異步處理
  3. 限流、熔斷、兜底
  4. 業務側優化

增加緩存層 + 預熱數據

如果我們所有數據都去讀取數據庫,數據庫可能無法承受較大的流量,此時一個常見的優化就是增加緩存層。

當我們需要查詢數據庫之前,我們先去查詢緩存,這樣可以減少絕大部分的數據庫請求,減輕數據庫壓力。如果在緩存中找不到數據,我們再去請求數據庫,隨後再將數據緩存到緩存中。在引入緩存層的時候,我們需要考慮緩存擊穿、緩存穿透的可能性,在寫相關代碼的時候就要做好這些優化。

另外,我們在秒殺活動開始之前,可以手動將熱點數據加載到緩存中,從而避免秒殺時去請求數據庫。

MQ 異步處理

我們知道秒殺活動一般涉及搶購、下單、支付、發貨等階段,而搶購與後續的幾個階段是可以異步執行的。爲了避免對下單、支付、發貨等階段產生影響,我們可以將搶購階段與後續階段用 MQ 進行解耦處理。當用戶搶購成功後,往消息隊列中丟入一臺消息,隨後再由訂單系統消費進行下單處理。

通過各系統之間的解耦處理,我們可以將原本同步的處理方式變爲異步處理,從而大大的減少了請求的處理時間,提高了系統的併發處理能力。其次,也能避免系統之間相互影響,提高了整體系統的穩定性。

限流、熔斷、降級

雖然我們做了非常多的優化措施,但還是可能存在請求超量的可能性,那怎麼辦呢?

我們可以在每個業務系統做限流操作,從而避免因爲請求太多,導致整個系統都無法工作。當併發請求在正常範圍內時,我們正常處理請求。當超過設置的限流閾值時,我們則直接拒絕該請求,提示用戶搶購失敗。

如果沒有限流操作,那麼系統直接崩潰了,一個請求都處理不了。而通過限流這種方式,系統至少還可以保持正常工作,而不至於一個請求都處理不了。而超量的需求,本來就處理不了,因此提示失敗也是情理之中。

除了限流之外,不同的系統還可以採用熔斷、降級的服務治理措施。

熔斷指的是請求的錯誤次數超過閾值時,不再到用後端服務,直接返回失敗。同時每隔一定時間放幾個請求去重試後端服務,看看是否正常。如果正常則關閉熔斷狀態,如果失敗則繼續快速失敗。熔斷的目的是避免因下游短暫的異常,導致上游不斷重試,最終造成下游有太多請求,最終壓垮下游系統。

降級指的是當服務失敗或異常後,返回指定的默認信息。降級的目的是保證有基本的信息,當下遊異常時,與其返回空信息,不如返回一個有業務含義的默認信息,可以提高用戶體驗。

業務側優化

一般來說,經過上述的整體優化之後,系統已經能夠比較穩當地應對秒殺活動了。如果此時還是流量比較大,那麼或許應該從業務側去進行優化了。

例如 12306 剛開始的時候,購買時間都在同一時刻,這導致同一時刻併發量太大,系統經常支撐不住。後來 12306 將購票週期放長,可以提前 20 天購買火車票。通過業務側的優化,我們將本來在 1 個小時的搶購分攤到了 20 天,服務器壓力一下子降低了 480 倍!

張小龍也說過:如果公司最厲害的程序員來實現業務都覺得複雜,那很可能就是業務確實不合理,這時候應該從業務側進行優化。

例如一個存儲了 10 億條記錄的消息記錄表,業務側既想查詢速度快,又想進行 1 年數據範圍的數據查詢,這無論如何都是無法實現的。這時候就需要從業務需求側進行優化,否則是無法兩全其美的。對於這個場景,一個合理的實現方式是:要實現 1 年數據範圍的查詢,那麼只能根據消息 ID 進行,因爲這樣可以使用上索引。而要根據時間範圍進行查詢,只能縮短查詢時間到 3 天內,這樣也可以滿足業務需求。

因此從業務側進行優化,是一個四兩撥千斤的辦法,可以極大地降低技術側實現的難度。

總結

設計一個秒殺系統,整體而言可以從前端與後端進行優化。

對於前端優化而言,可以從「頁面靜態化 + CDN」、請求頻率限制進行優化。

其中「頁面靜態化 + CDN」指的是將不變的靜態數據固定下來,然後放入 CDN 服務器,從而降低用戶請求的響應速度,降低服務器的併發壓力。請求頻率限制,則是通過搶購概率與搶購頻率限制,降低後端服務器的服務壓力。

對於後端優化而言,一般有「增加緩存層 + 預熱數據」、「MQ 異步處理」、「限流、熔斷、降級」、業務側優化這 4 種優化方式。

其中「增加緩存層 + 預熱數據」指的是將熱點數據存入緩存,並在活動開始前提前加載到緩存中,降低數據庫層的讀取壓力。「MQ 異步處理」指的是對於非必要的業務邏輯,通過 MQ 進行異步處理,降低請求處理延時,同時提高業務系統整體穩定性。

「限流、熔斷、降級」是對於整體微服務的保護,其中限流指的是對請求進行限制,當超過限流閾值時,直接拒絕請求,保護系統本身;熔斷指的是保護下游系統,當請求下游系統連續錯誤超過閾值時,自動不去請求下游系統,避免因重試流量過大擊垮下游系統。降級指的是當請求失敗時,自動返回默認數據,提高用戶體驗。業務側優化,則是指從業務層面去進行邏輯優化,從而降低技術複雜度,使得業務與技術複雜度達到一個平衡的狀態,有利於更好地實現秒殺系統的高可用與高併發。

上面說到的 6 個優化思路,是設計秒殺系統常見的優化思路。但在實際業務場景中,除了要保障正常的功能設計之外,還還考慮防刷、安全、黑產等問題,此時可能需要多考慮一些其他優化,例如:黃牛利用搶購工具搶購,導致正常用戶無法搶到商品等。這時候可能需要考慮增加驗證碼,用 App 設備指紋等風控措施。此外,對於秒殺系統而言,做好業務指標和系統指標的埋點監控也是非常重要的。

如何設計一個秒殺系統?

參考資料

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章