【系統設計】秒殺系統的設計思路

 

       還記得校招剛開始的時候,作爲一個非科班、無實習經驗、手上只有一個後臺管理項目的渣渣來說,那面試真是一個酸爽,各種猛錘吊打啊。但是最後,自己還是拿到了幾個offer?是什麼原因呢?是運氣嗎?有可能,但其中還有一個重要原因就是我在校招後期花了一個星期,囫圇吞棗的跟着視頻完成了一個秒殺項目,這個項目爲自己面試加了不少分。你看,囫圇吞棗都有這種功效,說明這個項目還是很牛逼滴!!!其實秒殺的業務邏輯還是不復雜,就一個減庫存,下訂單,但中間處理高併發的一些思路與方法很值得我們在其他的場景中借鑑(不僅僅是編程喲),所以今天就大致講講秒殺設計的思路

 

秒殺場景的特點:

  • 高併發  ---> 逐層分流

  • 讀多寫少  ---> 使用緩存

 

應該考慮的問題:

  1. 高併發:短時間內大量請求(假設使用redis緩存,如果QPS超過了redis的承受範圍(3~4W),緩存會被擊穿,直接滲透到DB,從而擊垮DB)

  2. 接口防刷:用戶使用秒殺軟件來請求,需過濾這些重複無效請求

  3. 秒殺url:提前知道秒殺url,繞過前端直接刷票

  4. 超賣:備貨100,超賣200(秒殺價格都很低,超賣會嚴重影響公司利益)

  5. 數據庫設計:秒殺有把數據庫擊垮的風險,儘量使秒殺業務的數據不和其他業務數據放在同一個數據庫

 

針對上面問題的解決方案:

1. 秒殺頁面靜態化:將商品描述、圖片等不會變化的信息寫入到靜態頁面,用戶請求時直接由前臺客戶端生成返回

2. 接口限流:

前端:用戶點擊秒殺按鈕後,接下來的5秒內是不能點擊的

後端:(1) 使用Redis中的Hash,key爲商品id,field爲userId,value爲業務屬性,過期時間需要根據實際業務和我秒殺人數決定,一般10秒

         (2) 令牌桶算法

3. 使用nginx服務器:nginx服務器是一個高性能web服務器,併發能力可達到幾萬,而tomcat只有幾百。通過nginx映射客戶端請求,再分發到後臺tomcat服務器集羣可以大大提高併發能力

4. redis預減庫存:我們可以用redis來預減庫存。預設一個庫存值,使用redis的list結構,通過push和pop原子操作保證庫存增減的原子性。如果庫存已經沒了,就直接返回搶購失敗的提示。

5. 使用集羣redis:考慮到緩存擊穿問題,我們可以構建redis集羣,採用哨兵模式,如果master掛了,可以自動切換到slave,提升redis的性能和可用性

6. 精簡sql+樂觀鎖:我們在扣減庫存時,我們可以將查庫和更新操作合併成一個sql,並採用樂觀鎖機制,我們先查出版本號version,再執行更新語句:UPDATE  t_store  SET  store_count = store_count - 1, version = version + 1  WHERE  goods_id = ? AND version = ? AND store_count > 0;當範圍結果爲0時,有兩種情況,一種是庫存不足,返回提示消息給客戶就行了,第二種就是樂觀鎖生效,返回消息給客戶提示再進行一次(雖然肯定搶不到了)

7. 異步下單:經過限流,庫存校驗後,流入到這一步驟就是有效請求,爲了提高下單效率,防止下單服務失敗,我們將下單這一操作異步處理,最常用的就是使用消息隊列,隊列的優點是:異步,消峯,解耦;下單入庫沒問題後,我們就可以通知用戶秒殺成功,如果失敗,可以採用補償機制重試

8. 服務降級:秒殺過程中出現某個服務器宕機,我們可以對服務進行熔斷和降級

 

注意事項

1. redisCluster採用一hash一致性算法,所以不同的數據存在不同的redis節點上,所以當我們秒殺一個商品時,我們的數據請求是訪問同一個redis節點。如果對於多個商品的秒殺,爲了防止多個商品key落在同一redis節點,我們可以通過命令將不同的key強制放在不同的redis節點上

 

今天就講這麼多,太晚了要睡覺了,明天還要上班,雖然是在家遠程上班,但還是不能“遲到”呀,希望疫情能快快變好,就算讓我無償加幾天班我都願意,嗯嗯,不能超過五天,超過了還是要給調休的。。。不開玩笑了,真心希望疫情快點過去,大家都能健康平安,中國加油,武漢加油,大家加油!

 

 

歡迎大家關注公衆號:程序員進階之路,裏面記錄了一個非科班程序員的成長之路

                                                       

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