高并發秒殺系統分析

qam37b88 7年前發布 | 20K 次閱讀 并發 CDN 數據庫 軟件架構

項目源碼:

一、秒殺系統中存在高并發的點

一個秒殺系統的基本流程基本如上所示

用戶請求詳情頁,系統時間,請求秒殺接口,執行秒殺操作都是位于服務端,都會被大量訪問,那么我們優化系統高并發就是從這四點著手

1. 請求詳情頁的優化

詳情頁是屬于靜態濟源,例如css,js等,對于這些靜態資源,如果全部放在服務端主機中,勢必對服務主機造成很大的壓力,并發量也得不到支持,我們可以使用CDN來進行優化

什么是CDN?

使用CDN會極大地簡化網站的系統維護工作量, 網站維護人員只需將網站內容注入CDN的系統,通過CDN部署在各個物理位置的服務器進行全網分發,就可以實現跨運營商、跨地域的用戶覆蓋 。由于CDN將內容推送到網絡邊緣,大量的用戶訪問被分散在網絡邊緣,不再構成網站出口、互聯互通點的資源擠占,也不再需要跨越長距離IP路由了

傳統的B/S架構:

B/S架構,即Browser-Server(瀏覽器 服務器)架構,是對傳統C/S架構的一種變化或者改進架構。在這種架構下,用戶只需使用通用瀏覽器,主要業務邏輯在服務器端實現。B/S架構,主要是利用了不斷成熟的WWW瀏覽器技術,結合瀏覽器的多種Script語言(VBScript、JavaScript等)和ActiveX等技術,在通用瀏覽器上實現了C/S架構下需要復雜的軟件才能實現的強大功能。

①用戶在自己的瀏覽器中輸入要訪問的網站域名。

②瀏覽器向本地DNS服務器請求對該域名的解析。

③本地DNS服務器中如果緩存有這個域名的解析結果,則直接響應用戶的解析請求。

④本地DNS服務器中如果沒有關于這個域名的解析結果的緩存,則以遞歸方式向整個DNS系統請求解析,獲得應答后將結果反饋給瀏覽器。

⑤瀏覽器得到域名解析結果,就是該域名相應的服務設備的IP地址。

⑥瀏覽器向服務器請求內容。

⑦服務器將用戶請求內容傳送給瀏覽器

加入cdn后:

在網站和用戶之間加入CDN以后,用戶不會有任何與原來不同的感覺。最簡單的CDN網絡有一個DNS服務器和幾臺緩存服務器就可以運行了。一個典型的CDN用戶訪問調度流程

①當用戶點擊網站頁面上的內容URL,經過本地DNS系統解析,DNS系統會最終將域名的解析權交給CNAME指向的CDN專用DNS服務器。

②CDN的DNS服務器將CDN的全局負載均衡設備IP地址返回用戶。

③用戶向CDN的全局負載均衡設備發起內容URL訪問請求。

④CDN全局負載均衡設備根據用戶IP地址,以及用戶請求的內容URL,選擇一臺用戶所屬區域的區域負載均衡設備,告訴用戶向這臺設備發起請求。

⑤區域負載均衡設備會為用戶選擇一臺合適的緩存服務器提供服務,選擇的依據包括:根據用戶IP地址,判斷哪一臺服務器距用戶最近;根據用戶所請求的URL中攜帶的內容名稱,判斷哪一臺服務器上有用戶所需內容;查詢各個服務器當前的負載情況,判斷哪一臺服務器尚有服務能力。基于以上這些條件的綜合分析之后,區域負載均衡設備會向全局負載均衡設備返回一臺緩存服務器的IP地址。

⑥全局負載均衡設備把服務器的IP地址返回給用戶。

⑦用戶向緩存服務器發起請求,緩存服務器響應用戶請求,將用戶所需內容傳送到用戶終端。如果這臺緩存服務器上并沒有用戶想要的內容,而區域均衡設備依然將它分配給了用戶,那么這臺服務器就要向它的上一級緩存服務器請求內容,直至追溯到網站的源服務器將內容拉到本地。

DNS服務器根據用戶IP地址,將域名解析成相應節點的緩存服務器IP地址,實現用戶就近訪問。使用CDN服務的網站,只需將其域名解析權交給CDN的GSLB設備,將需要分發的內容注入CDN,就可以實現內容加速了。

2. 獲取系統時間操作的優化

獲取系統時間的操作不用優化,java訪問一次內存大概10ns,1,000,000,000 納秒 = 1秒 ,也就是說1s中可以訪問1,000,000,00次內存,所以根本不需要我們去優化!

3. 秒殺地址接口獲取的優化

cdn適合存放不變的內容,例如css,js等靜態內存,所以它不適合放在cdn中緩存,但是適合在服務端緩存,例如redis,甚至可以做redis集群。

4. 秒殺操作高并發的問題(重點)

1. 這個也是不能使用cdn緩存的,但是能不能使用redis做緩存呢?秒殺首先會在數據庫中減庫存,那我們能在redis緩存中做減庫存操作嗎?肯定不可以,因為這會導致數據一致性的問題,凡是需要進行寫操作的數據都不適合做緩存。

2. 高并發的點還在于,熱點商品競爭上。當多個用戶在秒殺同一個商品時,由于mysql的事務機制和行級鎖,一個用戶在獲取該商品額行級鎖進行減庫存操作時,其它的用戶只能等待,這就變成了串行的操作,這就是秒殺操作高并發中最困難的優化點

二、如何優化秒殺操作

第一個方案:

首先,可以用NoSQL例如redis作為一個原子計數器,記錄商品的庫存,當用戶秒殺該商品時,該計數器便減1;

然后記錄哪個用戶秒殺了該商品,作為一個消息,存儲到分布式mq中(例如alibaba的rocketMQ);

最后由服務端的服務去執行數據庫的update操作。

這個方案有什么問題呢?

1. 運維和不穩定性,NoSQL不如MySQL穩定,所以需要高水平的運維團隊

2. 重發秒殺的問題。在記錄行為信息中,分布式MQ只知道記錄哪個用戶在秒殺該商品,但是不知道該用戶是否已經重復秒殺過該商品,因此還需要另外維護一個NoSQL,來記錄哪些用戶已經秒殺了哪些商品,加大了成本

第二個方案:在mysql上優化

我們之所以考慮第一個方案,就是因為MySQL低效,為什么低效

我們來看看mysql執行一條update的并發量:

可以看到,mysql可以抗住大約4wQPS,那么MySQL本身是不低效的,但是是什么地方使得它低效呢?

我們分析一下瓶頸所在:

服務端的程序和數據庫一般放在不同的主機上,當服務端進行減庫存操作時,會發送update的sql語句到數據庫中,這里會存在一個網絡延遲和gc,同樣insert語句記錄秒殺明細也會存在網絡延遲和gc,假如一次的網絡延遲和gc加起來的總延遲是2ms,那么1s就只能進行500次的秒殺操作

同城機房:

異地機房:

優化:把服務器的執行邏輯放在mysql服務端,避免網絡延遲和gc延遲

兩種方案:

1. 定制sql方案:update /* + [auto_commit] + */,需要修改mysql源碼(太困難)

2. 使用存儲過程,在mysql中去完成整個秒殺操作的事務(即把下面這個操作放到存儲過程中),雖然存儲過程在互聯網中很少用,但是在

 

 

來自:http://blog.csdn.net/jeffleo/article/details/56015710

 

 本文由用戶 qam37b88 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!