偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

如果讓你設(shè)計一個秒殺系統(tǒng),你會怎么做?

開發(fā) 架構(gòu)
秒殺本來就是一個看運(yùn)氣的事,誰秒到算誰的,沒秒到就算失敗,產(chǎn)品數(shù)量往往有限,秒到的必然是少數(shù)人,所以在請求從客戶端到達(dá)服務(wù)端并處理的過程中,可以對流量進(jìn)行層層過濾。

這個算是一個經(jīng)典面試題了,雖說是一個場景題,但是也算是老八股了。

今天就從系統(tǒng)設(shè)計的角度來和小伙伴們聊一聊這個話題。

一般來說秒殺系統(tǒng)需要考慮到下面這樣一些問題:

  1. 瞬時高并發(fā)流量
  2. 熱點(diǎn)商品數(shù)據(jù)
  3. 庫存管理
  4. 重復(fù)下單
  5. 黃牛

接下來我們就這里提到的點(diǎn)逐一進(jìn)行分析。

一 瞬時高并發(fā)流量

應(yīng)對瞬時高并發(fā)流量,不是某一種方案就可以,是一個組合拳。另外大家要記得,系統(tǒng)設(shè)計沒有銀彈。

1.1 動靜分離部署

這算是一個基本要求了,引入 Nginx,將靜態(tài)資源和動態(tài)資源利用 Nginx 分流,靜態(tài)資源直接返回,動態(tài)資源則轉(zhuǎn)發(fā)給后端服務(wù)器去處理。

圖片圖片

這一點(diǎn)其實(shí)還蠻重要,松哥之前就有遇到這個問題,一開始沒有動靜分離部署,后來動靜分離部署之后,系統(tǒng)并發(fā)能力提升 2 倍以上。

不過如果愿意花點(diǎn)錢,把靜態(tài)資源都交給云服務(wù)商的 CDN 來處理,那就更好了。

一般來說使用 CDN 是比較劃算的,因?yàn)?CDN 流量費(fèi)往往比云主機(jī)的流量費(fèi)便宜。

1.2 數(shù)據(jù)庫獨(dú)立部署

這個也算是基操了,將應(yīng)用程序和數(shù)據(jù)庫部署到一起,往往無法讓數(shù)據(jù)庫發(fā)揮自己的極限性能。正常來說,一臺 1C2G 的服務(wù)器上只部署 MySQL,就能做到每秒處理 200 次查詢請求,這樣的數(shù)據(jù)基本上就能滿足一個每天 100W PV 的小網(wǎng)站了。

但是你想想,1C2G 的服務(wù)器部署 MySQL 和應(yīng)用程序的話,估計卡的沒法用了。

將 MySQL 和應(yīng)用程序部署到一臺服務(wù)器上,往往會因?yàn)閮烧呋ハ嘤绊懚档驼w的并發(fā)性能,具體來說可能會發(fā)生這些問題:

  1. 高并發(fā)導(dǎo)致 CPU 被耗盡,進(jìn)而 MySQL 響應(yīng)變慢。
  2. 應(yīng)用程序處理請求的時候需要等待更長的時間獲取數(shù)據(jù)庫的數(shù)據(jù),這個過程占用了大量的內(nèi)存。
  3. 系統(tǒng)內(nèi)存緊張導(dǎo)致 MySQL 中緩存的數(shù)據(jù)被回收,進(jìn)而拖慢 MySQL。
  4. 如此循環(huán)往復(fù),系統(tǒng)最終越來越慢甚至崩潰。

因此我們要做的第二件事情就是將數(shù)據(jù)庫和應(yīng)用程序獨(dú)立分開部署。

1.3 流量過濾

秒殺本來就是一個看運(yùn)氣的事,誰秒到算誰的,沒秒到就算失敗,產(chǎn)品數(shù)量往往有限,秒到的必然是少數(shù)人,所以在請求從客戶端到達(dá)服務(wù)端并處理的過程中,可以對流量進(jìn)行層層過濾。

一般來說,請求主要經(jīng)過如下節(jié)點(diǎn):

圖片

由于秒殺的隨機(jī)性,我們可以這么做:

  1. Client 處也就是用戶請求發(fā)起的地方,我們就可以隨機(jī)丟棄一些請求,直接彈出秒殺失敗、網(wǎng)絡(luò)阻塞等等。
  2. 當(dāng)請求到達(dá) Nginx 之后,可以在 Nginx 處進(jìn)行限流,利用像 limit_req_zone、limit_req_conn 等模塊來實(shí)現(xiàn)不同的限流策略。
  3. 當(dāng)請求從 Nginx 上轉(zhuǎn)發(fā)到 Java 服務(wù)上之后,我們可以繼續(xù)使用一些限流工具,比如 Sentinel,或者自己利用 Redis 寫限流工具也可以,在這里繼續(xù)進(jìn)行限流。
  4. 當(dāng)請求突破層層關(guān)卡到達(dá)業(yè)務(wù)層之后,對于實(shí)時性要求不高的數(shù)據(jù),直接從緩存查詢,緩存優(yōu)先查本地緩存,其次是遠(yuǎn)程分布式緩存如 Redis,緩存中沒有數(shù)據(jù)的話,最后再是 MySQL。

1.4 頁面靜態(tài)化

對于熱點(diǎn)數(shù)據(jù)頁面可以進(jìn)行靜態(tài)化處理。

比如秒殺商品頁、秒殺商品詳情頁等等這些熱點(diǎn)頁面直接自動進(jìn)行靜態(tài)化處理,這樣用戶每次訪問的時候,直接返回現(xiàn)成的頁面,就不用走數(shù)據(jù)庫了。

如果頁面數(shù)據(jù)發(fā)生變化,重新自動生成靜態(tài)頁面即可。

二 熱點(diǎn)商品數(shù)據(jù)

接下來就是熱點(diǎn)商品數(shù)據(jù)的處理了。

秒殺這種事情,在秒殺活動開始之前,我們基本上就能夠確定哪些數(shù)據(jù)是熱點(diǎn)數(shù)據(jù)了,所以處理處理起來相對來說并不難。

不過需要注意的是,能緩存的數(shù)據(jù)肯定是一些商品信息類的數(shù)據(jù),對于像庫存這類實(shí)時性要求極高的數(shù)據(jù),是不適合緩存的。

2.1 緩存預(yù)熱

緩存預(yù)熱主要從兩方面入手:

  1. 本地緩存預(yù)熱
  2. Redis 緩存預(yù)熱

查詢的時候先查本地緩存,沒有再查 Redis 緩存,這樣能夠有效避免 Redis 的熱 Key 問題。

2.2 數(shù)據(jù)拆分

另一方面就是我們要避免熱點(diǎn)數(shù)據(jù)聚集到一起,將熱點(diǎn)數(shù)據(jù)進(jìn)行拆分。避免從一個緩存處去獲取多個熱點(diǎn)數(shù)據(jù),這樣就能降低緩存的壓力。

比如:

  • 商品詳情數(shù)據(jù)
  • 價格數(shù)據(jù)
  • 秒殺規(guī)則數(shù)據(jù)
  • 。。。

可以對這些熱點(diǎn)數(shù)據(jù)進(jìn)行拆分,其實(shí)拆分之后,熱點(diǎn)數(shù)據(jù)也就不那么“熱”了。

三 庫存管理

庫存因?yàn)閷?shí)時性要求比較高,因此就不方便用緩存。

庫存管理要是做不好,可能會發(fā)生超賣或者少賣。

那么庫存管理怎么做呢?保險的方案當(dāng)然就是直接去數(shù)據(jù)庫扣減,但是數(shù)據(jù)庫并發(fā)能力有限,所以往往還需要結(jié)合緩存來做。

我們分別來看。

3.1 數(shù)據(jù)庫扣減

數(shù)據(jù)庫扣減,為了避免把庫存扣成負(fù)數(shù),一般來說我們有兩種思路:

  1. 悲觀鎖
  2. 樂觀鎖

在高并發(fā)場景下,悲觀鎖會導(dǎo)致更新效率降低很多;而樂觀鎖則會導(dǎo)致大量的失敗。似乎都不是一個很好的選擇。

其實(shí)我們只是要保證庫存不被減為負(fù)數(shù)而已,那么其實(shí)就可以在更新 SQL 中添加一個條件就行了,像下面這樣:

***** and 庫存>=0

大致上這樣就可以了。

不過只是這樣做還不夠,因?yàn)閿?shù)據(jù)庫的并發(fā)能力在哪擺著呢。所以我們還是要利用緩存。

3.2 緩存扣減

由于 Redis 本身就是單線程執(zhí)行的,因此我們再結(jié)合上 Lua 腳本,就可以保證扣減庫存這個操作的原子性。

在 Lua 腳本中我們可以獲取到庫存數(shù)據(jù),然后判斷庫存,沒問題再進(jìn)行扣減。

Redis 本身的高性能+單線程執(zhí)行+Lua 腳本的原子性,這三點(diǎn)結(jié)合起來就可以確保上述操作是沒有問題的。

3.3 最佳實(shí)踐

在具體實(shí)踐中,往往是 3.1 和 3.2 結(jié)合起來。

具體流程是這樣:

首先 Redis 做扣減,扣減完了之后,發(fā)送一條消息給 MQ,應(yīng)用程序再去消費(fèi)這條消息,消費(fèi)消息時完成數(shù)據(jù)庫的扣減。

這個過程中我們需要確保好 MQ 消息的可靠性和冪等性,處理好消息積壓。

當(dāng)然,穩(wěn)妥起見還需要有對賬機(jī)制,定時拉取 Redis 中的數(shù)據(jù)和數(shù)據(jù)庫中的數(shù)據(jù)進(jìn)行對比,保證數(shù)據(jù)的一致性。

四 重復(fù)下單

秒殺場景下用戶由于比較焦急,頻繁點(diǎn)擊可能造成重復(fù)下單,因此我們需要處理好下單操作的冪等性。

這個也有很多思路,需要多管齊下。

4.1 前端置灰

前端用戶點(diǎn)擊之后,就對秒殺按鈕進(jìn)行置灰操作,同時提醒用戶目前正在進(jìn)行秒殺。

這是基操,但是不能從根本上解決問題,還得配合后段冪等性處理。

4.2 后端冪等性處理

后段冪等性處理有很多方案,可以利用 Token 機(jī)制,這個松哥之前也有很多文章介紹,不多說。

同時因?yàn)槊霘⑦@種場景往往是限購的,因此在用戶下單的時候可以判斷是否有在途訂單或者用戶是否已經(jīng)下單,進(jìn)而決定當(dāng)前下單操作是否能夠成功。

五 黃牛

薅羊毛的黃牛也是我們要考慮的一個問題。

5.1 識別黃牛

首先我們要識別出來哪些用戶可能是黃牛,一般來說,我們可以通過如下方式來識別:

  1. 請求頻率:監(jiān)測用戶的請求頻率,若某一賬戶的請求過于頻繁,則可能是黃牛使用自動化工具發(fā)出的。
  2. 訪問模式:分析用戶的訪問模式,例如短時間內(nèi)大量的重復(fù)請求或者非正常人類行為的訪問模式。
  3. IP 地址:檢查請求來源的 IP 地址,對于同一 IP 地址下頻繁的請求進(jìn)行限制或標(biāo)記。

如果公司有足夠的人力資源,這塊可以建立預(yù)測模型,通過模型去分析哪些人可能是黃牛。

5.2 防止黃牛

當(dāng)我們識別出來黃牛之后,一般來說有如下一些辦法:

  • 圖形驗(yàn)證碼(CAPTCHA):在關(guān)鍵環(huán)節(jié)加入圖形驗(yàn)證碼,要求用戶識別并輸入相應(yīng)的字符,以防止自動化工具的使用。
  • 滑動驗(yàn)證:在關(guān)鍵環(huán)節(jié)采用滑動驗(yàn)證等交互式驗(yàn)證方式,這類驗(yàn)證方式難以被自動化工具模擬,這也是大家目前見到的最多的驗(yàn)證方式了。
  • 行為驗(yàn)證:基于用戶的行為軌跡(如鼠標(biāo)移動軌跡、鍵盤輸入模式等)來進(jìn)行驗(yàn)證,這個目前松哥只在京東圖書上見過這種驗(yàn)證方式。
  • 請求頻率限制:對識別出來的用戶或 IP 地址的請求頻率進(jìn)行限制,超出限制則暫時禁止訪問,這塊利用 Nginx 或者 Sentinel 就能實(shí)現(xiàn)。
  • 黑名單:對于已知的黃牛 IP 地址或賬戶進(jìn)行封禁處理,這塊可以直接在 Nginx 上處理,也可以在網(wǎng)關(guān)如 Spring Cloud Gateway 上處理。
  • 動態(tài)調(diào)整:根據(jù)系統(tǒng)的實(shí)時負(fù)載情況動態(tài)調(diào)整限流閾值。

六 小結(jié)

秒殺是一個大工程,以上是松哥和大家分享的一些實(shí)現(xiàn)思路,具體落實(shí)下來還有很多細(xì)節(jié)需要處理。

借助本文希望小伙伴們在面試的時候不怯場,能夠回答出來。

責(zé)任編輯:武曉燕 來源: 江南一點(diǎn)雨
相關(guān)推薦

2021-01-14 05:23:32

高并發(fā)消息中間件

2022-09-19 18:14:58

分布式架構(gòu)中間件

2022-02-17 08:57:18

內(nèi)存設(shè)計進(jìn)程

2023-08-28 08:52:49

監(jiān)聽頁面用戶

2024-06-21 08:15:25

2023-09-02 21:22:36

Airbnb系統(tǒng)

2023-01-15 17:57:12

緩存技術(shù)kafka磁盤

2021-05-13 07:32:17

培訓(xùn)代碼同事

2025-04-25 07:15:00

勒索軟件企業(yè)安全

2019-11-27 15:19:44

系統(tǒng)緩存架構(gòu)

2023-12-22 09:03:31

2020-08-03 08:30:00

JSCSS排序

2025-06-10 01:00:00

分布式日志系統(tǒng)

2023-12-14 17:27:28

架構(gòu)設(shè)計數(shù)據(jù)表

2023-12-29 11:32:27

2023-11-08 07:05:07

架構(gòu)設(shè)計群聊系統(tǒng)

2025-03-17 02:00:00

2025-04-29 02:00:00

高并發(fā)系統(tǒng)場景

2025-05-26 01:55:00

HashMap擴(kuò)容Redis

2024-03-19 00:52:52

前端網(wǎng)頁篡改
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號