個人從事電商行業十幾年,經歷過大大小小的**活動和秒殺上百次,每次做秒殺瞬時訪問量會翻數十倍,甚至數百倍。對系統架構是巨大的考驗,期間也曾經歷過系統宕機,甚至整體雪崩。那麼我們怎麼設計秒殺系統,才能保證秒殺系統的高效能和穩定性,同時還要保證日常業務不受影響呢?
先看看秒殺場景特點。秒殺開始前幾分鐘,大量使用者開始進入秒殺商品詳情頁面,很多人開始頻繁重新整理秒殺商品詳情頁,這時秒殺商品詳情頁訪問量會猛增。秒殺開始,大量使用者開始搶購,這時建立訂單,扣庫存壓力會顯著增大。實際上,秒殺場景基本都是秒殺參與人多,秒殺成功的人卻寥寥無幾,經常是幾十萬人或者更多人搶幾百個商品庫存。
那麼我們曾經是怎麼設計秒殺系統的呢?主要涉及以下幾個方面:
秒殺業務流程上的考慮:
由於參加秒殺的商品售賣**非常低,基本都是「搶到即賺到」,成功下單後卻不付款的情況非常少。所以我們採用下單減庫存的方案,下單時扣減庫存,然後再進行支付。假如真有個別訂單不付款怎麼辦?沒關係,秒殺好活動最主要的目的是吸引流量,個別訂單不支付對秒殺活動本身影響不大。況且,沒支付剩下的庫存還可以做為普通商品繼續售賣。不過要注意對機械人和自動指令碼的防禦,後面會詳細介紹。
頁面靜態化:
請求攔截:
前端頁面,相關按鈕點選後置灰,防止重複提交
閘道器(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服務商提前申請頻寬。
還有哪些細節要考慮:
如何避免超賣?如果在redis中扣減庫存,可以利用decr命令扣減庫存,decr是原子操作,在分布式環境下也不會有併發問題,decr扣減庫存後,判斷返回值,如果返回值小於0,扣減庫存失敗,秒殺也就失敗了;如果在資料庫中扣減庫存可以在where後面加上庫存大於0的條件,來避免庫存被減成負值。這樣就可以避免超賣情況發生了。
介面防刷,前面已經提到過,在閘道器層對下單等介面按userid限流。
閘道器層除了對userid做限流外,還要做整體限流。在實際訪問量超過預估訪問量時,整體限流可以起到保護作用,避免系統被壓垮。
防止重複下單,按userid限流已經起到了防止重複下單的作用。假如限制同乙個使用者10分鐘能下一次單,一般情況下10分鐘內,商品早已經被搶光了,使用者也就沒有再次下單的機會了。
可以結合風控系統,在閘道器層把羊毛黨等有問題的使用者請求直接拒掉。
秒殺系統架構設計
最近聊天總有人問秒殺的架構設計,秒殺這種業務場景一直是個熱門話題,網上也看了很多,感覺大牛分享的12306的搶票架構挺不錯的。文章講的從網路接入開始,不是那種空洞的架構。1,client請求接入到內網後,通過ospf協議進行第一步負載均衡,2,請求被 到多台lvs負載均衡器,lvs是網路層負載,效率...
秒殺系統的架構設計
秒殺系統,是典型的短時大量突發訪問類問題。對這類問題,有三種優化效能的思路 寫入記憶體而不是寫入硬碟 非同步處理而不是同步處理 分布式處理 用上這三招,不論秒殺時負載多大,都能輕鬆應對。更好的是,redis能夠滿足上述三點。因此,用redis就能輕鬆實現秒殺系統。用我這個方案,無論是電商平台 秒殺,...
怎麼設計乙個秒殺系統
方向 將請求盡量攔截在系統上游 思路 限流和削峰 1 限流 遮蔽掉無用的流量,允許少部分流量流向後端。2 削峰 瞬時大流量峰值容易壓垮系統。常用的消峰方法有非同步處理 快取和訊息中介軟體等技術 1 靜態資源快取 2 限流方法 1 控制層方面入手 使用多個伺服器併發處理請求,減小伺服器壓力。在秒殺開始...