如何設計電商行業億級使用者秒殺系統

奈學教育發表於2020-06-08

電商行業在近十幾年中,經歷過大大小小的促銷活動和秒殺上百次,每次做秒殺瞬時訪問量會翻數十倍,甚至數百倍。對系統架構是巨大的考驗,期間也曾經歷過系統當機,甚至整體雪崩。那麼我們怎麼設計秒殺系統,才能保證秒殺系統的高效能和穩定性,同時還要保證日常業務不受影響呢?

先看看秒殺場景特點。秒殺開始前幾分鐘,大量使用者開始進入秒殺商品詳情頁面,很多人開始頻繁重新整理秒殺商品詳情頁,這時秒殺商品詳情頁訪問量會猛增。秒殺開始,大量使用者開始搶購,這時建立訂單,扣庫存壓力會顯著增大。實際上,秒殺場景基本都是秒殺參與人多,秒殺成功的人卻寥寥無幾,經常是幾十萬人或者更多人搶幾百個商品庫存。

那麼我們曾經是怎麼設計秒殺系統的呢?主要涉及以下幾個方面:

秒殺業務流程上的考慮:

由於參加秒殺的商品售賣價格非常低,基本都是“搶到即賺到”,成功下單後卻不付款的情況非常少。所以我們採用下單減庫存的方案,下單時扣減庫存,然後再進行支付。假如真有個別訂單不付款怎麼辦?沒關係,秒殺好活動最主要的目的是吸引流量,個別訂單不支付對秒殺活動本身影響不大。況且,沒支付剩下的庫存還可以做為普通商品繼續售賣。不過要注意對機器人和自動指令碼的防禦,後面會詳細介紹。

頁面靜態化:

“秒殺開始前幾分鐘,大量使用者開始進入秒殺商品詳情頁面,很多人開始頻繁重新整理秒殺商品詳情頁,這時秒殺商品詳情頁訪問量會猛增”。如果請求全部打到後端服務,那後端服務的壓力會非常大(後端服務要處理業務邏輯,而且還要訪問資料庫,吞吐量比較低)。

考慮到秒殺是運營同學提前安排的活動,要秒殺哪些商品、商品價格等資訊在秒殺活動開始前已經確定下來,所以我們可以把秒殺商品詳情頁做成靜態頁面,把商品詳情、商品價格等引數、評論評價等資訊全部放在這個靜態頁面裡,然後把這個靜態頁面上傳到CDN上預熱(CDN是內容分發網路,可以簡單理解成網際網路上的巨大的快取,用於存放靜態頁面、圖片、影片等,可以顯著提高訪問速度),用CDN扛流量,這樣大量的商品詳情頁的訪問請求就不用訪問自己的網站(源站)。這樣既可以提高訪問速度,也沒有給網站增加壓力,同時也減少了網站頻寬壓力。

請求攔截:

前端頁面,相關按鈕點選後置灰,防止重複提交

閘道器(zuul,nginx)層,為了避免前端惡意請求,比如一些攻擊指令碼,在閘道器層要對下單等介面按userID限流,幾秒鐘只能訪問一次。考慮到秒殺場景參與人多,秒殺成功的人極少,我們可以把絕大部分搶購下單請求在閘道器層直接拒掉,按秒殺失敗處理。這樣就極大減少了後端服務的壓力。

假設秒殺庫存是200個,我們可以只放行200個請求到後端服務。要注意,為了儘量避免庫存被機器人和自動指令碼搶走,200個請求不能在秒殺開始瞬間同時放行,可以分段放行,比如秒殺開始後隨機選取100ms內的5個請求放行(這100ms內的其他請求直接拒掉,按秒殺失敗處理),之後每隔100ms放行5個請求,4秒鐘可以放行完200個請求。分段放行,除了限制了機器人和自動指令碼,把請求分散在各個時間段,還進一步緩解了後端服務的壓力。

分段放行總時間不能太長,假如每100ms放行1個請求,放行完所有200個請求需要20秒時間,這樣使用者就會明顯感知到下單早的人沒秒殺成功,下單晚的人反而秒殺成功了,使用者體驗會變差。

另外,秒殺過程閘道器壓力會比較大,閘道器可以做成叢集,多節點分攤訪問壓力。

後端服務設計:

如果秒殺庫存只有200,經過閘道器攔截,再加上採用分段放行的方式,對於後端服務基本沒什麼壓力了,日常的後端服務就完全可以支撐秒殺活動了。不用再做更復雜的設計。不過,假如秒殺庫存有幾萬個,放行的下單請求就有幾萬個,為了使用者體驗放行總時間也不能太長,這時後端服務該怎麼設計呢?
這時主要壓力就在資料庫了,扣減庫存壓力,建立訂單壓力。

庫存可以放到Reids快取中,來提高扣減庫存吞吐能力。對於熱點商品的庫存可以利用Redis分片儲存。

建立訂單可以走非同步訊息佇列。後端服務接到下單請求,直接放進訊息佇列,監聽服務取出訊息後,先將訂單資訊寫入Redis,每隔100ms或者積攢100條訂單,批次寫入資料庫一次。前端頁面下單後定時向後端拉取訂單資訊,獲取到訂單資訊後跳轉到支付頁面。用這種批次非同步寫入資料庫的方式大幅減少了資料庫寫入頻次,從而明顯降低了訂單資料庫寫入壓力。

隔離:

1,業務隔離。從業務上把秒殺和日常的售賣區分開來,把秒殺做為營銷活動,要參與秒殺的商品需要提前報名參加活動,這樣我們就能提前知道哪些商家哪些商品要參與秒殺,可以根據提報的商品提前生成靜態頁面並上傳到CDN預熱,提報的商品庫存也需要提前預熱,可以將商品庫存在活動開始前預熱到Redis,避免秒殺開始後大量的快取穿透。

2,部署隔離。秒殺相關服務和日常服務要分組部署,不能因為秒殺出問題影響日常售賣業務。可以申請單獨的秒殺域名,從網路入口層就開始分流。閘道器也單獨部署,秒殺走自己單獨的閘道器,從而避免日常閘道器受到影響。秒殺可以複用訂單,庫存,支付等日常服務,只是需要一些小的改造(比如下單流程走訊息佇列,批次寫入訂單庫,以及在Redis中扣減庫存)。

3,資料隔離。為了避免秒殺活動影響到日常售賣業務,Redis快取需要單獨部署,甚至資料庫也需要單獨部署!資料隔離後,秒殺剩餘的庫存怎麼辦?秒殺活動結束後,剩餘庫存可以歸還到日常庫存繼續做為普通商品售賣。資料隔離後,秒殺訂單和日常訂單不在相同的資料庫,之後的訂單查詢怎麼展示?可以在建立秒殺訂單後發訊息到訊息佇列,日常訂單服務採取拉的方式消費訊息,這時日常訂單服務是主動方,可以採用執行緒池的方式,根據機器的效能來增加或縮小執行緒池的大小,控制拉取訊息的速度,來控制訂單資料庫的寫入壓力。

網路:

秒殺前要和網路運營商、CDN服務商提前申請頻寬。

還有哪些細節要考慮:

1.如何避免超賣?如果在redis中扣減庫存,可以利用decr命令扣減庫存,decr是原子操作,在分散式環境下也不會有併發問題,decr扣減庫存後,判斷返回值,如果返回值小於0,扣減庫存失敗,秒殺也就失敗了;如果在資料庫中扣減庫存可以在where後面加上庫存大於0的條件,來避免庫存被減成負值。這樣就可以避免超賣情況發生了。

2.介面防刷,前面已經提到過,在閘道器層對下單等介面按userID限流。

3.閘道器層除了對userID做限流外,還要做整體限流。在實際訪問量超過預估訪問量時,整體限流可以起到保護作用,避免系統被壓垮。

4.防止重複下單,按userID限流已經起到了防止重複下單的作用。假如限制同一個使用者10分鐘能下一次單,一般情況下10分鐘內,商品早已經被搶光了,使用者也就沒有再次下單的機會了。

5.可以結合風控系統,在閘道器層把羊毛黨等有問題的使用者請求直接拒掉。

6.可以在閘道器層上面再加一層防火牆或者高防服務,來防禦DDos等分散式網路攻擊。

更多免費技術資料及影片

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69976011/viewspace-2696884/,如需轉載,請註明出處,否則將追究法律責任。

相關文章