高頻面試題:秒殺場景設(shè)計
秒殺這個話題到現(xiàn)在來說已經(jīng)是一個老生常談的話題了,不過因為又臨近一年一度的雙11,而且發(fā)現(xiàn)前段時間無論是阿里還是騰訊一些大廠其實還是在頻繁的問到這個場景題,所以還是準備拿出來說說。
秒殺從規(guī)模上來說可以分為大秒和小秒。大秒指的是比如雙11這種特定的節(jié)日,商品規(guī)模超大、價格超低、流量超大的這種類型活動,小秒一般指的是商家自己配置的一些時段類型的活動,由商家自己指定時間上架。從形式來說還可以分為單時段秒殺和多時段秒殺。但是在這個場景里,我們一般就是指的單時段大型秒殺。

秒殺設(shè)計要面對的壓力和難度有幾點:
-
怎么保證超高的流量和并發(fā)下系統(tǒng)的穩(wěn)定性?如果峰值的QPS達到幾十萬,面對巨大的流量的壓力系統(tǒng)怎么設(shè)計保證不被打崩?
-
怎么保證數(shù)據(jù)最終一致性?比如庫存不能超賣,超賣了那虧本的要么就是商家要么就是平臺,用戶反正不背這個鍋,超賣了就今年325預(yù)訂。
當然,涉及到這種大型的活動,還需要考慮到數(shù)據(jù)統(tǒng)計分析,總不能活動做完了,效果不知道怎么樣。
系統(tǒng)架構(gòu)
假設(shè)今年的雙11預(yù)估峰值QPS將會有50萬(我隨便扯的),而根據(jù)我們平時的經(jīng)驗單機8C8G的機器可以達到1000左右的QPS,那么從理論上來說我們只要500臺機器就可以抗住了,就有錢任性不行?這么設(shè)計的話只能出門右轉(zhuǎn)不送了。
流量過濾
本質(zhì)上,參與秒殺的用戶很多,但是商品的數(shù)量是有限的,真正能搶到的用戶并不多,那么第一步就是要過濾掉大部分無效的流量。
-
活動開始前前端頁面的Button置灰,防止活動未開始無效的點擊產(chǎn)生流量 -
前端添加驗證碼或者答題,防止瞬間產(chǎn)生超高的流量,可以很好的起到錯峰的效果,現(xiàn)在的驗證碼花樣繁多,題庫有的還要做個小學(xué)題,而且題庫更新頻繁,想暴力破解怕是很難。當然我知道的還有一種人工打碼的方式,不過這個也是需要時間的,不像機器無限刷你的接口。 -
活動校驗,既然是活動,那么活動的參與用戶,參加條件,用戶白名單之類的要首先做一層校驗攔截,還有其他的比如用戶終端、IP地址、參與活動次數(shù)、黑名單用戶的校驗。比如活動主要針對APP端的用戶校驗,那么根據(jù)參數(shù)其他端的用戶將被攔截,針對IP、mac地址、設(shè)備ID和用戶ID可以對用戶參與活動的次數(shù)做校驗,黑名單根據(jù)平時的活動經(jīng)驗攔截掉一部分羊毛黨等異常用戶。 -
非法請求攔截,做了以上攔截如果還有用戶能繞過限制,那不得不說太牛X了。比如雙11零點開始還做了答題限制,那么正常人怎么也需要1秒的時間來答題吧,就算單身30年手速我想也不能超過0.5秒了,那么針對剛好0點或者在0.5秒以內(nèi)的請求就可以完全攔截掉。 -
限流,假設(shè)秒殺10000件商品,我們有10臺服務(wù)器,單機的QPS在1000,那么理論上1秒就可以搶完,針對微服務(wù)就可以做限流配置,避免后續(xù)無效的流量打到數(shù)據(jù)庫造成不必要的壓力。針對限流還有另外一種柵欄方式限流,這是一種純靠運氣的限流方式,就是在系統(tǒng)約定的請求開始的時間內(nèi)隨機偏移一段時間,針對每個請求的偏移量不同,如果在偏移時間之內(nèi)就會被攔截,反之通過。

性能優(yōu)化
做完無效流量的過濾,那么可能你的無效請求已經(jīng)過濾掉了90%,剩下的有效流量會大大的降低系統(tǒng)的壓力。之后就是需要針對系統(tǒng)的性能做出優(yōu)化了。
-
頁面靜態(tài)化,參與秒殺活動的商品一般都是已知的,可以針對活動頁面做靜態(tài)化處理,緩存到CDN。假設(shè)我們一個頁面300K大小,1千萬用戶的流量是多少?這些請求要請求后端服務(wù)器、數(shù)據(jù)庫,壓力可想而知,緩存到CDN用戶請求不經(jīng)過服務(wù)器,大大減小了服務(wù)器的壓力。 -
活動預(yù)熱,針對活動的活動庫存可以獨立出來,不和普通的商品庫存共享服務(wù),活動庫存活動開始前提前加載到redis,查詢?nèi)孔呔彺?,最后扣減庫存再視情況而定。 -
獨立部署,資源充足的情況下可以考慮針對秒殺活動單獨部署一套環(huán)境,這套環(huán)境中可以剝離一些可能無用的邏輯,比如不用考慮使用優(yōu)惠券、紅包、下單后贈送積分的一些場景,或者這些場景可以活動結(jié)束后異步的統(tǒng)一發(fā)放。這只是一個舉例,實際上單獨針對秒殺活動的話你肯定有很多無用的業(yè)務(wù)代碼是可以剝離的,這樣可以提高不少性能。
經(jīng)過這兩步之后,最終我們的流量應(yīng)該是呈漏斗狀。

超賣
秒殺除開高并發(fā)高流量下的服務(wù)穩(wěn)定性之外,剩下的核心大概就是怎么保證庫存不超賣了,也可以說要保證的是最終一致性。一般來說,針對下單和庫存有兩種方式:
-
下單即扣庫存,這是最常規(guī)的大部分的做法。但是可能在活動中會碰到第二點說到的情況。
-
支付完成扣庫存,這種設(shè)計我碰到過就是酒店行業(yè),廉價房放出來之后被黃牛下單搶占庫存導(dǎo)致正常用戶無法下單,然后黃??梢杂蒙愿叩膬r格再售賣給用戶從中牟利,所以會有在一些活動的時候采取支付成功后才占用庫存的做法。不過這種方式實現(xiàn)起來比較復(fù)雜,可能造成大量的無效訂單,在秒殺的場景中不太適用。
針對秒殺建議選擇下單扣庫存的方式,實現(xiàn)相對簡單而且是常規(guī)做法。
方案

-
首先查詢redis緩存庫存是否充足 -
先扣庫存再落訂單數(shù)據(jù),可以防止訂單生成了沒有庫存的超賣問題 -
扣庫存的時候先扣數(shù)據(jù)庫庫存,再扣減redis庫存,保證在同一個事務(wù)里,無論兩者哪一個發(fā)生了異常都會回滾。有一個問題是可能redis扣成功了由于網(wǎng)絡(luò)問題返回失敗,事務(wù)回滾,導(dǎo)致數(shù)據(jù)庫和緩存不一致,這樣實際少賣了,可以放到下輪秒殺去。
這種做法能一定程度上解決問題,但是也有可能會有其他問題。比如當大量請求落在同一條庫存記錄上去做update時,行鎖導(dǎo)致大量的鎖競爭會使得數(shù)據(jù)庫的tps急劇下降,性能無法滿足要求。
另外一種做法就是排隊,在服務(wù)層進行排隊,針對同一個商品ID的也就是數(shù)據(jù)庫是一條庫存記錄的做一個內(nèi)存隊列,串行化去扣減庫存,可以一定程度上緩解數(shù)據(jù)庫的并發(fā)壓力。
質(zhì)量保障
為了保證系統(tǒng)的穩(wěn)定性,防止你的系統(tǒng)被秒殺,一些質(zhì)量監(jiān)控就不得不做。
-
熔斷限流降級,老生常談,根據(jù)壓測情況進行限流,可以使用sentinel或者hystrix。另外前端后端都該有降級開關(guān)。 -
監(jiān)控,該上的都上,QPS監(jiān)控、容器監(jiān)控、CPU、緩存、IO監(jiān)控等等。 -
演練,大型秒殺事前演練少不了,不能冒冒失失的就上了吧。 -
核對、預(yù)案,事后庫存訂單 金額、數(shù)量核對,是否發(fā)生超賣了?金額是否正常?都是必須的。預(yù)案可以在緊急情況下進行降級。
數(shù)據(jù)統(tǒng)計
活動做完了,數(shù)據(jù)該怎么統(tǒng)計?
-
前端埋點 -
數(shù)據(jù)大盤,通過后臺服務(wù)的打點配合監(jiān)控系統(tǒng)可以通過大盤直觀的看到一些活動的監(jiān)控和數(shù)據(jù) -
離線數(shù)據(jù)分析,事后活動的數(shù)據(jù)可以同步到離線數(shù)倉做進一步的分析統(tǒng)計
總結(jié)
總的來說,面對巨量的流量我們的方式就是首先通過各種條件先篩選掉無效流量,進行流量錯峰,然后再對現(xiàn)有的系統(tǒng)性能做出優(yōu)化,比如頁面靜態(tài)化,庫存商品預(yù)熱,也可以通過獨立部署的方式和其他的環(huán)境做隔離,最后還要解決高并發(fā)下緩存一致性、庫存不能超賣的問題,防止大量的并發(fā)打爆你的數(shù)據(jù)庫。
一個完整的活動從前端到后端是一個完整的鏈路,中間有事前的演練工作,事后的數(shù)據(jù)分析等都是必不可少的環(huán)節(jié)。
- END -
特別推薦一個分享架構(gòu)+算法的優(yōu)質(zhì)內(nèi)容,還沒關(guān)注的小伙伴,可以長按關(guān)注一下:
長按訂閱更多精彩▼
如有收獲,點個在看,誠摯感謝
免責聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺僅提供信息存儲服務(wù)。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!