秒殺系統設計&測試


秒殺系統設計

說起秒殺,從雙十一購物到春節搶紅包,再到逢年過節搶⻋票,“秒殺”的場景在我們的生活中處處可⻅。簡單來說,秒殺就是在同一個時刻有大量的請求,爭搶購買同一個商品並完成交易的過程。

不管校招,還是社招,如何設計一個秒殺系統的面試題經常出現,如果懂得其中原理,就可以對答如流,不過涉及到一些瓶頸優化,有些同學就未必都能答出。

面試官:簡單說一下秒殺系統的設計思路?

這種題目,小菜是准備過的,巴拉巴拉的說了一堆。

面試官:那這里是怎么保證秒殺成功的?

小菜:&8^%#

面試官:你這里用了Redis,有什么用?

小菜:#¥&……%

面試官:你用什么測試過這個系統的並發量?

小菜:&*%$^&.

面試官:你覺得你這個系統還可以再優化么?

小菜:&%……¥)

面試官:你知道這個系統的瓶頸在哪里嗎?如果流量再大10倍,怎么應對?

小菜:88!

秒殺系統的整體架構可以概括為“穩、准、快”。

整個系統架構要滿足高可用,流量符合預期時肯定要穩定,超出預期時也同樣不能掉鏈子,你要保證秒殺活動順利完成,即秒殺商品順利地賣出去,這個是最基本的前提。

你的業務需求是秒殺10台iPhone XS,那就只能成交10台,多一台少一台都不行。一旦庫存不對,那平台就要承擔損失。

就是說系統的性能要足夠高,否則你怎么支撐這么大的流量呢?不光是服務端要做極致的性能優化,而且在整個請求鏈路上都要做協同的優化,每個地方快一點,整個系統就完美了。

設計思路:將請求攔截在系統上游,降低下游壓力。在一個並發量大,實際需求小的系統中,應當盡量在前端攔截無效流量,降低下游服務器和數據庫的壓力,不然很可能造成數據庫讀寫鎖沖突,甚至導致死鎖,最終請求超時。

限流:前端直接限流,允許少部分流量流向后端。

削峰:瞬時大流量峰值容易壓垮系統,解決這個問題是重中之重。常用的消峰方法有異步處理、緩存和消息中間件等技術。

異步處理:秒殺系統是一個高並發系統,采用異步處理模式可以極大地提高系統並發量,其實異步處理就是削峰的一種實現方式。

內存緩存:秒殺系統最大的瓶頸一般都是數據庫讀寫,由於數據庫讀寫屬於磁盤IO,性能很低,如果能夠把部分數據或業務邏輯轉移到內存緩存,效率會有極大地提升。

消息隊列:消息隊列可以削峰,將攔截大量並發請求,這也是一個異步處理過程,后台業務根據自己的處理能力,從消息隊列中主動的拉取請求消息進行業務處理。

可拓展:當然如果我們想支持更多用戶,更大的並發,最好就將系統設計成彈性可拓展的,如果流量來了,拓展機器就好了,像淘寶、京東等雙十一活動時會臨時增加大量機器應對交易高峰。

 

秒殺系統測試

對於大並發量的系統,有幾個可能需要優化的點,下面我們要一步步測試來優化這個系統。

測試目標
對於一個系統,幾個常用的評價指標是:平均響應時間、吞吐率、qps等。我的測試主要測試3個接口

主頁(訪問根路徑,沒有數據庫交互)
秒殺接口暴露(暴露秒殺接口,有后台數據交互)
執行秒殺操作(插入秒殺成功記錄和減庫存一個完整的事務操作)
對於這三個接口,我們主要的測試目標和優化目標是平均響應時間,當然這是建立在數據正確返回的基礎上的,失敗率太高那這個平均響應時間是沒有意義的。
這里的優化側重於后端數據庫和內存方面的優化。

測試環境
我是在Windows10下用jmeter來進行負載測試和壓力測試,其他環境如下,涉及具體配置再提。

Tomcat8.0.38
Jdk1.8 hotspot vm
Mysql 5.7
Redis 2.7.3
測試過程
首先進行主頁測試,我們訪問tomcat的主頁,使用jmeter的線程組中的線程數模擬用戶數,不斷增加線程數對主頁進行性能測試。
我們將結果數據寫到一個xml文件中。首先我們模擬5000個用戶同時請求主頁。

5000個用戶同時請求主頁
設置循環次數為2,即一共有10000個請求將被發送。

 

從響應的結果可以看到,沒有錯誤數,這10000個請求全部返回成功了,只是有的請求慢有的請求快。平均的響應時間在300ms,50%的請求的響應時間平均為87ms。到后面越來越多的請求開始等待,這里可以想到的優化的點在於tomcat的線程池中線程的數量,越來越多的請求在等待隊列中。查看tomcat的配置后發現最大線程數為maxThreads=”150”,好那我們用150個線程,循環10次,也就是一共1500個請求,那結果會是什么樣呢?

 平均相應時間為5ms,前50%的請求的平均響應時間為1ms。

但是這里並不能直接修改tomcat的最大線程數來優化。復雜點說就是這是一個復雜的東西,線程數越大,你也要有相應的cpu來執行啊。直接點說就是,我不懂。。。
我把tomcat的線程數設置為500,然后起5000個線程發送10000個請求,然后得到了:

 

比之前的更差了。無論是平均相應時間還是錯誤率。簡單粗暴的去改線程數是不可行的。這里我們不去管tomcat的線程數或者是其他層面的優化,我們只專注於后端數據庫層面的優化。

500個用戶同時請求暴露秒殺接口
為什么用500個,是為了減少因為tomcat請求等待帶來的數據誤差。
直接向MySQL請求數據
先模擬500個用戶,每個用戶發送10次請求。該請求相應的操作為根據id向數據庫查詢一條記錄。得到了這樣的數據。 

期間打開windows的性能監控器,發現磁盤IO有變化,IO百分比最高的時候也不超過15%。
這樣的操作,錯誤率為0,相當穩定,平均響應時間為1406ms。
模擬5000個用戶,每個用戶發送一次請求。、

磁盤的IO百分比一度達到了100%。從數據的絕對值來看,這樣的測試沒有意義了,因為瓶頸不在MySQL,而在我們前面分析過的tomcat。但是數據的相對值是有意義的。

使用redis緩存數據
還是模擬500個用戶,每個用戶發送10次請求。

響應速度顯著提高,注意一個值,Min=1,有些請求幾乎不足1ms,因為redis直接從內存讀取數值,非常快。如果不是tomcat的請求在排隊,我想平均響應時間是個位數。
Redis下模擬5000個用戶,每個用戶發送一次請求。會是什么結果呢?

 

使用150個線程,循環100次,即發送15000次請求,得到:

可以看到,150個用戶的話這種響應速度是可以接受的,響應的瓶頸在於tomcat的請求排隊等待。

———–我是分割線—————-

這個優化的過程我想到了很多東西,感覺就是,優化是無止盡的。
比如,我想到了內存回收那一塊。選用合適的垃圾收集器,盡可能地減少GC時stop the world的時間和次數顯然對於一個秒殺系統來說是非常對的優化方向。這里我嘗試用過幾款垃圾收集器比如parNew,G1來對比他們的平均響應時間,但是多次測試后沒有明顯的差距。有兩個原因,一是這個接口沒有產生太多的大對象,二是這個優化並不太明顯。后面有機會的話還是希望繼續在內存方面進行優化,感覺內存回收方面有點神秘,很想試一試。

————我是分割線————-

可以看到redis的使用很大程度上提高了響應的時間。上面那個接口只是暴露一個地址,這些地址每個產品都只有一個,那這樣的場景是可以用redis的。但是有些操作並沒有辦法使用緩存。比如執行秒殺這個操作。
這個操作是個事務型操作。如果其中一個操作失敗了,我就讓他rollback,這樣的話,應該會有更多的並發問題。

 

轉載自:

https://blog.csdn.net/weixin_34109408/article/details/89623725

https://blog.csdn.net/OWEN_7/article/details/78319115


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM