面試必備指南:你的系統如何支撐高併發?

一道面試題的背景引入面試

大多數同窗被問到這個問題壓根兒沒什麼思路去回答,不知道從什麼地方提及,其實本質就是沒經歷過一些真正有高併發系統的錘鍊罷了。數據庫

由於沒有過相關的項目經歷,因此就無法從真實的自身體會和經驗中提煉出一套回答,而後系統的闡述出來本身負責過的系統如何支撐高併發的。緩存

因此,這篇文章就從這個角度切入來簡單說說這個問題,用一個最簡單的思路來回答,大體如何應對。性能優化

固然這裏首先說清楚一個前提:高併發系統各不相同。好比每秒百萬併發的中間件系統、每日百億請求的網關係統、瞬時每秒幾十萬請求的秒殺大促系統。服務器

他們在應對高併發的時候,由於系統各自特色的不一樣,因此應對架構都是不同的。網絡

另外,好比電商平臺中的訂單系統、商品系統、庫存系統,在高併發場景下的架構設計也是不一樣的,由於背後的業務場景什麼的都不同。架構

因此,這篇文章主要是給你們提供一個回答這類問題的思路,不涉及任何複雜架構設計,讓你不至於在面試中被問到這個問題時,跟面試官大眼瞪小眼。併發

具體要真能在面試的時候回答好這個問題,建議各位參考一下本文思路,而後對你本身手頭負責的系統多去思考一下,最好作一些相關的架構實踐。負載均衡

先考慮一個最簡單的系統架構異步

假設剛剛開始你的系統就部署在一臺機器上,背後就鏈接了一臺數據庫,數據庫部署在一臺服務器上。

咱們甚至能夠再現實點,給個例子,你的系統部署的機器是 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 了麼?

你們看看,是否是發現減輕了數據庫的壓力?到目前爲止,經過下面的手段,咱們已經可讓系統架構儘量用最小的機器資源抗住了最大的請求壓力,減輕了數據庫的負擔:

  • 系統集羣化。
  • 數據庫層面的分庫分表+讀寫分離。
  • 針對讀多寫少的請求,引入緩存集羣。
  • 針對高寫入的壓力,引入消息中間件集羣。

初步來講,簡單的一個高併發系統的闡述是說完了。可是,故事到這裏還遠遠沒有結束。

如今能 Hold 住高併發面試題了嗎?

看完了這篇文章,你以爲本身能回答好面試裏的高併發問題了嗎?

很遺憾,答案是不能。並且我以爲單單憑藉幾篇文章是絕對不可能真的讓你徹底回答好這個問題的,這裏有不少緣由在裏面。

首先,高併發這個話題自己是很是複雜的,遠遠不是一些文章能夠說的清楚的,它的本質就在於,真實的支撐複雜業務場景的高併發系統架構實際上是很是複雜的。

好比說每秒百萬併發的中間件系統、每日百億請求的網關係統、瞬時每秒幾十萬請求的秒殺大促系統、支撐幾億用戶的大規模高併發電商平臺架構,等等。

爲了支撐高併發請求,在系統架構的設計時,會結合具體的業務場景和特色,設計出各類複雜的架構,這須要大量底層技術支撐,須要精妙的架構和機制設計的能力。

最終,各類複雜系統呈現出來的架構複雜度會遠遠超出大部分沒接觸過的同窗的想象。

可是那麼複雜的系統架構,經過一些文章是很難說的清楚裏面的各類細節以及落地生產的過程的。

其次,高併發這話題自己包含的內容也遠遠不止本文說的這麼幾個 topic:分庫分表、緩存、消息。

一個完整而複雜的高併發系統架構中,必定會包含:

  • 各類複雜的自研基礎架構系統。
  • 各類精妙的架構設計(好比熱點緩存架構設計、多優先級高吞吐 MQ 架構設計、系統全鏈路併發性能優化設計,等等)。
  • 還有各類複雜系統組合而成的高併發架構總體技術方案。
  • 還有 NoSQL(Elasticsearch 等)/負載均衡/Web 服務器等相關技術。

因此你們切記要對技術保持敬畏之心,這些東西都很難經過一些文章來表述清楚。

最後,真正在生產落地的時候,高併發場景下你的系統會出現大量的技術問題。

好比說消息中間件吞吐量上不去須要優化、磁盤寫壓力過大性能太差、內存消耗過大容易撐爆、分庫分表中間件不知道爲何丟了數據,等等吧。

諸如此類的問題很是多,這些也不可能經過文章給所有說清楚。

本文能帶給你什麼啓發?

其實本文的定位,就是對高併發這個面試 topic 作一個掃盲,由於我發現大部分來問我這個問題的同窗,連本文闡述的最最基本的高併發架構演進思路可能都沒理解。

固然,也是由於畢竟沒真的作太高併發系統,沒相關經驗,確實很難理解好這個問題。

因此本文就是讓不少沒接觸過的同窗有一個初步的感知,這個高併發究竟是怎麼回事兒,到底對系統哪裏有壓力,要在系統架構裏引入什麼東西,才能夠比較好的支撐住較高的併發壓力。

並且你能夠順着本文的思路繼續思考下去,結合你本身熟悉和知道的一些技術繼續思考。

好比說,你熟悉 Elasticsearch 技術,那麼你就能夠思考,在高併發的架構之下,是否是能夠經過分佈式架構的 ES 技術支撐高併發的搜索?

上面所說,權當拋磚引玉。你們本身平時必定要多思考,多畫圖,盤點本身手頭系統的請求壓力。

計算一下分散到各個中間件層面的請求壓力,到底應該如何利用最少的機器資源最好的支撐更高的併發請求。

這纔是一個好的高併發架構設計思路。

若是起到這個效果,本文就成功了。剩下的,仍是建議各位同窗,對高併發這個話題,結合本身手頭負責的系統多作思考。

好比當前業務場景下,你的系統有多大的請求壓力?若是請求壓力增加 10 倍,你的架構如何支撐?若是請求壓力增加 100 倍,你的架構如何支撐?若是請求壓力增加 1000 倍,你的架構如何支撐?

平時必定多給本身設置一些技術挑戰,敦促本身去思考本身的系統,最好多作寫架構上的演練、落地和實踐,實際操做一下,纔有更好的感知。

而後在面試的時候,起碼本身作過必定深度的思考,結合本身負責的系統作過一些實踐,能夠跟面試官有一個較爲清晰和系統的闡述。

雖然大部分同窗可能沒機會經歷那種真正大規模超高併發的系統架構的設計,可是本文若是能讓你們平時對本身的項目多一些思考。在面試的時候,有一些系統性的思路和闡述,那麼也就達到本文的目的了。

感興趣的能夠本身來個人Java架構羣,能夠獲取免費的學習資料,羣號:855801563對Java技術,架構技術感興趣的同窗,歡迎加羣,一塊兒學習,相互討論。

相關文章
相關標籤/搜索