高并發秒殺系統分析
項目源碼:
一、秒殺系統中存在高并發的點
一個秒殺系統的基本流程基本如上所示
用戶請求詳情頁,系統時間,請求秒殺接口,執行秒殺操作都是位于服務端,都會被大量訪問,那么我們優化系統高并發就是從這四點著手
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