“秒殺”是如何實現的?

丿ZouJunWei丶


秒殺系統難做,是因為庫存有限,很多人會在集中的時間內讀寫有限的數據,在短時間內,系統會面臨成千上萬倍的流量進入。那麼如何能做好秒殺系統呢?我認為核心思想有這麼兩點:

  • 將請求儘量的在上游環節就攔截住(不要輕易到數據庫這一級)

  • 充分利用緩存

那麼這兩點如何實現呢,下面詳細說說:

  • 最上層是客戶端層,常見的都是瀏覽器訪問。點擊一次【秒殺按鈕】,然後再點一次【秒殺按鈕】,那麼是訪問了兩次後端系統麼?如果用戶手速快一些的話,或者用第三方插件不停的點擊,那麼豈不是多出來很多請求。從產品層面,我們會設置點擊一次按鈕後,將按鈕置灰,從技術角度,我們可以通過JS控制幾秒內只能提交一次請求。看,這就是“將請求儘量的在上游環節就攔截住”。

  • 當然客戶端層做限制,對於在座的程序猿們都是小意思,因為一抓包,請求長什麼樣子一清二楚,然後寫個腳本,循環調用就好了;為了防止這樣的情況發生,後端的服務需要做去重的工作。比如按照用戶名去重,在N秒內,只允許1個請求訪問進來,然後做頁面緩存;比如10秒內發送了一萬次請求,其中1次請求訪問成功並返回了頁面,將這個頁面進行緩存,剩餘9999次請求直接返回這個緩存頁面。

  • 再往下走,10秒內一個客戶只有一次請求進來,但是如果同時有一百萬個客戶,那麼這10秒內也有有一百萬次訪問,那麼如何應對呢?用【消息隊列】,所有的請求過來,都排隊吧,每次只讓有限的請求去訪問數據。

  • 當然訪問數據也不是直接去讀寫數據庫,這裡還有一層數據緩存,比如可以使用Memcached或者Redis緩存庫存剩餘,通常在秒殺系統中,這個“庫存”可以是粗粒度的,也就是說這個數字可以是不準確的,客戶關心的是買到還是買不到,而不會關心剩餘數量到底是20件還是10件;數據讀操作也可以放在緩存中,再由緩存和數據庫做數據同步。

  • 上面幾步已經攔截了大多數的請求,到DB這一層的時候,基本上沒有什麼壓力了。

我將持續分享Java開發、架構設計、程序員職業發展等方面的見解,希望能得到你的關注。


會點代碼的大叔


秒殺是電商企業最吸引流量,也是最考驗技術的場景!

秒殺的系統設計其實遵循“倒金字塔”原理,就是從前端頁面,網絡,到後端服務,數據庫,一一的將請求濾掉,最終讓落在數據庫的數據都是滿足要求的有效數據,假設是100萬人參加1000臺機器的秒殺,即是設計100萬-->1000的過濾系統;

要達到這樣的目的通常有下列的手段:

①,前端頁面防重複刷:點擊一次後,按鈕置灰,在指定時間內只允許搶一次!

②,控制網絡流量暴增:將前端頁面放置在CDN節點,防止網絡流量壓力快速增大;

網關進行限流:使用nginx 進行網關限流!

通過設置nginx參數limit_conn_zone ,limit_req_zone ,ngx_http_upstream_module進行限流!

③,後端應用服務:

1,如果沒有使用nginx限流,可使用spring-cloud-zuul進行網關限流!

2,異步處理 :防止同步調用帶來的阻塞,包括數據落庫等都可以使用異步調用!

3,緩存: 最重要的一步,可以事先將要秒殺的商品id進行緩存,使用分佈式鎖獲取到id和對應的userid進行綁定,才算秒殺成功,然後異步保存數據!

使用redis的watch對同一個賬號進行限制;


④,數據庫系統: 使用讀寫分離 ,分庫分表 等手段提升數據庫系統的吞吐量!

關於以上談到的技術,具體場景中可能會有更多的需求和問題出現,比如超賣超買等現象,需要具體分析,文章只提及整個系統的實現,筆者以後也會持續分享這種JAVA開發的技術和碰到的問題,敬請關注。。


哎喲JAVA不錯哦


這其實和每次微博有爆炸新聞熱搜就掛掉的場景是相似的,我和我的BAT同事們討論過這個問題,我們想出了一些場景和實現方式,不妨和大家分享下。

所謂秒殺,它的難點有兩個,分別是瞬時間的高併發與請求量遠大於庫存量,引申下來就是首先從服務器的角度,要抗住這個瞬時間的巨大請求量,server不能被打崩潰;然後再在保證數據一致性的情況下,用好分佈式鎖,進行秒殺請求分配。

巨大請求量

首先,針對瞬時間的巨大請求量,這個問題的解決方式只有擴容。還記得每次出點新聞,微博的搜索功能都會掛掉的事情嗎,那就是由於當時搜索的人實在太多了,server實在沒法響應這麼多的請求,就掛了。微博的解決方式也就是擴容,當時王寶強和MR的事情,有一次好像是MR號稱要爆料,嚇得微博趕緊擴容,結果爆出來的料好像根本沒有水花。每次擴容都是要錢的啊,要花錢買機器部署服務器的啊,才不是白白擴容的,所以那一次相當於微博被MR溜了一圈。等到上次陳羽凡吸毒,MR疑似在微博映射某人也吸毒的時候,微博的boss直接回復,“別鬧了,不會再為你擴容了”

庫存量有限

那遠大於庫存量這個問題怎麼解決呢。其實也比較簡單,假如說現在有10個商品等待秒殺,但是此時有10000個請求,此時在持久層之上,可能是接口層,就會攔截掉99.9%的請求,也就是說,1000個請求裡,999個都在接口層就被處理掉,給了返回了,此時用戶看到的結果就是,秒殺失敗,沒有搶到。最後,1000個裡面剩下的最後一個幸運兒,才真正的走到了邏輯層,再去數據持久層裡拿到秒殺到的商品信息。

以上是我們討論的部分結果,如果對你有所啟發或者幫助,歡迎點個贊或者留個言再走。

我是蘇蘇思量,來自BAT的Java開發工程師,每日分享科技類見聞,歡迎關注我,與我共同進步。


蘇蘇思量


秒殺與其他業務最大的區別在於:秒殺的瞬間,(1)系統的併發量會非常的大(2)併發量大的同時,網絡的流量也會瞬間變大。

關於(2),最常用的辦法就是做頁面靜態化,也就是常說的前後端分離,把靜態頁面直接緩存到用戶的瀏覽器端,所需要的數據從服務端接口動態獲取。這樣會大大節省網絡的流量,再加上CDN,一般不會有大問題。

關於(1),這裡的核心問題就在於如何在大併發的情況下能保證DB能扛得住壓力,因為大併發的瓶頸在於DB。如果說請求直接從前端透傳到DB,顯然,DB是無法承受幾十萬上百萬甚至上千萬的併發量的。所以,我們能做的只能是減少對DB的訪問,前端發出了100萬個請求,通過我們的處理,最終只有10個會訪問DB,這樣就可以了!針對秒殺這種場景,因為秒殺商品的數量是有限的,這種做法剛好適用!

那麼具體是如何來減少DB的訪問呢?

假如:某個商品可秒殺的數量是10,那麼在秒殺活動開始之前,把商品的ID和數量加載到緩存,比如:Redis。服務端收到請求的時候,首先減一下Redis裡面的數量,如果數量減到0隨後的訪問直接返回秒殺失敗。也就是說,只有10個請求最終會去實際的請求DB。

當然,如果我們的商品數比較多,1萬件商品參與秒殺,1萬*10=10萬個併發去請求DB,DB的壓力還是會很大,這裡就用到另一個非常重要的組件:消息隊列。我們不是把請求直接去訪問DB,而是先把請求寫到消息隊列,做一個緩存,然後再去慢慢的更新數據庫。這樣做以後,前端用戶的請求可能不會立即得到響應是成功還是失敗,很可能得到的是一個排隊中的返回值,這個時候,需要客戶端再去服務端輪詢,因為我們不能保證一定就秒殺成功了。當服務端出隊,生成訂單以後,把用戶ID和商品ID寫到緩存中,來應對客戶端的輪詢就可以了。

這樣處理以後,我們的應用是可以很簡單的進行分佈式橫向擴展的,以應對更大的併發。

當然,秒殺系統還有很多要處理的事情:比如防刷限流、比如分佈式Session等等。


分享到:


相關文章: