首頁 > Java > java教程 > 6000多字 | 秒殺系統設計注意點

6000多字 | 秒殺系統設計注意點

發布: 2023-08-23 14:28:01
轉載
1021 人瀏覽過

五個架構原則

資料要盡量少

首先是指使用者請求的資料能少就少。請求的資料包括上傳給系統的資料和系統傳回給使用者的資料(通常就是網頁)。

請求數要盡量少

用戶請求的頁面返回後,瀏覽器渲染這個頁面還要包含其他的額外請求,比如說,這個頁面依賴的CSS/JavaScript、圖片,以及Ajax 請求等等都定義為“額外請求”,這些額外請求應該盡量少。

路徑要盡量短

就是使用者發出請求到返回資料這個過程中,需求經過的中間的節點數。

依賴要盡量少

指的是要完成一次使用者請求必須依賴的系統或服務,這裡的依賴指的是強依賴。

高可用

系統中的單點可以說是系統架構上的一個大忌,因為單點意味著沒有備份,風險不可控,我們設計分散式系統最重要的原則是“消除單點”,另外一種叫法“高可用”。

架構是一種平衡的藝術,而最好的架構一旦脫離了它所適應的場景,一切都將是空談。我們要記住的是,這裡所說的幾點都只是一個個方向而已,我們應該盡量往這些方向上去努力,但也要考慮平衡其他因素。

如何做動靜分離

何為動靜資料

那到底什麼才是動靜分離呢?所謂“動靜分離”,其實就是把使用者要求的資料(如 HTML 頁面)分成“動態資料”和“靜態資料”。簡單來說,「動態資料」和「靜態資料」的主要差異就是看頁面中輸出的資料是否和 URL、瀏覽者、時間、地理相關,以及是否含有 Cookie 等私密資料。

  1. 很多媒體類的網站,某一篇文章的內容不管是你訪問還是我訪問,它都是一樣的。所以它就是一個典型的靜態數據,但它是個動態頁面。
  2. 我們如果現在訪問淘寶的首頁,每個人看到的頁面可能都是不一樣的,淘寶首頁中包含了很多根據訪問者特徵推薦的信息,而這些個性化的數據就可以理解為動態數據了。
怎麼對靜態資料做快取呢?

第一,你應該把靜態資料快取到離使用者最近的地方。靜態數據就是那些相對不會變化的數據,因此我們可以把它們緩存起來。快取到哪裡呢?常見的就三種,使用者瀏覽器裡、CDN 上或在服務端的 Cache 中。你應該根據情況,把它們盡量緩存到離用戶最近的地方。

第二,靜態化改造就是要直接快取 HTTP 連線。相較於普通的資料快取而言,你肯定還聽過系統的靜態化改造。靜態化改造是直接快取HTTP 連接而不是僅僅緩存數據,如下圖所示,Web 代理伺服器根據請求URL,直接取出對應的HTTP 響應頭和響應體然後直接返回,這個響應過程簡單得連HTTP 協議都不用重新組裝,甚至連HTTP 請求頭也不需要解析。

第三,讓誰來快取靜態資料也很重要。不同語言寫的 Cache 軟體處理快取資料的效率也各不相同。以Java 為例,因為Java 系統本身也有其弱點(例如不擅長處理大量連線請求,每個連線消耗的記憶體較多,Servlet 容器解析HTTP 協定較慢),所以你可以不在Java 層做緩存,而是直接在Web 伺服器層上做,這樣你就可以屏蔽Java 語言層面的一些弱點;而相比起來,Web 伺服器(如Nginx、Apache、Varnish)也更擅長處理大並發的靜態檔案請求。

如何做動靜分離的改造
  1. URL 唯一化
  2. 分離瀏覽者相關的因素
  3. 分離時間因素
  4. #非同步化地域因素
  5. ##去掉Cookie
##去掉Cookie

動靜分離的幾種架構方案

6000多字 | 秒殺系統設計注意點根據架構上的複雜度,有3 種方案可選:

實體機單機部署:

6000多字 | 秒殺系統設計注意點

統一Cache 層:

6000多字 | 秒殺系統設計注意點

#加上CDN層:

######### ###CDN 化部署方案還有以下幾個特點:###
  1. 把整個頁面快取在使用者瀏覽器中;
  2. 如果強制重新整理整個頁面,也會要求CDN;
  3. #實際有效請求,只是使用者對「刷新搶寶」按鈕的點擊。
秒殺系統熱點資料如何處理?
什麼是「熱點」

熱點分為熱點操作和熱點資料。

所謂“熱點操作”,例如大量的刷新頁面、大量的添加購物車、雙十一零點大量的下單等都屬於此類操作。對系統來說,這些操作可以抽象化為“讀取請求”和“寫入請求”,這兩種熱點請求的處理方式大相徑庭,讀取請求的最佳化空間要大一些,而寫入請求的瓶頸一般都在存儲層,優化的想法就是根據CAP 理論做平衡,這個內容我在「減庫存」一文再詳細介紹。

而「熱點數據」比較好理解,那就是用戶的熱點請求對應的數據。而熱點資料又分為「靜態熱點資料」和「動態熱點資料」。

所謂“靜態熱點資料”,就是能夠事先預測的熱點資料。例如,我們可以透過賣家報名的方式事先篩選出來,並透過報名系統對這些熱點商品進行打標。另外,我們還可以透過大數據分析來提前發現熱點商品,例如我們分析歷史成交記錄、用戶的購物車記錄,來發現哪些商品可能更熱門、更好賣,這些都是可以事先分析出來的熱點。

所謂“動態熱點資料”,就是無法事先預測的,系統在運作過程中暫時產生的熱點。例如,賣家在抖音上做了廣告,然後商品一下就火了,導致它在短時間內被大量購買。

由於熱點操作是使用者的行為,我們不好改變,但能做一些限制和保護,所以本文我主要針對熱點資料來介紹如何進行最佳化。

發現熱點資料
  1. 發現靜態熱點資料
  2. 發現動態熱點資料
處理熱點數據

優化

優化熱點數據最有效的方法就是快取熱點數據,如果熱點資料做了動靜分離,那麼可以長期快取靜態資料。但是,快取熱點數據更多的是「臨時」緩存,即不管是靜態數據還是動態數據,都用一個隊列短暫地緩存數秒鐘,由於隊列長度有限,可以採用 LRU 淘汰演算法替換。

限制

#

限制更多的是一種保護機制,限制的辦法也有很多,例如對被訪問商品的ID 做一致性Hash,然後根據Hash 做分桶,每個分桶設置一個處理隊列,這樣可以把熱點商品限制在一個請求佇列裡,防止因某些熱點商品佔用太多的伺服器資源,而使其他請求始終無法獲得伺服器的處理資源。

隔離

秒殺系統設計的第一個原則就是將這種熱點資料隔離出來,不要讓1% 的請求影響到另外的99%,隔離出來後也更方便對這1% 的請求做針對性的最佳化。其中隔離又可分為:業務隔離、系統隔離、資料隔離。

流量削峰怎麼做

就像城市裡的道路,因為存在早高峰和晚高峰的問題,所以有了錯峰限行的解決方案。

削峰的存在,一是可以讓服務端處理變得更平穩,二是可以節省伺服器的資源成本。

針對秒殺這一場景,削峰從本質上來說就是更多地延緩用戶請求的發出,以便減少和過濾掉一些無效請求,它遵從「請求數要盡量少」的原則。

流量削峰思路

##要對流量進行削峰,最容易想到的解決方案就是用訊息佇列來緩衝瞬時流量,把同步的直接呼叫轉換成非同步的間接推送,中間透過一個佇列在一端承接瞬時的流量洪峰,在另一端平滑地將訊息推送出去。

除了訊息佇列,類似的排隊方式還有很多,例如:

  • 利用執行緒池加鎖等待也是一種常用的排隊方式;
  • 先進先出、先進後出等常用的記憶體排隊演算法的實作方式;
  • #把請求序列化到檔案中,然後再順序地讀文件(例如基於MySQL binlog 的同步機制)來恢復請求等方式。

可以看到,這些方式都有一個共同特徵,就是把“一步的操作”變成“兩步的操作”,其中增加的一步操作用來起到緩衝的作用。

效能最佳化

  • 減少編碼
  • #減少序列化
  • Java 極致優化
  • 並發讀優化

#「減庫存」核心邏輯

這是非常重要的,其他步驟都是做一些輔助性的。庫存 100 件就賣 100 件,在資料庫裡減到 0 就好了啊,這有什麼麻煩的?是的,理論上是這樣,但是具體到業務場景中,「減庫存」就不是這麼簡單了。

減庫存有哪幾種方式

在商品頁麵點了“立即購買”按鈕,核對資訊之後點擊“提交訂單”,這一步稱為下單操作。下單之後,你只有真正完成付款操作才能算真正購買,也就是俗話說的「落袋為安」。

減庫存作業一般有以下幾個方式:

下單減庫存

即當買家下單後,在商品的總庫存中減去買家購買數量。下單減庫存是最簡單的減庫存方式,也是控制最精確的一種,下單時直接透過資料庫的事務機制控製商品庫存,這樣一定不會出現超賣的情況。但是你要知道,有些人下完單可能並不會付款。

付款減庫存

即買家下單後,不立即減庫存,而是等到有用戶付款後才真正減庫存,否則庫存一直保留給其他買家。但因為付款時才減庫存,如果併發比較高,有可能出現買家下單後付不了款的情況,因為可能商品已經被其他人買走了。

預扣庫存

這種方式相對複雜一些,買家下訂單後,庫存為其保留一定的時間(如10 分鐘),超過這個時間,庫存將會自動釋放,釋放後其他買家就可以繼續購買。在買家付款前,系統會校驗該訂單的庫存是否還有保留:如果沒有保留,則再次嘗試預扣;如果庫存不足(也就是預扣失敗)則不允許繼續付款;如果預扣成功,則完成付款並實際減去庫存。

高可用建設應該從哪裡著手

說到系統的高可用建設,它其實是一個系統工程,需要考慮到系統建設的各個階段,也是說它其實貫穿了系統建設的整個生命週期,如下圖:

6000多字 | 秒殺系統設計注意點

#
架構階段

架構階段主要考慮系統的可擴展性和容錯性,要避免系統出現單點問題。例如多機房單元化部署,即使某個城市的某個機房出現整體故障,仍然不會影響整體網站的運作。

編碼階段

編碼最重要的是保證程式碼的健全性,例如涉及遠端呼叫問題時,要設定合理的逾時退出機制,防止被其他系統拖垮,也要對呼叫的回傳結果集有預期,防止回傳的結果超出程式處理範圍,最常見的做法就是對錯誤異常進行捕獲,對無法預料的錯誤要有預設處理結果。

測試階段

測試主要是保證測試案例的覆蓋度,保證最壞情況發生時,我們也有對應的處理流程。

發布階段

發佈時也有一些地方要注意,因為發佈時最容易出現錯誤,因此要有緊急的回溯機制。

運行階段

運行時是系統的常態,系統大部分時間都會處於運行狀態,運行態最重要的是對系統的監控要準確及時,發現問題能夠準確警報且警報資料要準確詳細,以便於排除問題。

故障發生

故障發生時首先最重要的就是及時止損,例如由於程序問題導致商品價格錯誤,那就要及時下架商品或者關閉購買鏈接,防止造成重大資產損失。然後就是要能夠及時恢復服務,並定位原因解決問題。

在遇到大流量時,我們該怎麼最大化的保障我們的系統正常運作呢?

降級

所謂“降級”,就是當系統的容量達到一定程度時,限製或關閉系統的某些非核心功能,從而把有限的資源保留給更核心的業務。它是一個有目的、有計劃的執行過程,所以對降級我們一般需要有一套預案來配合執行。如果我們把它系統化,就可以透過計畫系統和開關係統來實現降級。

6000多字 | 秒殺系統設計注意點

限流

限流是當系統容量達到瓶頸時,我們需要透過限制一部分流量來保護系統,並做到既可以人工執行開關,也支援自動化保護的措施。

客戶端限流和服務端限流的優缺點:

客戶端限流,好處可以限制請求的發出,透過減少發出無用請求從而減少對系統的消耗。缺點就是當客戶端比較分散時,沒法設定合理的限流閾值:如果閾值設的太小,會導致服務端沒有達到瓶頸時客戶端已經被限制;而如果設的太大,則起不到限制的作用。

服務端限流,好處是可以根據服務端的效能設定合理的閾值,而缺點就是被限制的請求都是無效的請求,處理這些無效的請求本身也會消耗伺服器資源。

6000多字 | 秒殺系統設計注意點

常見限流演算法

#計數器(固定視窗)演算法

計數器演算法是使用計數器在週期內累積造訪次數,當達到設定的限流值時,觸發限流策略。下一個週期開始時,進行清零,重新計數。

此演算法在單機還是分散式環境下實作都非常簡單,使用redis的incr原子自增性和執行緒安全性即可輕鬆實現。

滑動視窗演算法 

滑動視窗演算法是將時間週期分為N個小週期,分別記錄每個小週期內造訪次數,並且根據時間滑動刪除過期的小周期。此演算法可以很好的解決固定視窗演算法的臨界問題。

漏桶演算法 

漏桶演算法是存取請求到達時直接放入漏桶,如當前容量已達到上限(限流值),則進行丟棄(觸發限流策略)。漏桶以固定的速率進行釋放存取請求(即請求通過),直到漏桶為空。

令牌桶演算法

令牌桶演算法是程式以r(r=時間週期/限流值)的速度向令牌桶中增加令牌,​​直到令牌桶滿,請求到達時向令牌桶請求令牌,如獲取到令牌則透過請求,否則觸發限流策略

拒絕服務
##如果限流還不能解決問題,最後一招就是直接拒絕服務了。當系統負載達到一定閾值時,例如 CPU 使用率達到 90% 或係統 load 值達到 2*CPU 核數時,系統直接拒絕所有請求,這種方式是最暴力但也最有效的系統保護方式。例如秒殺系統,我們在以下幾個環節設計過載保護:

在最前端的Nginx 上設定過載保護,當機器負載達到某個值時直接拒絕HTTP 請求並回傳503 錯誤碼,在Java層同樣也可以設計過載保護。

拒絕服務可以說是一種不得已的兜底方案,用以防止最壞情況發生,防止因把伺服器壓跨而長時間徹底無法提供服務。像這種系統過載保護雖然在過載時無法提供服務,但是系統仍然可以運作,當負載下降時又很容易恢復,所以每個系統和每個環節都應該設置這個兜底方案,對系統做最壞情況下的保護。

快取問題
快取雪崩
資料未載入到快取中,或快取同時在大範圍中失效,導致所有請求查找資料庫,導致資料庫、CPU 和記憶體過載,甚至停機。

一個簡單的雪崩過程:

1) Redis 叢集的大面積故障;

#2)快取失敗,但仍有大量請求存取快取服務Redis;

3) 在大量Redis 請求失敗後,請求轉向資料庫;

4) 資料庫請求急劇增加,導致資料庫被打死;

5) 由於你應用程式服務大部分都依賴於資料庫和Redis 服務,它很快就會導致伺服器叢集的雪崩,最後整個系統將徹底崩潰。

解決方案

事前:高可用的快取

高可用的快取是防止整個快取故障。即使個別節點,機器甚甚至機房都關閉,系統仍可提供服務,Redis 哨兵(Sentinel) 和 Redis 叢集(Cluster) 都可以做到高可用。

事中:快取降級(暫存支援)

當造訪次數急劇增加導致服務出現問題時,我們如何確保服務仍然可用。在國內使用更多的是 Hystrix,它透過熔斷、降級、限流三個手段來降低雪崩發生後的損失。只要確保資料庫不死,系統總是可以回應請求,每年的春節 12306 我們不都是這麼過來的嗎?只要還可以響應起碼還有搶到票的機會。

事後:Redis 備份與快速預熱

1) Redis 資料備份與還原

2)快速快取預熱

### ####快取擊穿#########快取擊穿意味著當熱點資料儲存到期時,多個執行緒同時請求熱點資料。因為快取剛過期,所有並發請求都會到資料庫查詢資料。 #########解決方案###:######實際上,在大多數實際業務場景中,快取擊穿是即時發生的,但不會對資料庫造成太大壓力,因為一般的公司業務,並發量不會那麼高。當然如果你不幸有這種情況,你可以透過設定這些熱點鍵,使其永遠不會過期。另一種方法是透過互斥鎖來控制查詢資料庫的執行緒訪問,但這種會導致系統的吞吐率下降,需要實際情況使用。 ###
快取穿透

快取穿透是指查詢一個一定不存在的數據,因為快取中也無該資料的信息,則會直接去資料庫層進行查詢,從系統層面來看像是穿透了快取層直接達到db,從而稱為快取穿透,沒有了快取層的保護,這種查詢一定不存在的資料對系統來說可能是一種危險,如果有人惡意用這種一定不存在的資料來頻繁地要求系統,不,準確的說是攻擊系統,請求都會到達資料庫層導致db癱瘓從而造成系統故障。

快取穿透產業內的解決方案已經比較成熟,主要常用的有以下幾種:

  • 布隆過濾器:類似哈希表的演算法,用所有可能的查詢條件產生一個bitmap,在進行資料庫查詢之前會使用這個bitmap進行過濾,如果不在其中則直接過濾,從而減輕資料庫層面的壓力。
  • 空值快取:比較簡單的解決方法,第一次查詢完不存在的資料後,將該key與對應的空值(null或物件裡只有key)也放入快取中,只不過設定為較短的失效時間,例如幾分鐘,這樣則可以應對短時間的大量的該key攻擊,設定為較短的失效時間是因為該值可能業務無關,存在意義不大,且該次的查詢也未必是攻擊者發起,無過久存儲的必要,故可以早點失效。

總結

由於本篇文章屬於理論篇,所以全篇沒有一行程式碼,但文中提出來的基本上就是秒殺系統所發生過的,每個系統可能發生的問題不同而已。

以上是6000多字 | 秒殺系統設計注意點的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:Java后端技术全栈
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
最新問題
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板