秒殺設計方案討論

2021-07-23 02:22:58 字數 2689 閱讀 7502

一、秒殺帶來了什麼?

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

搶訂單環節一般會帶來2個問題:

1、高併發

2、超賣

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

二、如何解決?

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

1、前端

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

a:擴容

加機器,這是最簡單的方法,通過增加前端池的整體承載量來抗峰值。

b:靜態化

將活動頁面上的所有可以靜態的元素全部靜態化,並儘量減少動態元素。通過cdn來抗峰值。

c:限流

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

或者活動入口的時候增加遊戲或者問題環節進行消峰操作。

d:有損服務

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

2、後端

那麼後端的資料庫在高併發和超賣下會遇到什麼問題呢?主要會有如下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-

1where (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,但是訂單數並沒有達到庫存閥值。

三、總結

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

2、後端兩條路【記憶體】+【排隊】

四、非技術感想

1、團隊的力量是無窮的,各種各樣的解決方案(先不談可行性)都是在小夥伴們七嘴八舌中討論出來的。我們需要讓所有人都發出自己的聲音,不要著急去否定。

2、優化需要從整體層面去思考,不要只糾結於自己負責的部分,如果只盯著乙個點思考,最後很可能就走進死胡同中了。

3、有很多東西以為讀過了就懂了,其實不然。依然還是需要實踐,否則別人的知識永遠不可能變成自己的。

4、多思考為什麼,會發生什麼,不要想當然。只有這樣才能深入進去,而不是留在表面。

TinyURL設計方案

現在貌似tinyurl很火爆,也逐漸成為一種流行趨勢。對應於php版本的tinyurl也有一些演算法,其實本質上來說是一種hash。除此之外,還有另外一種tinyurl方案 類似於http img.ly 其實這種設計 是最簡單的,沒有使用hash,而是遞增,這種的好 處就是資料庫 可以無限擴充套件,...

許可權設計方案

簡要介紹一下該許可權管理系統的特點,該系統功能上做到了靈活授權,操控細緻,許可權可以細到按鈕及超鏈級別,而且部署簡單,下面談談我自己的設計經驗。該系統主要功能如下 1 自定義操作動作 如增加 刪除 修改 審核等,不再是以前見過的那種粗粒度的 按模組分配許可權,或者稍微先進點的規定死某幾個操作了 2 ...

快取設計方案

redis提供的快取的api,但是在開發階段,如果每個人都自己呼叫原生api實現快取時,由於每個人的水平問題,會導致實現方案千差萬別,同事又很難統一管理維護 通過提供spring的annotation,實現快取方案的統一 target retention retentionpolicy.runtim...