如何解決秒殺的性能問題和超賣的討論

最近業務試水電商,接了一個秒殺的活。以前常常看到淘寶的同行們討論秒殺,討論電商,此次終於輪到咱們本身理論結合實際一次了。前端

ps:進入正文前先說一點我的感覺,以前看淘寶的ppt感受都懂了,等到本身出解決方案的時候發現仍是有不少想不到的地方其實都沒懂,再次驗證了「細節是魔鬼」的理論。而且一我的的能力有限,只有你們一塊兒討論才能想的更周全,更細緻。好了,閒話少說,下面進入正文。數據庫

 

1、秒殺帶來了什麼?後端

 


 

秒殺或搶購活動通常會通過【預定】【搶訂單】【支付】這3個大環節,而其中【搶訂單】這個環節是最考驗業務提供方的抗壓能力的。架構

搶訂單環節通常會帶來2個問題:併發

  一、高併發異步

  比較火熱的秒殺在線人數都是10w起的,如此之高的在線人數對於網站架構從前到後都是一種考驗。高併發

  二、超賣性能

  任何商品都會有數量上限,如何避免成功下訂單買到商品的人數不超過商品數量的上限,這是每一個搶購活動都要面臨的難題。優化

 

2、如何解決?網站


 

首先,產品解決方案咱們就不予討論了。咱們只討論技術解決方案

一、前端

面對高併發的搶購活動,前端經常使用的三板斧是【擴容】【靜態化】【限流】

  A:擴容

  加機器,這是最簡單的方法,經過增長前端池的總體承載量來抗峯值。

  B:靜態化

  將活動頁面上的全部能夠靜態的元素所有靜態化,並儘可能減小動態元素。經過CDN來抗峯值。

  C:限流

  通常都會採用IP級別的限流,即針對某一個IP,限制單位時間內發起請求數量。

  或者活動入口的時候增長遊戲或者問題環節進行消峯操做。

  D:有損服務

  最後一招,在接近前端池承載能力的水位上限的時候,隨機拒絕部分請求來保護活動總體的可用性。

 

二、後端

那麼後端的數據庫在高併發和超賣下會遇到什麼問題呢?主要會有以下3個問題:(主要討論寫的問題,讀的問題經過增長cache能夠很容易的解決)

  I: 首先MySQL自身對於高併發的處理性能就會出現問題,通常來講,MySQL的處理性能會隨着併發thread上升而上升,可是到了必定的併發度以後會出現明顯的拐點,以後一路降低,最終甚至會比單thread的性能還要差。

  II: 其次,超賣的根結在於減庫存操做是一個事務操做,須要先select,而後insert,最後update -1。最後這個-1操做是不能出現負數的,可是當多用戶在有庫存的狀況下併發操做,出現負數這是沒法避免的。

  III:最後,當減庫存和高併發碰到一塊兒的時候,因爲操做的庫存數目在同一行,就會出現爭搶InnoDB行鎖的問題,致使出現互相等待甚至死鎖,從而大大下降MySQL的處理性能,最終致使前端頁面出現超時異常。

 

針對上述問題,如何解決呢? 咱們先看眼淘寶的高大上解決方案:

  I:  關閉死鎖檢測,提升併發處理性能。

  II:修改源代碼,將排隊提到進入引擎層前,下降引擎層面的併發度。

  III:組提交,下降server和引擎的交互次數,下降IO消耗。

以上內容能夠參考丁奇在DTCC2013上分享的《秒殺場景下MySQL的低效》一文。在文中全部優化都使用後,TPS在高併發下,從原始的150飆升到8.5w,提高近566倍,很是嚇人!!!

 

不過結合咱們的實際,改源碼這種高大上的解決方案顯然有那麼一點不切實際。因而小夥伴們須要討論出一種適合咱們實際狀況的解決方案。如下就是咱們討論的解決方案:

首先設定一個前提,爲了防止超賣現象,全部減庫存操做都須要進行一次減後檢查,保證減完不能等於負數。(因爲MySQL事務的特性,這種方法只能下降超賣的數量,可是不可能徹底避免超賣)

update number set x=x-1 where (x -1 ) >= 0;

 

解決方案1:

將存庫從MySQL前移到Redis中,全部的寫操做放到內存中,因爲Redis中不存在鎖故不會出現互相等待,而且因爲Redis的寫性能和讀性能都遠高於MySQL,這就解決了高併發下的性能問題。而後經過隊列等異步手段,將變化的數據異步寫入到DB中。

優勢:解決性能問題

缺點:沒有解決超賣問題,同時因爲異步寫入DB,存在某一時刻DB和Redis中數據不一致的風險。

 

解決方案2:

引入隊列,而後將全部寫DB操做在單隊列中排隊,徹底串行處理。當達到庫存閥值的時候就不在消費隊列,並關閉購買功能。這就解決了超賣問題。

優勢:解決超賣問題,略微提高性能。

缺點:性能受限於隊列處理機處理性能和DB的寫入性能中最短的那個,另外多商品同時搶購的時候須要準備多條隊列。

 

解決方案3:

將寫操做前移到MC中,同時利用MC的輕量級的鎖機制CAS來實現減庫存操做。

優勢:讀寫在內存中,操做性能快,引入輕量級鎖以後能夠保證同一時刻只有一個寫入成功,解決減庫存問題。

缺點:沒有實測,基於CAS的特性不知道高併發下是否會出現大量更新失敗?不過加鎖以後確定對併發性能會有影響。

 

解決方案4:

將提交操做變成兩段式,先申請後確認。而後利用Redis的原子自增操做(相比較MySQL的自增來講沒有空洞),同時利用Redis的事務特性來發號,保證拿到小於等於庫存閥值的號的人均可以成功提交訂單。而後數據異步更新到DB中。

優勢:解決超賣問題,庫存讀寫都在內存中,故同時解決性能問題。

缺點:因爲異步寫入DB,可能存在數據不一致。另可能存在少買,也就是若是拿到號的人不真正下訂單,可能庫存減爲0,可是訂單數並無達到庫存閥值。

 

3、總結


 

一、前端三板斧【擴容】【限流】【靜態化】

二、後端兩條路【內存】+【排隊】

 

4、非技術感想


 

一、團隊的力量是無窮的,各類各樣的解決方案(先不談可行性)都是在小夥伴們七嘴八舌中討論出來的。咱們須要讓全部人都發出本身的聲音,不要着急去否認。

二、優化須要從總體層面去思考,不要只糾結於本身負責的部分,若是隻盯着一個點思考,最後極可能就走進死衚衕中了。

三、有不少東西覺得讀過了就懂了,其實否則。依然仍是須要實踐,不然別人的知識永遠不可能變成本身的。

四、多思考爲何,會發生什麼,不要想固然。只有這樣才能深刻進去,而不是留在表面。

 

ps:以上僅僅是咱們討論的一些方案設想,歡迎你們一塊兒討論各類可行方案。 

相關文章
相關標籤/搜索