1. 程式人生 > >高併發秒殺系統總結

高併發秒殺系統總結

大家也許開發過高併發的系統或者秒殺程式,但肯定都有接觸過,像電商平臺的秒殺、搶購等活動,還有12306春運搶票。

活動週期短,瞬間流量大(高併發),技術在這種情況下,會發生和要做的事。

第一:高併發

技術要做的事,一方面優化程式,讓程式效能最優,單次請求時間能從50ms優化到25ms,那就可以在一秒鐘內成功響應翻倍的請求了。

另一方面就是增加伺服器,用更大的叢集來處理使用者請求,設計好一個可靠且靈活擴充的分散式方案就更加重要了。

第二:時間短

火熱的秒殺活動,真的是一秒鐘以內就會把商品搶購一空,而大部分使用者的感受是,提交訂單的過程卻要等待好幾秒、甚至十幾秒,更糟糕的當然是請求報錯。

那麼一個好的秒殺體驗,當然希望儘可能減少使用者等待時間,準確的提示使用者當前是否還有商品庫存。而這些,也是需要有優秀的程式設計來保證的。

第三:系統容量預估

系統設計的時候,都需要有一個容量預估,那就是要提前計算好,我們設計的系統,要承載多大的數量級。

假如線上前端伺服器規格是8核16G記憶體的伺服器,而提交訂單的處理程式耗時100ms,那麼可以簡單計算一下:

每秒可以處理的訂單請求數=1000ms/100ms*8=80qps

上面這個結果,對於秒殺系統來說,肯定是非常不理想的。

如果能將處理程式耗時優化後,降低到10ms,那麼就可以達到800qps。

如果我們可以把程式繼續優化,能快速區分開有庫存和無庫存處理,那麼無庫存時處理就有可能做到1ms甚至更低的耗時。這樣無庫存時就能有更好的效能,上萬的qps也是可以達到的。

上面的預估,都是針對單機,那麼簡單的增加前端伺服器,是不是就能有更好的併發處理量呢?

肯定沒這麼簡單,因為資料庫、快取系統甚至機房網路頻寬都會成為瓶頸。

於是就要有一個更好的分散式方案。

第四:好的分散式方案

一個好的分散式方案,首先當然是穩定可靠,不要出亂子,然後就是方便擴充,最好的效果當然是增加一臺伺服器,併發處理量可以1:1線性增長。

比如:單機qps是1k,那麼10臺伺服器可以做到1w,100臺可以做到10w每秒。

要做到這樣的線性增長效果,就要杜絕出現瓶頸,否則還是會代價太大。

拒絕假的分散式尤其重要,比如:前端伺服器是可以獨立存在的,但是都依賴集中的一個數據庫或者快取系統,那最後,一定是集中的那個資料庫或者快取系統受不了,同樣無法做到一個好的分散式。

第五:關注系統的瓶頸

大家先有幾個基本的共識,系統的處理速度

  • 程式內資料讀寫 > redis > mysql > 磁碟
  • 單機網路請求 > 區域網內請求 > 跨機房請求

我們優化程式的時候,儘量用最快的方式,儘量用最簡短的邏輯。

用redis替代mysql來儲存訂單處理中依賴的資料,用程式中的提交的資料代替從redis中二次獲取資料,比如:商品庫存資訊,使用者訂單資訊。

邏輯處理中,把速度快且提前中斷的邏輯放在最前面,比如:驗證登入,驗證問答。

我們做分散式方案的時候,儘量把資源呼叫放在最近的地方。

前端伺服器依賴的資料儘量就在區域網內,如果能在單機都有讀的redis服務當然更好,程式維護資料響應會複雜些。

不要出現跨機房網路請求,不要出現跨機房網路請求,不要出現跨機房網路請求,重要的事情說三遍。

第六:什麼語言更適合這類系統

課程中用的是PHP語言,開發這類系統也是沒問題的。

當然,像是用golang, ngx_lua可能在高併發和效能方面會更有優勢。

如果使用java、.net當然也是可以的,作為一個系統,語言只是工具,更好的設計和優化,才能達到最終想要的效果。

有了上面的基本概念,我們接下來再來看看,具體執行時,會出現什麼狀況。

下面是一些具體的問題:

問題1:庫存超賣

只有10個庫存,但是一秒鐘有1k個訂單,怎麼能不超賣呢?

核心思想就是保證庫存遞減是原子性操作,10--返回9,9--返回8,8--返回7。

而不能是讀取出來庫存10,10-1=9再更新回去。因為這個讀取和更新是併發執行的,很可能就會有1k個訂單都成功了,而庫存實際只有10。

那麼,怎麼保證原子性操作呢?

1. 資料庫:

update product set left_numleft_num=left_num-1 where left_num>0; 

這裡用到的是left_num=left_num-1,如果left_num>0才能執行成功,資料庫查詢、更新的時候有用到鎖,是可以保證更新操作的原子性的。

資料庫效能較差,不建議使用。

2. 分散式鎖

用redis來做一個分散式鎖,reids->setnx('lock', 1) 設定一個鎖,程式執行完成再del這個鎖。

鎖定的過程,不利於併發執行,大家都在等待鎖解開,不建議使用。

3. 訊息佇列

將訂單請求全部放入訊息佇列,然後另外一個後臺程式一個個處理佇列中的訂單請求。

併發不受影響,但是使用者等待的時間較長,進入佇列的訂單也會很多,體驗上並不好,也不建議使用。

4. redis遞減

通過 redis->incrby('product', -1) 得到遞減之後的庫存數。

效能方面很好,同時體驗上也很好,在PHP秒殺課程中,優化後就是用的這種方法,而沒有使用上述其他方法,大家應該也能對比了解啦。

問題2:叢集怎麼來規劃

前端伺服器因為沒有相互間關聯,叢集的數量不受影響。

redis的效能可以達到每秒幾萬次響應,所以一個叢集的規模,也就是redis服務可以承載的數量。

比如:一臺前端伺服器是1~2k的qps(有庫存時),那麼10臺+1臺redis就可以是一個獨立的叢集,可以支撐1~2w每秒訂單量。

10個上述的叢集就可以做到一秒鐘處理10w~20w的有效訂單。

如果秒殺活動的庫存量在1w以內,預計參與的人數在百萬左右,那麼有一個叢集也就可以搞定。

如果秒殺參與的人數超過千萬,那麼就要用到不止一個叢集了。

問題3:多個叢集的資料怎麼保持一致性

不要做多叢集的資料同步,而是用雜湊,每個叢集的資料是獨立存在的。

假設,有10個商品,每個商品有1w庫存,規劃用10個叢集,那麼每個叢集有10個商品,每個商品是1k庫存。

每個叢集只需要負責把自己的庫存賣掉即可,至於說,會不會有使用者知道有10個叢集,然後每個叢集都去搶。

這種情況就不要用程式來處理了,利用運營規則,活動結束後彙總訂單的時候再去處理就好了。

如果擔心雜湊的不合理,比如:某個叢集使用者訪問量特別少,那麼可以引入一箇中控服務,來監控各個叢集的庫存,然後再做平衡。

問題4:機器人搶購怎麼辦:

沒什麼太好的辦法,類似DDOS攻擊,只能是讓自身更強大才是王道。

運營策略上,可以嚴格控制使用者註冊,必須登入,提交訂單的時候引入影象驗證碼,問答,互動式驗證等。

如果你現在在JAVA這條路上掙扎,也想在IT行業拿高薪,可以參加我們的訓練營課程,選擇最適合自己的課程學習,技術大牛親授,7個月後,進入名企拿高薪。我們的課程內容有:Java工程化、高效能及分散式、高效能、深入淺出。高架構。效能調優、Spring,MyBatis,Netty原始碼分析和大資料等多個知識點。如果你想拿高薪的,想學習的,想就業前景好的,想跟別人競爭能取得優勢的,想進阿里面試但擔心面試不過的,你都可以來,q群號為:760940986

注:加群要求

1、具有1-5工作經驗的,面對目前流行的技術不知從何下手,需要突破技術瓶頸的可以加。

2、在公司待久了,過得很安逸,但跳槽時面試碰壁。需要在短時間內進修、跳槽拿高薪的可以加。

3、如果沒有工作經驗,但基礎非常紮實,對java工作機制,常用設計思想,常用java開發框架掌握熟練的,可以加。

4、覺得自己很牛B,一般需求都能搞定。但是所學的知識點沒有系統化,很難在技術領域繼續突破的可以加。

5.阿里Java高階大牛直播講解知識點,分享知識,多年工作經驗的梳理和總結,帶著大家全面、科學地建立自己的技術體系和技術認知!