網站首頁 編程語言 正文
1.如何設計一個秒殺系統
在設計任何系統之前,我們首先都需要先理解秒殺系統的業務背景
下面我簡單的舉一個例子:
在某個時間點,某某電商網站要低價賣某件商品,而且限量1千件,搶購人數超過數十萬人。
所以我們面臨的第一個秒殺的問題就是:時間極短,然后瞬間流量非常大
我們的系統必須保證秒殺搶購的結果不出錯,達到搶購的預期目的。
而且秒殺庫存的實現也需要保障秒殺結果的準確性。
總結幾個特點就是:
- 高性能:秒殺中有大量的并發讀寫,所以需要使系統能支撐起高并發訪問,這是一個關鍵點。
- 高可用:藐視瞬間流量非常大,很有可能會導致系統宕機,所以需要從各方面保證系統的可用性。
- 一致性:由于秒殺請求量非常大,此時就需要我們的秒殺結果要準確。因為一旦出錯,那么波及面會非常廣,損失非常大。
2.秒殺流程
我們先從秒殺的入口開始說起
在秒殺入口的地方會有這些問題需要解決:
2.1 前端處理
靜態資源處理
秒殺商品一般都會包含很多靜態資源,所以這些圖片什么的靜態資源一定要放到CDN(Content Delivery Network,即內容分發網絡),能放的盡量放進去。讓秒殺時后端服務器的壓力盡可能小。
說到此處,我重點描述講解CDN
為了能在傳統IP網上發布豐富的寬帶媒體內容,提出在現有互聯網基礎上建立一個內容分發平臺專門為網站提供服務。由于CDN是為加快網絡訪問速度而被優化的網絡覆蓋層,因此被形象地稱為“網絡加速器”。
首先要說的是應用服務器和資源服務器應該解耦,也就是應用服務器只處理邏輯,而資源服務器存放內容或者叫資源。
- CDN專注于「內容」,也就是CDN的C所代表的Content,專注于靜態資源的分發和訪問,比如一張圖片,一個文本文件,一個視頻,一個CSS,一個JS等等,任何以文件形式存儲的,為了提高在互聯網上的訪問速度和質量,都可以將這個資源部署在CDN這個網絡上。
- CDN動作是「分發」,也就是如何讓剛才提到的那些「內容」快速的部署在這個網絡中,從而快速為用戶服務,其實還有一層更重要的含義是用戶的快速訪問與就近接入,分發的目的是為了用戶更好的體驗。
- CDN落定于「網絡」,是部署于全國或者全世界的一大堆服務器,這些服務器基于當前互聯網的基礎架構在其上層再構成一個網絡,這個網絡專為資源分發而生。
那CDN的原理是什么呢?為什么用戶可以接入離他最近的服務器呢?
主要是利用了DNS來判斷用戶位置,再返回給用戶最近的機房的服務器的資源地址。
下面我再通俗的解釋一下:
肯德基的總部在美國,可是你家樓下也有一家肯德基,并且漢堡包是一模一樣的,這就是CDN(這個比喻來自知乎,覺得非常的恰當)。肯德基部署了很多個CDN在世界各地提供服務,用戶都是找到最近的店,這個計算過程就是剛才講的「就近接入」。
什么是CDN的調度呢?
(1)DNS調度是最常用和最通用的調度方案,缺點是存在DNS劫持的風險,調度的精確度也會差一些;
(2)302調度非常適合用在大文件下載和視頻點播這兩個應用場景,優點是可以提高調度的精確度,缺點是將會增加首包的時延(在大文件下載和視頻點播場景下對首包時延不太敏感,而對調度精確度要求更高)
(3)HTTPDNS調度的優點是有較高的安全性(可以規避DNS劫持風險)和調度精確度,但是有個很大的缺點,需要客戶端提供支持(例如在手機APP上嵌入SDK),通用性較差。
通俗來講:
在一個商圈有兩家肯德基,有一家組織活動,雞腿隨便吃,所以顧客全部涌到這家店,已經水泄不通,另一家店則門可羅雀。這個時候CDN的調度功能就要發揮作用了,另一家店也發布了一個消息說,買一個漢堡,打五折。這個時候,在第一家店搶不上雞腿的顧客,馬上跑去了第二家店,這個時候兩個店的流量處于均衡狀態。這就是CDN的調度。
惡意訪問行為的處理
當我們推出秒殺活動后,還需要考慮黃牛黨們開發出的各種秒殺器,可以自動填單,自動回答各種問題,以及自動模擬點擊等,令我們防不勝防。
針對這些我們可以通過各種工作來限制和識別這些惡意訪問。
- 例如限制IP的提交次數
- 提高各種動態驗證碼及問題的難度
- 增加黑名單賬戶
秒殺鏈接隱藏
如果稍微懂點程序的人可以提前拿到秒殺鏈接,那么就可以通過程序在最快的時間發起秒殺請求,這樣人家就可以拿到大部分商品了。為了防止這一點,可以使秒殺鏈接動態化。使用MD5算法等加密隨機字符串作為URL的一部分,秒殺開始后才將連接放出來,同時在后臺進行校驗,此時已經可以防止一大批的羊毛黨了。
前端限流
可以在秒殺按鈕點擊之后灰掉幾秒鐘,幾秒鐘之內只能點擊一次。
可以使用Nginx用戶請求到Nginx的時候將流量分散到多個服務器上,而且也可以針對用戶進行一些過濾,將一些請求攔截,保證后端的穩定性。比如1萬個商品,最多放進來10萬個請求就可以了,其他的用戶就只能等著靜態頁面嘍。同時也可以在秒殺預約的時候隨機發放一些token,只有擁有這些token的客戶才有可能搶購成功。
同時在分布式的架構下,我們也可以通過gateway,redis+lua或者nginx進行限流
2.2 后端處理
后端限流
如果服務的流量到達最大值的時候,新的請求就不能再進來了。
而服務宕機的時候也需要引導請求到備用服務器上面,然后返回一些靜態提示頁面等。
隔離就要求,秒殺的服務單獨部署,只承擔其秒殺的單一職責,即使出問題,也不會影響其他的服務。
削峰
秒殺流量在某一個時間點非常高,那么我們讓瞬間進來的流量進到一個緩沖池,然后再進行平緩處理。比較多用到的方案就是使用消息隊列來處理。
庫存預熱
由于秒殺的商品的數量一般都是提前已知的,這時我們可以提前將商品的一些數據提前加載到緩存中。并且可以將商品分區來進行秒殺,根據每個大區的用戶數量以及活躍程度,為每個大區分配單獨的秒殺商品數量。這樣也可以分散服務器壓力。
使用緩存
高并發的情況下必然會遇到緩存雪崩,緩存擊穿,緩存穿透等問題。而且秒殺的場景是讀多寫少,使用Redis作為緩存非常合適,為了避免單臺Redis服務器出問題,導致緩存擊穿等問題,升級使用Redis集群是一個比較好的方案。提升可用性的性能也可以大大提高。
下面我簡單說一下緩存雪崩,緩存擊穿以及緩存穿透
首先我們要先了解一下緩存的處理流程
前臺請求,后臺先從緩存中取數據,取到直接返回結果,取不到時從數據庫中取,數據庫取到更新緩存,并返回結果,數據庫也沒取到,那直接返回空結果。
知道什么是緩存之后我們重點來了解一下這三個名次具體指什么,以及如何去解決
緩存穿透
描述:緩存穿透是指緩存和數據庫中都沒有的數據,而用戶不斷發起請求,如發起為id為“-1”的數據或id為特別大不存在的數據。這時的用戶很可能是攻擊者,攻擊會導致數據庫壓力過大。
解決方案:
- 接口層增加校驗,如用戶鑒權校驗,id做基礎校驗,id<=0的直接攔截;
- 從緩存取不到的數據,在數據庫中也沒有取到,這時也可以將key-value對寫為key-null,緩存有效時間可以設置短點,如30秒(設置太長會導致正常情況也沒法使用)。這樣可以防止攻擊用戶反復用同一個id暴力攻擊
緩存擊穿
描述: 緩存擊穿是指緩存中沒有數據但數據庫中有的數據(一般是緩存時間到期),這時由于并發用戶特別多,同時讀緩存沒讀到數據,又同時去數據庫去取數據,引起數據庫壓力瞬間增大,造成過大壓力。
解決方案:
- 設置熱點數據永遠不過期。
- 加互斥鎖,互斥鎖參考代碼如下
我簡單解釋一下代碼思路:
我們首先從緩存中獲取數據,如果數據不存在,我們則去獲取鎖,這把鎖 只需要能夠互斥,可重入即可,最簡單的就是redis的setnx來實現,獲取鎖資源以后,從數據庫讀取數據,同時將數據更新至緩存,然后釋放鎖;如果獲取鎖資源失敗,我們就讓其隔一段時間之后重新嘗試去獲取鎖資源。
緩存雪崩
緩存雪崩是指緩存中數據大批量到過期時間,而查詢數據量巨大,引起數據庫壓力過大甚至down機。
和緩存擊穿不同的是,緩存擊穿指并發查同一條數據,緩存雪崩是不同數據都過期了,很多數據都查不到從而查數據庫。
解決方案:
- 緩存數據的過期時間設置隨機,防止同一時間大量數據過期現象發生。
- 如果緩存數據庫是分布式部署,將熱點數據均勻分布在不同搞得緩存數據庫中。
- 設置熱點數據永遠不過期。
3.超賣問題
秒殺中一個重要的點就是超賣問題,由于搶購人數多,流量也很大,但是也不能賣多了。
目前常見的解決方案就是:
1.數據庫要加唯一索引,減庫存的時候要先進行庫存數量判斷等,數據庫鎖,加版本號的樂觀鎖方式等等。2
.采用Redis來維護庫存,由于秒殺活動可以預先知道商品的數量,所以可以提前將商品的數據加載到Redis中,如果Redis的庫存不足的話則秒殺失敗。
3.生成訂單的時候將請求放到服務端的異步隊列中去處理,可以使用Redis的隊列,或者MQ均可。
4.總體思路
其實秒殺方案的總體思路也很簡單:
1.盡可能的將請求攔截在上游;
2.后端均要處理限流;
3.盡量減少請求到數據庫;
4.多利用緩存;
5.使用異步操作-可以使用隊列等;
6.秒殺服務單一職責;
7.盡早失敗,讓秒殺請求返回
原文鏈接:https://blog.csdn.net/zhiyikeji/article/details/123352473
相關推薦
- 2022-10-03 React?Native?中實現倒計時功能_React
- 2022-11-28 詳解如何使用C++寫一個線程安全的單例模式_C 語言
- 2022-05-10 通過addroutes動態添加路由
- 2024-03-06 Modbus通信及數據存儲讀取
- 2022-07-01 python性能檢測工具函數運行內存及運行時間_python
- 2022-12-07 C++成員函數后面加override問題_C 語言
- 2022-06-13 go語言編程之select信道處理示例詳解_Golang
- 2022-06-08 淺析C++可變參數模板的展開方式_C 語言
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支