討論一下秒殺系統的技術難點與解決方案
前言
小夥伴們,我們先回顧一下,在一個系統中引入消息中間件後會給系統帶來什麼好處呢?
通過之前的文章我們了解到,引入MQ後主要可以解決三個問題:異步、解耦、削峰,小夥伴們可以回顧一下這篇文章什麼是消息中間件?主要作用是什麼?
今天王子就和大家聊一聊削峰的具體場景,針對一個電商系統中,秒殺系統這部分的技術難點與解決方案。
系統面臨的瓶頸是什麼
我們先了解一下,秒殺系統中具體有什麼問題需要解決呢?王子給大家畫一張圖:
假如我們的系統有了秒殺的業務,那麼會有海量的用戶訪問我們的訂單系統集群,其實這裡還不是技術的瓶頸,只要擴展訂單系統集群,增加訂單系統的機器數量就可以抗下這樣甚至更高的高並發情況。
那麼技術瓶頸是什麼呢?
我們再來看數據庫部分,你會發現無論你的訂單系統集群增加多少機器,它們訪問的還是一個數據庫。那麼每次面對秒殺系統這樣的活動時,數據庫要承受的壓力是極大的,很可能因此宕機,導致整個系統崩掉,後果是很可怕的。
由此我們分析出,數據庫是秒殺系統面臨的一大瓶頸。
如何解決秒殺系統的瓶頸
剛才我們談到,秒殺系統面臨的技術瓶頸是數據庫,那麼我們如何解決呢?是不是要部署更多的數據庫服務器,對數據庫進行分庫分表,然後讓更多的數據庫服務器共同抗下高並發的情況呢?
這種分庫分表的策略是這樣的,假設我們目前操作的是一個庫中的一張訂單表,那麼分庫後就變成了多個庫,每個庫里都只存一部分的訂單數據,分庫策略可以是按時間戳或者哈希算法計算得出(這不是本篇重點,以後會有單獨的數據庫專題討論),分表呢就是可以在一個庫里對訂單表再次拆分成多張表,數據再次分片存儲。
這種模式有什麼好處呢?
好處其實是顯而易見的,我們的高並發請求可以平均分配到多台數據庫上,整個的數據庫集群可以共同承擔高並髮帶來的壓力,而且也可以通過擴展數據庫集群實現可以承擔更高並發的能力。
說到這裡,小夥伴們是不是覺得,這個問題就這麼解決了呢?
王子可以明確的告訴大家,這種解決方法是很不靠譜的,除非公司的技術能力太弱,沒有人能搭建出更可靠的架構才會選擇這種下下策,通過堆疊機器數量來抵抗高並發下的壓力。
試着想一下,假如我們的系統很受客戶歡迎,用戶量日漸增長,達到了海量的用戶數,難道我們要不停的增加服務器數量嗎?
服務器的成本是不是有點偏高了呢?
所以要解決這個問題,我們的出發點一定要正確,就是不能通過不停的增加機器解決這種問題,而是在有限的資源下設計出更優雅的架構來解決,這樣的方法才是上策。
前台頁面的優化
知道了我們應該在有限資源下進行架構優化,那麼我們先思考一個問題。
用戶參加秒殺活動的時候,是如何操作系統的呢?
就拿雙十一搶購來說,在00:00的時候就是秒殺的時候,那麼很多用戶會提前幾分鐘把手機打開到對應的頁面,沒到時間就開始不停的刷新頁面,等待秒殺開始那一刻的到來。
那麼小夥伴們有沒有考慮過,這些要刷新的頁面都是從哪裡來的呢?
我們的頁面其實也是要有自己專門的訂單頁面服務器的,主要用於提供前端的訪問頁面,基本結構如下圖:
所以,首先接受高並發請求的系統是前端頁面系統。
大家思考一個問題,如果平時不是秒殺的時候,用戶看的商品可能都是不同的,但是一旦有秒殺活動,可能有大量的用戶一起不停的刷新同一個商品的同一個頁面,對系統造成壓力。
那麼對於這個問題,我們應該怎麼解決呢?
王子今天介紹的解決方案就是頁面數據的靜態化+多級緩存的策略。
頁面數據靜態化
我們先來聊一聊什麼是頁面數據的動態化。
假設我們的前端頁面是動態化的,那麼用戶每次訪問頁面,都會向頁面系統發送請求獲取數據,然後前端頁面根據獲取的數據渲染頁面,一般來說系統的演進都是從這種動態化開始的,比如jsp頁面。
那麼如何實現頁面的靜態化呢,其實就是改變頁面獲取數據的方式,每次獲取數據不再是通過頁面系統查詢數據庫而是從別的地方獲取數據,避免每次都去訪問後端數據庫,對系統造成壓力。
多級緩存
了解的靜態化的思想,那麼我們再來看看多級緩存是什麼,我們要聊的多級緩存指的是CDN+Nginx+Redis的多級緩存架構。
什麼意思呢?就是說頁面的數據首先放一份到離用戶最近的CDN上邊。
可能有的小夥伴不理解CDN,王子給大家簡單掃一下盲。
比如我們的系統服務器部署在北京,訪問我們系統的用戶在海南,那麼它每次訪問我們系統是要到我們北京的服務器上面獲取數據嗎?
不是的,我們是可以把靜態化的頁面數據部署到海南的CDN上邊去的,而海南的用戶就可以通過CDN獲取到我們系統的頁面數據。
這個CDN都是各種雲廠商提供的服務,它就是我們架構中的第一級緩存。
如果由於CDN中緩存過期等原因,導致沒有從CDN中得到頁面數據,那麼此時用戶就會將這個請求發送到我們北京的服務器上邊,但是這個時候系統不是直接查詢數據庫返回數據的,而是先訪問Nginx服務器上的緩存。
Nginx是可以基於Lua腳本實現本地緩存的,我們可以提前吧頁面數據放到Nginx緩存中,作為第二級的緩存。
如果Nginx上邊也沒有想要的數據呢?
那麼此時可以通過Nginx上的lua腳本發送請求到Redis集群中加載數據,Redis集群就作為我們多級緩存架構的第三級緩存。
如果在Redis集群中還是沒有找到數據,我們再去從數據庫加載出來,並更新到緩存里。
通過這樣一套多級緩存的架構,我們就可以實現頁面的靜態化數據的存儲(數據可能就是一段json串),這樣對於我們的頁面服務器本身壓力就非常的小了。
架構圖如下:
總結
今天我們聊了聊高並發系統下,堆積機器方案的弊端,同時也介紹了秒殺場景下面臨的一些技術挑戰。
又講解了使用多級緩存架構構建靜態化頁面的方式。
但秒殺系統是一個複雜的系統,深入研究細節是很多的,王子主要是在這裡介紹一下秒殺系統的整體場景,和針對於秒殺系統做的一些架構優化的思路,從而引出如何將RocketMQ落實到秒殺系統中,實現流量的削峰功效。
那麼下篇文章我們就一起聊聊如何將MQ引入到系統中,進行流量削峰的優化吧。
往期文章推薦:
中間件專輯:
算法專輯: