如何設計秒殺系統(tǒng)

如何設計秒殺系統(tǒng)

1 秒殺的問題

服務單一荒叶、獨立部署

秒殺服務即使自己扛不住高并發(fā)而宕機幻林,也不要造成服務雪崩。

秒殺鏈接加密

  • 避免惡意攻擊酪惭,機器人模擬秒殺請求
  • 避免鏈接暴露,自己工作人員者甲,提前秒殺商品

庫存預熱春感、快速扣減

秒殺讀多寫少。無需每次實時校驗庫存虏缸。庫存預熱鲫懒,放到Redis,信號量控制進來秒殺的請求刽辙。

動靜分離

Nginx做好動靜分離窥岩。靜態(tài)資源 Nginx 直接返回,保證秒殺和商品詳情頁的動態(tài)請求才打到后端服務集群宰缤。
使用CDN網(wǎng)絡颂翼,分擔本集群壓力。

惡意請求攔截

服務網(wǎng)關識別非法攻擊請求并進行攔截慨灭。

流量削峰

使用各種手段朦乏,將流量分擔到更大寬度的時間點。比如驗證碼氧骤,加入購物車呻疹。

限流、熔斷筹陵、降級

前端限流+后端限流 限制次數(shù)刽锤,限制總量镊尺,快速失敗降級運行, 熔斷隔離防止雪崩姑蓝。

隊列削峰

1萬個商品鹅心,每個1000件秒殺。雙11 所有秒殺成功的請求纺荧,進入隊列旭愧,慢慢創(chuàng)建 訂單,扣減庫存即可宙暇。

高并發(fā)系統(tǒng)設計的三個目標:性能输枯、可用性和可擴展性

在提升系統(tǒng)性能方面我們一直關注的是系統(tǒng)的查詢性能占贫,比如數(shù)據(jù)庫的分布式改造桃熄,各類緩存。因為大部分場景都是讀多寫少型奥。

比如一個社區(qū)系統(tǒng)初期一定是只有少量的種子用戶在生產(chǎn)內(nèi)容瞳收,而大部分的用戶都在“圍觀”別人在說什么。此時厢汹,整體流量較小螟深,而寫流量可能只占整體流量的百分之一,那么即使整體的QPS到了1w烫葬,寫請求QPS也只是到了100界弧,如果要對寫請求做性能優(yōu)化,性價比不高搭综。

但隨著業(yè)務發(fā)展垢箕,可能遇到一些存在高并發(fā)寫請求場景,比如秒殺兑巾。假設你的商城策劃了一期秒殺活動条获,活動在第五天的00:00開始,僅限前200名蒋歌,那么秒殺即將開始時帅掘,后臺會顯示用戶正在瘋狂地刷新APP或者瀏覽器來保證自己能夠盡量早的看到商品。

但讀請求依舊過高奋姿,如何應對?

2 優(yōu)化方案

丟棄訂單

最早期素标,量太大扛不住称诗,直接前端隨機reject一些,返回搶單失敗头遭,簡單粗暴寓免,但有效癣诱,比如10萬人搶100個iPhone,只要能提前預測有大概1萬以上的人參與(通過資格確認袜香、報名等方式收集信息)撕予,那么直接請求進來以后隨機擋回去99%的流量都沒有啥問題。

優(yōu)化吞吐

中間有段時間蜈首,提前準備一大批機器实抡,服務化、分庫分表搞定后端性能欢策,讓前端業(yè)務可以加一定量的機器吆寨,然后搞穩(wěn)定性,依賴關系踩寇,容量規(guī)劃啄清,做彈性,提升吞吐量俺孙。

異步隊列

使用可堆積的消息隊列或內(nèi)存消息隊列辣卒。若搶單具有強順序,則先都進隊列睛榄,然后拿前N (就是庫存數(shù))個出來平滑處理荣茫,剩下都可作為失敗進行批處理。

甚至還可做一個定長隊列懈费,再往里寫直接提示失敗计露。隊列把并發(fā)變成串行,從而去掉了分布式鎖憎乙。

內(nèi)存分配

某些業(yè)務可以考慮預熱票罐,提前在每個機器節(jié)點內(nèi)存分配好庫存數(shù),然后直接在內(nèi)存處理庫存數(shù)泞边。

拆分擴展

對于不同類型该押、不同商家、不同來源的商品阵谚,部署不同的前端促銷集群蚕礼,
分散壓力。比如梢什,按每個整點發(fā)起秒殺奠蹬,具體到每個商家,其實量就不大了嗡午。

服務降級

越重要的搶單囤躁,大家越關心自己有沒有搶到,而不是特別在意訂單立即處
理完,也就是說狸演,下單占到位置比處理完成訂單要更有價值言蛇。比如12306春運搶票,只要告訴用戶你搶到了票宵距,但預計1個小時后訂單才會處理完腊尚,用戶有這個明確預期即可。用戶不會立馬使用這張票满哪,也不會在意1min還是1h內(nèi)處理完婿斥。

部分方案會導致銷售不足或超賣:

  • 銷售不足可以從搶購里加一些名單補發(fā),也可以加一輪秒殺
  • 超賣比較麻煩翩瓜,所以一般會多備一點貨受扳,比如搶100個iPhone,提前準備110 個

因為用戶查詢的是少量的商品數(shù)據(jù)兔跌,屬查詢熱點數(shù)據(jù)勘高,可采用緩存將請求盡量擋在上層緩存,能被靜態(tài)化的數(shù)據(jù)(比如商城里的圖片和視頻數(shù)據(jù))盡量做到靜態(tài)化坟桅,這就可命中CDN節(jié)點緩存华望,減少Web服務器的查詢量和帶寬負擔。Web服務器比如Nginx可以直接訪問分布式緩存節(jié)點仅乓,從而避免請求到達Tomcat等業(yè)務服務器赖舟。

當然,你可以加上一些限流的策略夸楣,比如對短時間之內(nèi)來自某一個用戶宾抓、某一個IP或者某一臺設備的重復請求做丟棄處理。

通過這幾種方式豫喧,請求就可以盡量擋在數(shù)據(jù)庫之外了石洗。

稍微緩解了讀請求之后,00:00分秒殺活動準時開始紧显,用戶瞬間向電商系統(tǒng)請求生成訂單讲衫,扣減庫存,用戶的這些寫操作都是不經(jīng)過緩存直達數(shù)據(jù)庫的孵班。1秒鐘之內(nèi)涉兽,有1萬個數(shù)據(jù)庫連接同時達到,系統(tǒng)的數(shù)據(jù)庫瀕臨崩潰篙程,尋找能夠應對如此高并發(fā)的寫請求方案迫在眉睫枷畏。這時你想到了消息隊列。

理解消息隊列

把消息隊列看作暫時存儲數(shù)據(jù)的一個容器虱饿,它是一個平衡低速系統(tǒng)和高速系統(tǒng)處理任務時間差的工具拥诡。

比如古代臣子朝見皇上陳述國家大事丹允,等皇上決策。但大臣很多袋倔,如果同時去找皇上,皇上肯定會崩潰折柠。后來變成臣子到午門后要原地等皇上將他們一個一個地召見進大殿商議宾娜,這就緩解皇上處理事情的壓力。
可以把午門看作一個暫時容納臣子的容器扇售,即消息隊列:

  • 在Java線程池中我們就會使用一個隊列來暫時存儲提交的任務前塔,等待有空閑的線程處理這些任務
  • os中斷的下半部分也會使用工作隊列來實現(xiàn)延后執(zhí)行
  • 實現(xiàn)一個RPC框架時,也會將從網(wǎng)絡上接收到的請求寫到隊列里承冰,再啟動若干個工作線程來處理

那如何用消息隊列解決秒殺場景下的問題呢华弓?

削去秒殺場景下的峰值寫流量

在秒殺場景下短時間之內(nèi)數(shù)據(jù)庫的寫流量很高,按以前思路困乒,應該分庫分表寂屏。若已做了分庫分表,則需要擴展更多數(shù)據(jù)庫應對更高寫流量娜搂。
但無論是分庫分表還是擴充更多數(shù)據(jù)庫都會很復雜迁霎,因為你需要遷移數(shù)據(jù)庫中的數(shù)據(jù),這個時間就要按天甚至周計算百宇。

而在秒殺場景下高并發(fā)的寫請求并不是持續(xù)的考廉,也不是經(jīng)常發(fā)生,而只有在秒殺活動開始后的幾s或十幾s時間內(nèi)才存在携御。
為了應對這十幾s瞬間寫高峰昌粤,而去花費幾天甚至幾周擴容DB,再在秒殺之后花費幾天做縮容啄刹,得不償失涮坐!

所以思路是:將秒殺請求暫存在MQ,然后業(yè)務服務器會響應用戶“秒殺結果正在計算”鸵膏,釋放了系統(tǒng)資源之后再處理其它用戶請求膊升。

在后臺啟動若干個隊列處理程序消費MQ中的消息,再執(zhí)行校驗庫存谭企、下單等邏輯廓译。因為只有有限個隊列處理線程在執(zhí)行,所以落入后端DB上的并發(fā)請求有限债查。而請求是可以在MQ被短暫堆積非区,當庫存被消耗完后,消息隊列中堆積的請求就可以被丟棄了盹廷。

這就是MQ在秒殺系統(tǒng)中主要作用:削峰填谷征绸,可以削平短暫流量高峰,雖說堆積會造成請求被短暫延遲處理,但只要我們時刻監(jiān)控MQ中的堆積長度管怠,在堆積量超過一定量時淆衷,增加隊列處理機數(shù)量來提升消息處理能力即可,而且秒殺用戶對于短暫延遲知曉秒殺的結果也有一定容忍度渤弛。

注意是“短暫”延遲祝拯,若長時間沒有給用戶公示秒殺結果,則用戶會懷疑秒殺活動有黑幕她肯。所以在使用MQ應對流量峰值時佳头,需要對隊列處理的時間、前端寫入流量的大小晴氨、數(shù)據(jù)庫處理能力做好評估康嘉,然后根據(jù)不同的量級來決定部署多少臺隊列處理程序。

比如你的秒殺商品有1000件籽前,處理一次購買請求的時間是500ms亭珍,那么總共就需要500s的時間。這時你部署10個隊列處理程序枝哄,那么秒殺請求的處理時間就是50s块蚌,也就是說用戶需要等待50s才可以看到秒殺的結果,這是可以接受的膘格。這時會并發(fā)10個請求到達數(shù)據(jù)庫峭范,并不會對數(shù)據(jù)庫造成很大的壓力。

通過異步處理簡化秒殺請求中的業(yè)務流程
其實在大量的寫請求“攻擊”你的電商系統(tǒng)的時候瘪贱,消息隊列除了發(fā)揮主要的削峰填谷的作用之外纱控,還可以實現(xiàn)異步處理來簡化秒殺請求中的業(yè)務流程,提升系統(tǒng)的性能菜秦。

你想甜害,在剛才提到的秒殺場景下,我們在處理購買請求時需要500ms球昨。這時你分析了一下整個的購買流程尔店,發(fā)現(xiàn)這里面會有主要的業(yè)務邏輯,也會有次要的業(yè)務邏輯:比如說主慰,主要的流程是生成訂單嚣州、扣減庫存;次要的流程可能是我們在下單購買成功之后會給用戶發(fā)放優(yōu)惠券共螺,會增加用戶的積分该肴。

假如發(fā)放優(yōu)惠券的耗時是50ms,增加用戶積分的耗時也是50ms藐不,那么如果我們將發(fā)放優(yōu)惠券匀哄、增加積分的操作放在另外一個隊列處理機中執(zhí)行秦效,那么整個流程就縮短到了400ms,性能提升了20%涎嚼,處理這1000件商品的時間就變成了400s阱州。如果我們還是希望能在50s之內(nèi)看到秒殺結果的話,只需要部署8個隊列程序就好了法梯。

經(jīng)過將一些業(yè)務流程異步處理之后贡耽,我們的秒殺系統(tǒng)部署結構也會有所改變:

解耦實現(xiàn)秒殺系統(tǒng)模塊之間松耦合
除了異步處理和削峰填谷以外刀森,消息隊列在秒殺系統(tǒng)中起到的另一個作用是解耦合。

比如數(shù)據(jù)團隊對你說影暴,在秒殺活動之后想要統(tǒng)計活動的數(shù)據(jù)软能,借此來分析活動商品的受歡迎程度、購買者人群的特點以及用戶對于秒殺互動的滿意程度等等指標欺抗。而我們需要將大量的數(shù)據(jù)發(fā)送給數(shù)據(jù)團隊,那么要怎么做呢?

一個思路是:使用HTTP或者RPC的方式來同步地調(diào)用至耻,也就是數(shù)據(jù)團隊這邊提供一個接口,我們實時將秒殺的數(shù)據(jù)推送給它镊叁,但是這樣調(diào)用會有兩個問題:

整體系統(tǒng)的耦合性比較強尘颓,當數(shù)據(jù)團隊的接口發(fā)生故障時,會影響到秒殺系統(tǒng)的可用性晦譬。
當數(shù)據(jù)系統(tǒng)需要新的字段疤苹,就要變更接口的參數(shù),那么秒殺系統(tǒng)也要隨著一起變更敛腌。
這時卧土,我們可以考慮使用消息隊列降低業(yè)務系統(tǒng)和數(shù)據(jù)系統(tǒng)的直接耦合度。

秒殺系統(tǒng)產(chǎn)生一條購買數(shù)據(jù)后像樊,我們可以先把全部數(shù)據(jù)發(fā)送給消息隊列尤莺,然后數(shù)據(jù)團隊再訂閱這個消息隊列的話題,這樣它們就可以接收到數(shù)據(jù)生棍,然后再做過濾和處理了颤霎。

秒殺系統(tǒng)在這樣解耦合之后,數(shù)據(jù)系統(tǒng)的故障就不會影響到秒殺系統(tǒng)了涂滴,同時當數(shù)據(jù)系統(tǒng)需要新的字段時友酱,只需要解析消息隊列中的消息,拿到需要的數(shù)據(jù)就好了柔纵。

異步處理粹污、解耦合和削峰填谷是消息隊列在秒殺系統(tǒng)設計中起到的主要作用,其中異步處理可以簡化業(yè)務流程中的步驟首量,提升系統(tǒng)性能壮吩;削峰填谷可以削去到達秒殺系統(tǒng)的峰值流量进苍,讓業(yè)務邏輯的處理更加緩和;解耦合可以將秒殺系統(tǒng)和數(shù)據(jù)系統(tǒng)解耦開鸭叙,這樣兩個系統(tǒng)的任何變更都不會影響到另一個系統(tǒng)觉啊,

如果你的系統(tǒng)想要提升寫入性能實現(xiàn)系統(tǒng)的低耦合,想要抵擋高并發(fā)的寫流量沈贝,那么你就可以考慮使用消息隊列來完成杠人。

課程小結
本節(jié)課,我結合自己的實際經(jīng)驗宋下,主要帶你了解了消息隊列在高并發(fā)系統(tǒng)設計中起到的作用以及一些注意事項嗡善,你需要了解的重點如下:

削峰填谷是消息隊列最主要的作用,但是會造成請求處理的延遲学歧。
異步處理是提升系統(tǒng)性能的神器罩引,但是你需要分清同步流程和異步流程的邊界,同時消息存在著丟失的風險枝笨,我們需要考慮如何確保消息一定到達袁铐。
解耦合可以提升你的整體系統(tǒng)的魯棒性。
當然横浑,你要知道剔桨,在使用消息隊列之后雖然可以解決現(xiàn)有的問題,但是系統(tǒng)的復雜度也會上升徙融。比如上面提到的業(yè)務流程中洒缀,同步流程和異步流程的邊界在哪里?消息是否會丟失欺冀,是否會重復帝洪?請求的延遲如何能夠減少?消息接收的順序是否會影響到業(yè)務流程的正常執(zhí)行脚猾?如果消息處理流程失敗了之后是否需要補發(fā)葱峡?這些問題都是我們需要考慮的。我會利用接下來的兩節(jié)課針對最主要的兩個問題來講講解決思路:一個是如何處理消息的丟失和重復龙助,另一個是如何減少消息的延遲砰奕。

引入了消息隊列的同時也會引入了新的問題,需要新的方案來解決提鸟,這就是系統(tǒng)設計的挑戰(zhàn)军援,也是系統(tǒng)設計獨有的魅力,而我們也會在這些挑戰(zhàn)中不斷提升技術能力和系統(tǒng)設計能力称勋。

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末胸哥,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子赡鲜,更是在濱河造成了極大的恐慌空厌,老刑警劉巖庐船,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異嘲更,居然都是意外死亡筐钟,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進店門赋朦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來篓冲,“玉大人,你說我怎么就攤上這事宠哄∫冀” “怎么了?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵毛嫉,是天一觀的道長诽俯。 經(jīng)常有香客問我,道長狱庇,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任恶耽,我火速辦了婚禮密任,結果婚禮上,老公的妹妹穿的比我還像新娘偷俭。我一直安慰自己浪讳,他們只是感情好,可當我...
    茶點故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布涌萤。 她就那樣靜靜地躺著淹遵,像睡著了一般。 火紅的嫁衣襯著肌膚如雪负溪。 梳的紋絲不亂的頭發(fā)上透揣,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天,我揣著相機與錄音川抡,去河邊找鬼辐真。 笑死,一個胖子當著我的面吹牛崖堤,可吹牛的內(nèi)容都是我干的侍咱。 我是一名探鬼主播,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼密幔,長吁一口氣:“原來是場噩夢啊……” “哼楔脯!你這毒婦竟也來了?” 一聲冷哼從身側響起胯甩,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤昧廷,失蹤者是張志新(化名)和其女友劉穎堪嫂,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體麸粮,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡溉苛,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了弄诲。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片愚战。...
    茶點故事閱讀 40,144評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖齐遵,靈堂內(nèi)的尸體忽然破棺而出寂玲,到底是詐尸還是另有隱情,我是刑警寧澤梗摇,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布拓哟,位于F島的核電站,受9級特大地震影響伶授,放射性物質發(fā)生泄漏断序。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一糜烹、第九天 我趴在偏房一處隱蔽的房頂上張望违诗。 院中可真熱鬧,春花似錦疮蹦、人聲如沸诸迟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽阵苇。三九已至,卻和暖如春感论,著一層夾襖步出監(jiān)牢的瞬間绅项,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工比肄, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留趁怔,地道東北人。 一個月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓薪前,卻偏偏與公主長得像润努,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子示括,可洞房花燭夜當晚...
    茶點故事閱讀 45,092評論 2 355

推薦閱讀更多精彩內(nèi)容