回答這份秒殺攻略,99.9%的面試官會給你比大拇指

對不起又標題黨啦!不過這裏想說的是,做互聯網行業的應該保持一顆謙虛的心靈,畢竟你知道的越多,你不知道的也越多,不是嗎?唯有不斷學習,纔是我們碼農的最終歸宿。

前言

設計一個秒殺系統,到底需要考慮哪些問題呢?相信不少同學在面試的時候都會被問到這個問題,接下來就讓我帶領大家一起從淺入深學習一下秒殺的特性,常見的問題與解決方案。
在這裏插入圖片描述

秒殺的特性

用過某寶,某東之類的電商產品的朋友肯定對一般的電商流程是瞭然於心的,不外乎就是先查詢一下商品賣完了沒有,沒賣完就創建訂單,然後去扣減庫存,接着更新訂單,在一段時間內要完成付款操作,最後付款之後商家會盡快發貨。

在流程上,秒殺其實與正常的電商流程是大同小異的,只不過秒殺的特性會決定在一段極短的時間內會有大量的用戶訪問,也就是我們常說的高併發。一般來說,如果是某寶搞的秒殺活動,來個幾十萬流量不成問題吧,如果放任這些流量直接打到 DB 上,我們的服務器肯定是頂不住的,就算使用 Redis,由於單機的 Redis 大概只能夠有 3-4W 的 QPS,肯定還是遠遠不夠的。下面我們來考慮一下,應該怎麼設計一個秒殺系統,保護我方服務器,消化掉這些流量?

秒殺帶來的問題與解決方案

秒殺之前

在秒殺開始之前,爲了不錯過秒殺,大部分用戶都有不斷刷新瀏覽器頁面的習慣(我也有。。)。正常來說,這樣一直刷新,會一直訪問應用服務器,對其造成負載壓力(廢話,幾十萬人一直刷,誰承受的了!)

那麼如何解決呢?可以通過頁面內容靜態化(頁面可以緩存在 CDN 服務器上)來解決這個問題,讓用戶請求不需要經過應用服務,這樣豈不皆大歡喜?另一個需要注意的點是,購買按鈕也需要在秒殺之前置灰,當秒殺開始時才點亮,這也是爲了防止用戶一直點購買按鈕,給服務器帶來壓力。

對現有的業務造成衝擊

秒殺一般只是網站營銷的一個附屬,畢竟電商不可能天天秒殺對不對,肯定得有自己正常的業務吧。由於秒殺高併發的特性,非常容易會對現有的業務造成衝擊。

我們想想,如果把秒殺系統與網站的其它系統部署在一起,秒殺系統掛了,網站的其它系統估計也跑不了,那這個網站不就當場去世了?對於電商來說,這得損失多少錢啊!所以,秒殺系統只能獨立部署吧,得與網站其它系統完全隔離,你掛了就算了,可別連累兄弟們啊!

超賣

秒殺一般都是不怎麼賺錢的,大部分都是賠本賺吆喝,如果你賣的是日用品之類的還好,倒黴的話你賣的是什麼手機,電腦啊,發生超賣是很恐怖的。你本來只想賣50個,想着虧點小錢賺點名聲,結果好了,程序有 bug,直接賣了1000個,這不直接原地爆炸?

至於如何解決,我們要麼引入鎖,要麼嘗試扣減庫存,只有在扣減庫存成功之後纔會進行下單邏輯。這個在這裏大家留個印象就好了,在下面的部分我會詳細介紹。

惡意請求

你搞個秒殺得做宣傳吧,這個宣傳用戶可以看到,一些別有用心的人沒理由看不到啊,這麼低的價格,他們買下來,再轉手賣掉,豈不是血賺?

這些別有用心的人搞幾臺機器,搞個腳本,模擬個幾十萬人的請求,成功率不比你們這些小用戶高的多?就像演唱會,春運高鐵票之類的一個性質,只要黃牛參加搶票了,票子肯定都是秒光的,畢竟機器還是比人手要先進一點的。如果我們的秒殺不加以控制,說不定全部商品都被這些人搶去了,也不是一件稀奇的事情呢。

這類惡意請求一般每秒的請求次數非常誇張,絕對不是正常人可以有的手速,我們可以在網關層 Nginx 加以攔截,不然的話,他搶不搶的到倒是另一回事,這麼多的請求會給服務器帶來巨大的壓力,把服務器打崩也未可知。

鏈接暴露

懂點代碼的人如果想 “作弊”,也不難,直接用谷歌的開發者模式看你的網頁代碼就好了,說不定裏面就有 URL,有了這個 URL,可以做的事情就太多了,這個不用我教了吧(一臉壞笑)?

又或者,日防夜防,家賊難防,你開發對商品動心了咋辦?開發總得知道地址吧,在秒殺的時候提前請求,這怎麼防?

其實我們可以將 URL 動態化,即使開發也無法在秒殺開始前得到秒殺頁面的 URL。那麼話說回來,我們如何將 URL 動態化呢?這裏提供一種思路,可以在 URL 加入隨機數作爲參數,在秒殺開始時才能得到。

數據庫問題

我們上面說過了,正常大一點的秒殺活動來個幾十萬的流量不成問題,QPS 可能達到六位數的級別,直接打到數據庫上,我懷疑你的數據庫可能會當場去世。
在這裏插入圖片描述

我們考慮一下這個流程,一個用戶的請求進來,你就去數據庫查詢一下庫存,這好像不太 ok 吧,那怎麼辦?

這麼多的流量數據庫雖然頂不住,但是它的兄弟 Redis 卻是可以承受的!有讀者可能會問,咦,你不是說,單機的 Redis 只有 3-4W 的 QPS,這怎麼頂啊!打架總打過吧,一個人打不過,就叫一羣人來唄,Redis 同理啊,單機的搞不定,我弄個 Redis 集羣,這下總頂的住吧。

在秒殺開始之前,我們搞一個定時任務,提前把商品的庫存加載到 Redis 裏面去,整個流程就在 Redis 中操作,秒殺結束之後再異步去修改庫存就好了。

但是,新的問題出現了!如果庫存只剩下一個,這時候十個請求進來,分別在十個服務器中進行查詢,發現庫存還有。美滋滋地直接去扣庫存,結果直接變成了 -9,也就是我們上面提到的超賣,這可怎麼辦?

可以使用 Lua 腳本來解決這個問題,好,問題來了,什麼是 Lua 腳本呢?Lua 腳本類似於 Redis 事務,有一定的原子性,不會被其他命令插隊,可以完成一些 Redis 事務性的操作。 我們可以寫一個 Lua 腳本,裏面包含判斷庫存和扣減庫存的邏輯,然後弄一個開關,庫存到0之後修改一下開關,直接攔住全部請求,完美!

巨大流量

秒殺自帶高併發屬性,一秒幾十萬的流量不是夢,這麼大的流量,我們總得做點什麼,不能讓服務器照單全收吧。

其實這個好辦,我們搞個限流就行了,其中限流又分前端限流和後端限流,那麼這兩者有什麼區別呢?

前端限流就是按鈕不能讓你一直點,點了一下之後就得等一段時間之後才能繼續點擊,也是爲了保護服務器,後端限流就是當產品賣完之後,就直接 return false,拒絕後續請求就好了。

關於限流,我們還可以加入限流組件,例如 Hystrix 之類的。

除了限流,我們還可以搞搞別的花樣,例如做個降級,熔斷,隔離之類的,這些都是在設計系統的時候需要考慮的。

從另外一個角度考慮,商品數量只有幾百個的時候,直接改庫沒有問題,但如果商品數量上去了呢?怎麼辦?爲了保護服務器,我們可以採取削峯填谷的策略,將請求放進消息隊列中,然後慢慢消費,慢慢修改庫存就好了嘛。

總結

要設計一個秒殺系統並不是那麼容易的,要考慮的問題非常多,把上面我提到的問題解決了,設計一個小型的系統應該不成問題,不過要是精益求精,最好還是得多考慮一些別的問題(畢竟,博主只是說讓99.9%的面試官比大拇指,並沒說100%啊哈哈哈),多思考,多動手,多實踐,這纔是程序員進階的正確打開方式。
在這裏插入圖片描述

參考:阿里面試官問我:如何設計秒殺系統?我的回答讓他比起大拇指
秒殺系統架構分析與實戰

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