高并發系統各不相同。比如每秒百萬并發的中間件系統、每日百億請求的網關系統、瞬時每秒幾十萬請求的秒殺大促系統。
他們在應對高并發的時候,因為系統各自特點的不同,所以應對架構都是不一樣的。
另外,比如電商平臺中的訂單系統、商品系統、庫存系統,在高并發場景下的架構設計也是不同的,因為背后的業務場景什么的都不一樣。
最簡單的系統架構
假設剛剛開始你的系統就部署在一臺機器上,背后就連接了一臺數據庫,數據庫部署在一臺服務器上。
我們甚至可以再現實點,給個例子,你的系統部署的機器是 4 核 8G,數據庫服務器是 16 核 32G。
此時假設你的系統用戶量總共就 10 萬,用戶量很少,日活用戶按照不同系統的場景有區別,我們取一個較為客觀的比例,10% 吧,每天活躍的用戶就 1 萬。
按照 28 法則,每天高峰期算它 4 個小時,高峰期活躍的用戶占比達到 80%,就是 8000 人活躍在 4 小時內。
然后每個人對你的系統發起的請求,我們算他每天是 20 次吧。那么高峰期 8000 人發起的請求也才 16 萬次,平均到 4 小時內的每秒(14400 秒),每秒也就 10 次請求。
好吧!完全跟高并發搭不上邊,對不對?
然后系統層面每秒是 10 次請求,對數據庫的調用每次請求都會有好幾次數據庫操作的,比如做做 crud 之類的。
那么我們取一個一次請求對應 3 次數據庫請求吧,那這樣的話,數據庫層每秒也就 30 次請求,對不對?
按照這臺數據庫服務器的配置,支撐是絕對沒問題的。上述描述的系統,用一張圖表示,就是下面這樣:
系統集群化部署
假設此時你的用戶數開始快速增長,比如注冊用戶量增長了 50 倍,上升到了 500 萬。
此時日活用戶是 50 萬,高峰期對系統每秒請求是 500/s。然后對數據庫的每秒請求數量是 1500/s,這個時候會怎么樣呢?
按照上述的機器配置來說,如果你的系統內處理的是較為復雜的一些業務邏輯,是那種重業務邏輯的系統的話,是比較耗費 CPU 的。
此時,4 核 8G 的機器每秒請求達到 500/s 的時候,很可能你會發現你的機器 CPU 負載較高了。
然后數據庫層面,以上述的配置而言,其實基本上 1500/s 的高峰請求壓力的話,還算可以接受。
這個主要是要觀察數據庫所在機器的磁盤負載、網絡負載、CPU 負載、內存負載,按照我們的線上經驗而言,那個配置的數據庫在 1500/s 請求壓力下是沒問題的。
所以此時你需要做的一個事情,首先就是要支持你的系統集群化部署。
你可以在前面掛一個負載均衡層,把請求均勻打到系統層面,讓系統可以用多臺機器集群化支撐更高的并發壓力。
比如說這里假設給系統增加部署一臺機器,那么每臺機器就只有 250/s 的請求了。
這樣一來,兩臺機器的 CPU 負載都會明顯降低,這個初步的“高并發”不就先 cover 住了嗎?
要是連這個都不做,那單臺機器負載越來越高的時候,極端情況下是可能出現機器上部署的系統無法有足夠的資源響應請求了,然后出現請求卡死,甚至系統宕機之類的問題。
所以,簡單小結,第一步要做的:
- 添加負載均衡層,將請求均勻打到系統層。
- 系統層采用集群化部署多臺機器,扛住初步的并發壓力。
此時的架構圖變成下面的樣子:
數據庫分庫分表 + 讀寫分離
假設此時用戶量繼續增長,達到了 1000 萬注冊用戶,然后每天日活用戶是 100 萬。
那么此時對系統層面的請求量會達到每秒 1000/s,系統層面,你可以繼續通過集群化的方式來擴容,反正前面的負載均衡層會均勻分散流量過去的。
但是,這時數據庫層面接受的請求量會達到 3000/s,這個就有點問題了。
此時數據庫層面的并發請求翻了一倍,你一定會發現線上的數據庫負載越來越高。
每次到了高峰期,磁盤 IO、網絡 IO、內存消耗、CPU 負載的壓力都會很高,大家很擔心數據庫服務器能否抗住。
沒錯,一般來說,對那種普通配置的線上數據庫,建議就是讀寫并發加起來,按照上述我們舉例的那個配置,不要超過 3000/s。
因為數據庫壓力過大,首先一個問題就是高峰期系統性能可能會降低,因為數據庫負載過高對性能會有影響。
另外一個,壓力過大把你的數據庫給搞掛了怎么辦?
所以此時你必須得對系統做分庫分表 + 讀寫分離,也就是把一個庫拆分為多個庫,部署在多個數據庫服務上,這是作為主庫承載寫入請求的。
然后每個主庫都掛載至少一個從庫,由從庫來承載讀請求。
此時假設對數據庫層面的讀寫并發是 3000/s,其中寫并發占到了 1000/s,讀并發占到了 2000/s。
那么一旦分庫分表之后,采用兩臺數據庫服務器上部署主庫來支撐寫請求,每臺服務器承載的寫并發就是 500/s。
每臺主庫掛載一個服務器部署從庫,那么 2 個從庫每個從庫支撐的讀并發就是 1000/s。
簡單總結,并發量繼續增長時,我們就需要 focus 在數據庫層面:分庫分表、讀寫分離。
此時的架構圖如下所示:
緩存集群引入
接著就好辦了,如果你的注冊用戶量越來越大,此時你可以不停的加機器,比如說系統層面不停加機器,就可以承載更高的并發請求。
然后數據庫層面如果寫入并發越來越高,就擴容加數據庫服務器,通過分庫分表是可以支持擴容機器的,如果數據庫層面的讀并發越來越高,就擴容加更多的從庫。
但是這里有一個很大的問題:數據庫其實本身不是用來承載高并發請求的,所以通常來說,數據庫單機每秒承載的并發就在幾千的數量級,而且數據庫使用的機器都是比較高配置,比較昂貴的機器,成本很高。
如果你就是簡單的不停的加機器,其實是不對的。
所以在高并發架構里通常都有緩存這個環節,緩存系統的設計就是為了承載高并發而生。
所以單機承載的并發量都在每秒幾萬,甚至每秒數十萬,對高并發的承載能力比數據庫系統要高出一到兩個數量級。
所以你完全可以根據系統的業務特性,對那種寫少讀多的請求,引入緩存集群。
具體來說,就是在寫數據庫的時候同時寫一份數據到緩存集群里,然后用緩存集群來承載大部分的讀請求。
這樣的話,通過緩存集群,就可以用更少的機器資源承載更高的并發。
比如說上面那個圖里,讀請求目前是每秒 2000/s,兩個從庫各自抗了 1000/s 讀請求,但是其中可能每秒 1800 次的讀請求都是可以直接讀緩存里的不怎么變化的數據的。
那么此時你一旦引入緩存集群,就可以抗下來這 1800/s 讀請求,落到數據庫層面的讀請求就 200/s。
同樣,給大家來一張架構圖,一起來感受一下:
按照上述架構,它的好處是什么呢?
可能未來你的系統讀請求每秒都幾萬次了,但是可能 80%~90% 都是通過緩存集群來讀的,而緩存集群里的機器可能單機每秒都可以支撐幾萬讀請求,所以耗費機器資源很少,可能就兩三臺機器就夠了。
你要是換成是數據庫來試一下,可能就要不停的加從庫到 10 臺、20 臺機器才能抗住每秒幾萬的讀并發,那個成本是極高的。
好了,我們再來簡單小結,承載高并發需要考慮的第三個點:
- 不要盲目進行數據庫擴容,數據庫服務器成本昂貴,且本身就不是用來承載高并發的。
- 針對寫少讀多的請求,引入緩存集群,用緩存集群抗住大量的讀請求。
引入消息中間件集群
接著再來看看數據庫寫這塊的壓力,其實是跟讀類似的。
假如說你所有寫請求全部都落地數據庫的主庫層,當然是沒問題的,但是寫壓力要是越來越大了呢?
比如每秒要寫幾萬條數據,此時難道也是不停的給主庫加機器嗎?
可以當然也可以,但是同理,你耗費的機器資源是很大的,這個就是數據庫系統的特點所決定的。
相同的資源下,數據庫系統太重太復雜,所以并發承載能力就在幾千/s的量級,所以此時你需要引入別的一些技術。
比如說消息中間件技術,也就是 MQ 集群,它可以非常好的做寫請求異步化處理,實現削峰填谷的效果。
假如說,你現在每秒是 1000/s 次寫請求,其中比如 500 次請求是必須請求過來立馬寫入數據庫中的,但是另外 500 次寫請求是可以允許異步化等待個幾十秒,甚至幾分鐘后才落入數據庫內的。
那么此時你完全可以引入消息中間件集群,把允許異步化的每秒 500 次請求寫入 MQ,然后基于 MQ 做一個削峰填谷。
比如就以平穩的 100/s 的速度消費出來,然后落入數據庫中即可,此時就會大幅度降低數據庫的寫入壓力。
此時,架構圖變成了下面這樣:
大家看上面的架構圖,首先消息中間件系統本身也是為高并發而生,所以通常單機都是支撐幾萬甚至十萬級的并發請求的。
所以,它本身也跟緩存系統一樣,可以用很少的資源支撐很高的并發請求,用它來支撐部分允許異步化的高并發寫入是沒問題的,比使用數據庫直接支撐那部分高并發請求要減少很多的機器使用量。
而且經過消息中間件的削峰填谷之后,比如就用穩定的 100/s 的速度寫數據庫,那么數據庫層面接收的寫請求壓力,不就成了 500/s + 100/s = 600/s 了么?
大家看看,是不是發現減輕了數據庫的壓力?到目前為止,通過下面的手段,我們已經可以讓系統架構盡可能用最小的機器資源抗住了最大的請求壓力,減輕了數據庫的負擔:
- 系統集群化。
- 數據庫層面的分庫分表+讀寫分離。
- 針對讀多寫少的請求,引入緩存集群。
- 針對高寫入的壓力,引入消息中間件集群。
初步來說,簡單的一個高并發系統的闡述是說完了。但是,故事到這里還遠遠沒有結束。
首先,高并發這個話題本身是非常復雜的,遠遠不是一些文章可以說的清楚的,它的本質就在于,真實的支撐復雜業務場景的高并發系統架構其實是非常復雜的。
比如說每秒百萬并發的中間件系統、每日百億請求的網關系統、瞬時每秒幾十萬請求的秒殺大促系統、支撐幾億用戶的大規模高并發電商平臺架構,等等。
為了支撐高并發請求,在系統架構的設計時,會結合具體的業務場景和特點,設計出各種復雜的架構,這需要大量底層技術支撐,需要精妙的架構和機制設計的能力。
最終,各種復雜系統呈現出來的架構復雜度會遠遠超出大部分沒接觸過的同學的想象。
但是那么復雜的系統架構,通過一些文章是很難說的清楚里面的各種細節以及落地生產的過程的。
其次,高并發這話題本身包含的內容也遠遠不止本文說的這么幾個 topic:分庫分表、緩存、消息。
一個完整而復雜的高并發系統架構中,一定會包含:
- 各種復雜的自研基礎架構系統。
- 各種精妙的架構設計(比如熱點緩存架構設計、多優先級高吞吐 MQ 架構設計、系統全鏈路并發性能優化設計,等等)。
- 還有各種復雜系統組合而成的高并發架構整體技術方案。
- 還有 NoSQL(Elasticsearch 等)/負載均衡/Web 服務器等相關技術。
所以大家切記要對技術保持敬畏之心,這些東西都很難通過一些文章來表述清楚。
最后,真正在生產落地的時候,高并發場景下你的系統會出現大量的技術問題。
比如說消息中間件吞吐量上不去需要優化、磁盤寫壓力過大性能太差、內存消耗過大容易撐爆、分庫分表中間件不知道為什么丟了數據,等等。