很多的電商平台,在節假日如雙十一,618等都會有商品描述的活動,今天和大家討論一下,如何設計乙個秒殺系統。什麼是秒殺在一定的時間內幾秒或者幾分鐘,對一定數量的庫存進行出賣。
1,在秒殺之前,比如上午十點開始秒殺,很多使用者可能在九點五十左右就開始訪問自己心儀的秒殺商品,這樣就會出現在秒殺之前用很多的併發量,所以在秒殺之前的半個小時之前,是否可以將參加秒殺活動商品的一些資訊快取起來,這樣就可以減小因為高併發訪問,給資料庫帶來的壓力。
2,有的使用者為了搶到商品可能利用第三外掛程式,去頻繁的訪問介面,這樣給介面會帶來很大的壓力,為了避免這種刻意的刷單問題,可以在後台對同乙個使用者的訪問頻率做限制,可以**介面的實際訪問頻率,然後對不同的介面,同乙個使用者做頻率限制,如某個搶購入口,可以設定乙個使用者一分鐘不能訪問超過60次,10秒內不能超過20次等不同時段不同訪問頻率策略,這個可以通過redis等快取框架做到,列入以某個介面+使用者唯一性標準為key,可以選擇value為string型別,如果快取更多資料,value可以選擇hash型別,value的值是int型別,這樣使用者每次訪問介面,可以先判斷該值有沒有達到預設的訪問頻率限制的值,如果達到了,就告訴使用者,你的訪問太過頻繁,請多長時間後再試,或者要求使用者輸入驗證碼(驗證碼或者簡訊驗證碼)。如果沒有達到預設值,就給int值技術加一,在第一次訪問的時候,即查詢的前exist判斷,如果不存在,插入第一條資料,並且設定過氣時間,這個時間就是訪問頻率的時間限制例如一分鐘只能訪問60次,那麼這裡的過期時間就是60秒,預設值就是60次。這樣就可以從一定程度上避免刷單問題。
3,乙個使用者可能當初註冊了很多的賬號,平時不用,專門用來參加秒殺活動,這樣其實也會造成系統壓力。解決方式和上述差不多,只是對ip做限制,但是怎樣做可能傷到真實的使用者,因為有的場合如網咖,裡面人購物其實是同乙個ip出口,這個時候,其實也可以對使用者的等級做限制,只有多高的等級才可以參加秒殺活動。
前端:頁面靜態化,禁止重複提交。
後端:可拓展,快取,限流,削峰,非同步處理
資料庫層是最脆弱的一層,一般在應用設計時在上游就需要把請求攔截掉,資料庫層只承擔「能力範圍內」的訪問請求。所以,上面通過在服務層引入佇列和快取,讓最底層的資料庫高枕無憂。
案例:利用redis+mysql實現簡單的秒殺系統
redis是乙個分布式key-value快取系統,value支援多種資料結構,這裡value可以選擇兩種型別,string(或者hash):主要用於記錄商品的庫存,對商品減庫存。set集合(這裡不要用list集合,list集合是可重複的,set是不可重複的,可以保證乙個使用者只賣一次,如果乙個使用者可以買多次那麼可以使用list集合):用於儲存使用者的id獲取其他唯一確定乙個使用者的值。
在秒殺開始的前:可以使用批處理,將參加秒殺的產品資訊快取到redis中。這裡將產品的業務唯一字段作為key,庫存作為value。這裡的key要和前端快取的key一致。
在秒殺開始時::使用者大量提交。根據使用者提交的產品資訊,獲取到redis中需要的key值,查詢快取(為了保證快取有效,如果第一次沒有查詢到,可以到資料庫查詢,然後在快取一下,不過一般不會出現),得到庫存量,判斷當前庫存是否大於零,如果大於零,判斷當前的set集合中是否用該使用者id,如果沒有,減庫存並且將使用者的id放入集合中,並對庫存減一,如果庫存為0,提示使用者,商品已售完等文案資訊,如果集合中已經存在該使用者id,則不做任何處理,直接處理下乙個請求。直到最後庫存售完,上面的過程可以利用redis事務和watch功能完成對資料一致性的控制即超賣問題。
庫存售完後:程式開始啟動乙個有個後台執行緒,可以阻塞等待商品庫存售完的通知,在上面一步,庫存一旦售完,後台程序獲取set集合中的使用者資訊,非同步處理需要操作的購買等後續操作。
這只是乙個簡單的秒殺系統,實際的秒殺系統,還要考慮到很多的真實場景需要很多完善的地方,希望大家一起討論如何設計乙個秒殺系統。
首先我們先看下秒殺場景的難點到底在哪?在秒殺場景中最大的問題在於容易產生大併發請求、產生超賣現象和效能問題,下面我們分別分析下下面這三個問題:
1)瞬時大併發:一提到秒殺系統給人最深刻的印象是超大的瞬時併發,這時你可以聯想到小公尺手機的搶購場景,在小公尺手機搶購的場景一般都會有10w+的使用者同時訪問乙個商品頁面去搶購手機,這就是乙個典型的瞬時大併發,如果系統沒有經過限流或者熔斷處理,那麼系統瞬間就會崩掉,就好像被ddos攻擊一樣;
2)超賣:秒殺除了大併發這樣的難點,還有乙個所有電商都會遇到的痛,那就是超賣,電商搞大促最怕什麼?最怕的就是超賣,產生超賣了以後會影響到使用者體驗,會導致訂單系統、庫存系統、**鏈等等,產生的問題是一系列的連鎖反應,所以電商都不希望超賣發生,但是在大併發的場景最容易發生的就是超賣,不同執行緒讀取到的當前庫存資料可能下個毫秒就被其他執行緒修改了,如果沒有一定的鎖庫存機制那麼庫存資料必然出錯,都不用上萬併發,幾十併發就可以導致商品超賣;
3)效能:當遇到大併發和超賣問題後,必然會引出另乙個問題,那就是效能問題,如何保證在大併發請求下,系統能夠有好的效能,讓使用者能夠有更好的體驗,不然每個使用者都等幾十秒才能知道結果,那體驗必然是很糟糕的;
對於上面提到的秒殺的問題,我們當時的架構是下面這樣的:
從整個秒殺系統的架構其實和一般的網際網路系統架構本身沒有太多的不同,核心理念還是通過快取、非同步、限流來保證系統的高併發和高可用。下面從一筆秒殺交易的流程來描述下秒殺系統架構設計的要點:
1)對於大促時候的秒殺活動,一般運營會配置靜態的活動頁面,配置靜態活動頁面主要有兩個目的一方面是為了便於在各種社交**分發,另一方面是因為秒殺活動頁的流量是大促期間最大的,通過配置成靜態頁面可以將頁面發布在公有雲上動態的橫向擴充套件;
2)將秒殺活動的靜態頁面提前重新整理到cdn節點,通過cdn節點的頁面快取來緩解訪問壓力和公司網路頻寬,cdn上快取js、css和;
3)將活動h5頁面部署在公有雲的web server上,使用公有雲最大的好處就是能夠根據活動的火爆程度動態擴容而且成本較低,同時將訪問壓力隔離在公司系統外部;
5)服務降級處理,除了上面講到的限流和熔斷控制,我們還設定了降級開關,對於首頁、購物車、訂單查詢、大資料等功能都會進行一定程度的服務降級,例如我們會對首頁原先動態生成的大資料頁面布局降級為所有人看到的是一樣的頁面、購物車也會降級為不在一級頁面的tabbar上的購物車圖示上顯示商品數量、歷史訂單的查詢也會提供時間週期較短的查詢、大資料商品推薦也會提供一樣的商品推薦,通過這樣的降級處理能夠很好的保證各個系統在大促期間能夠正常的提供最基本的服務,保證使用者能夠正常下單完成付款。
6)上面介紹的都是如何保證能扛住高併發,下面介紹下整個方案中如何防止超賣現象的發生,我們日常的下單過程中防止超賣一般是通過在資料庫上實施樂觀鎖來完成,使用樂觀鎖雖然比for update這種悲觀鎖方式效能要好很多,但是還是無法滿足秒殺的上萬併發需求,我們的方案其實也很簡單實時庫存的扣減在快取中進行,非同步扣減資料庫中的庫存,保證快取中和資料庫中庫存的最終一致性。
在這個方案中我們使用的分布式快取是redis,使用了codis集群方案穩定性和高可用方面還是比較***的,因為redis是單執行緒寫,所以也不用擔心執行緒安全的問題,redis自身就能夠保證資料的強一致性,在下單的事務中包含了實時扣減快取中的庫存和非同步傳送佇列,由佇列處理器再非同步從佇列中取出訂單根據訂單資訊扣減庫存系統資料庫中的商品數量。
整個秒殺方案就介紹這麼多,其實整個的思路還是比較簡單的,也沒有特別複雜的地方,對於大部分公司的高併發場景還是適用的,並且比較容易實施上線,該方案對於我們當時每秒幾萬的併發場景是能夠扛住的,但是對於像小公尺、12306這些在高峰時動不動幾十萬使用者併發的場景,使用這樣的方案可能使用者體驗方面和系統服務方面就會存在一些問題了,對於每秒幾十萬併發的場景我們一般除了會在技術層面進行優化,更多的會通過其他一些業務手段來進行交易分流來分散整體的高併發訪問,秒殺方案就介紹到這裡,具體的實現這裡就不寫了,我相信跟著上面介紹的思路很容易就能將**寫出來。
其他不錯文章推薦:
如何設計乙個秒殺系統
秒殺場景一般會在電商 舉行一些活動或者節假日在12306 上搶票時遇到。對於電商 中一些稀缺或者 商品,電商 一般會在約定時間點對其進行限量銷售,因為這些商品的特殊性,會吸引大量使用者前來搶購,並且會在約定的時間點同時在秒殺頁面進行搶購。限流 鑑於只有少部分使用者能夠秒殺成功,所以要限制大部分流量,...
如何設計乙個秒殺系統
秒殺場景一般會在電商 舉行一些活動或者節假日在12306 上搶票時遇到。對於電商 中一些稀缺或者 商品,電商 一般會在約定時間點對其進行限量銷售,因為這些商品的特殊性,會吸引大量使用者前來搶購,並且會在約定的時間點同時在秒殺頁面進行搶購。限流 鑑於只有少部分使用者能夠秒殺成功,所以要限制大部分流量,...
如何設計乙個秒殺系統
秒殺一般是訪問請求數量遠遠大於庫存數量,只有少部分使用者能夠秒殺成功。秒殺業務流程比較簡單,一般就是下訂單減庫存。瀏覽器端 js 服務端控制器層 閘道器層 服務層利用快取應對讀請求 對類似於12306等購票業務,是典型的讀多寫少業務,大部分請求是查詢請求,所以可以利用快取分擔資料庫壓力。利用快取應對...