很多人面試的時候被問到一個讓人特別手足無措的問題:你的系統(tǒng)如何支撐高并發(fā)?
大多數(shù)同學被問到這個問題壓根兒沒什么思路去回答猩系,不知道從什么地方說起,其實本質(zhì)就是沒經(jīng)歷過一些真正有高并發(fā)系統(tǒng)的錘煉罷了中燥。
因為沒有過相關的項目經(jīng)歷寇甸,所以就沒法從真實的自身體會和經(jīng)驗中提煉出一套回答,然后系統(tǒng)地闡述出來自己復雜過的系統(tǒng)如何支撐高并發(fā)的。
所以拿霉,這篇文章就從這個角度切入來簡單說說這個問題式塌,教你用一個最簡單的思路來如何應對的。
當然這里首先說清楚一個前提:高并發(fā)系統(tǒng)各不相同友浸。比如每秒百萬并發(fā)的中間件系統(tǒng)峰尝、每日百億請求的網(wǎng)關系統(tǒng)、瞬時每秒幾十萬請求的秒殺大促系統(tǒng)收恢。
他們在應對高并發(fā)的時候武学,因為系統(tǒng)各自特點的不同,所以應對架構(gòu)都是不一樣的伦意。
另外火窒,比如電商平臺中的訂單系統(tǒng)、商品系統(tǒng)驮肉、庫存系統(tǒng)熏矿,在高并發(fā)場景下的架構(gòu)設計也是不同的,因為背后的業(yè)務場景都不一樣离钝。
所以票编,這篇文章主要是給大家提供一個回答這類問題的思路,不涉及任何復雜架構(gòu)設計卵渴,讓你不至于在面試中被問到這個問題時慧域,跟面試官大眼瞪小眼。
具體要真能在面試的時候回答好這個問題浪读,建議各位參考一下本文思路昔榴,然后對你自己手頭負責的系統(tǒng)多去思考一下,最好做一些相關的架構(gòu)實踐碘橘。
先考慮一個最簡單的系統(tǒng)架構(gòu)
假設剛剛開始你的系統(tǒng)就部署在一臺機器上互订,背后就連接了一臺數(shù)據(jù)庫,數(shù)據(jù)庫部署在一臺服務器上痘拆。
我們甚至可以再現(xiàn)實點仰禽,例如你的系統(tǒng)部署的機器是4核8G,數(shù)據(jù)庫服務器是16核32G错负。
此時假設系統(tǒng)用戶量總共就10萬坟瓢,用戶量很少,日活用戶按照不同系統(tǒng)的場景有區(qū)別犹撒,我們?nèi)∫粋€較為客觀的比例折联,10%吧,每天活躍的用戶就1萬识颊。
按照二八法則诚镰,每天高峰期算4個小時奕坟,高峰期活躍的用戶占比達到80%,就是8000人活躍在4小時內(nèi)清笨。
然后每個人對系統(tǒng)發(fā)起請求月杉,我們按照每天20次算吧。那么高峰期8000人發(fā)起的請求也才16萬次抠艾,平均到4小時內(nèi)的每秒(14400秒)苛萎,每秒也就10次請求。
好吧检号!完全跟高并發(fā)搭不上邊腌歉,對不對?
然后系統(tǒng)層面每秒是10次請求齐苛,對數(shù)據(jù)庫的調(diào)用每次請求都會好幾次數(shù)據(jù)庫操作的翘盖,比如做CRUD之類的。
那么我們?nèi)∫粋€一次請求對應3次數(shù)據(jù)庫請求吧凹蜂,那這樣的話馍驯,數(shù)據(jù)庫層每秒也就30次請求,對不對玛痊?
按照這臺數(shù)據(jù)庫服務器的配置汰瘫,支撐是絕對沒問題的。
上述描述的系統(tǒng)卿啡,用一張圖表示吟吝,就是下面這樣:
系統(tǒng)集群化部署
假設此時用戶數(shù)開始快速增長颈娜,比如注冊用戶量增長了50倍,上升到了500萬浙宜。
此時日活用戶是50萬官辽,高峰期對系統(tǒng)每秒請求是500/s。然后對數(shù)據(jù)庫的每秒請求數(shù)量是1500/s粟瞬,這個時候會怎么樣呢同仆?
按照上述的機器配置來說,如果系統(tǒng)內(nèi)處理的是較為復雜的一些業(yè)務邏輯裙品,是那種重業(yè)務邏輯的系統(tǒng)的話俗批,是比較耗費CPU的。
此時市怎,4核8G的機器每秒請求達到500/s的時候岁忘,很可能你的機器CPU負載較高了。
然后數(shù)據(jù)庫層面区匠,以上述的配置而言干像,其實基本上1500/s的高峰請求壓力的話帅腌,還算可以接受。
這個主要是要觀察數(shù)據(jù)庫所在機器的磁盤負載麻汰、網(wǎng)絡負載速客、CPU負載、內(nèi)存負載五鲫,按照我們的線上經(jīng)驗而言溺职,那個配置的數(shù)據(jù)庫在1500/s請求壓力下是沒問題的。
所以此時需要做的一個事情位喂,首先就是要支持你的系統(tǒng)集群化部署浪耘。
可以在前面掛一個負載均衡層,把請求均勻打到系統(tǒng)層面忆某,讓系統(tǒng)可以用多臺機器集群化支撐更高的并發(fā)壓力点待。
比如說這里假設給系統(tǒng)增加部署一臺機器,那么每臺機器就只有250/s的請求了弃舒。
這樣一來癞埠,兩臺機器的CPU負載都會明顯降低,這個初步的“高并發(fā)”不就先cover住了嗎聋呢?
要是連這個都不做苗踪,那單臺機器負載越來越高的時候,極端情況下是可能出現(xiàn)機器上部署的系統(tǒng)無法有足夠的資源響應請求了削锰,然后出現(xiàn)請求卡死通铲,甚至系統(tǒng)宕機之類的問題。
所以器贩,簡單小結(jié)颅夺,第一步要做的:
添加負載均衡層,將請求均勻打到系統(tǒng)層蛹稍。
系統(tǒng)層采用集群化部署多臺機器吧黄,扛住初步的并發(fā)壓力。
此時的架構(gòu)圖變成下面的樣子:
數(shù)據(jù)庫分庫分表 + 讀寫分離
假設此時用戶量繼續(xù)增長拗慨,達到了1000萬注冊用戶,然后每天日活用戶是100萬奉芦。
那么此時對系統(tǒng)層面的請求量會達到每秒1000/s赵抢,系統(tǒng)層面,你可以繼續(xù)通過集群化的方式來擴容声功,反正前面的負載均衡層會均勻分散流量過去的烦却。
但是,這時數(shù)據(jù)庫層面接受的請求量會達到3000/s减噪,這個就有點問題了短绸。
此時數(shù)據(jù)庫層面的并發(fā)請求翻了一倍车吹,你一定會發(fā)現(xiàn)線上的數(shù)據(jù)庫負載越來越高。
每次到了高峰期醋闭,磁盤IO窄驹、網(wǎng)絡IO、內(nèi)存消耗证逻、CPU負載的壓力都會很高乐埠,大家很擔心數(shù)據(jù)庫服務器能否抗住。
沒錯囚企,一般來說丈咐,對那種普通配置的線上數(shù)據(jù)庫,建議就是讀寫并發(fā)加起來龙宏,按照上述我們舉例的那個配置棵逊,不要超過3000/s。
因為數(shù)據(jù)庫壓力過大银酗,首先一個問題就是高峰期系統(tǒng)性能可能會降低辆影,因為數(shù)據(jù)庫負載過高對性能會有影響。
另外一個黍特,壓力過大把你的數(shù)據(jù)庫給搞掛了怎么辦蛙讥?
所以此時你必須得對系統(tǒng)做分庫分表 + 讀寫分離,也就是把一個庫拆分為多個庫灭衷,部署在多個數(shù)據(jù)庫服務上次慢,這是作為主庫承載寫入請求的。
然后每個主庫都掛載至少一個從庫翔曲,由從庫來承載讀請求迫像。
此時假設對數(shù)據(jù)庫層面的讀寫并發(fā)是3000/s,其中寫并發(fā)占到了1000/s瞳遍,讀并發(fā)占到了2000/s侵蒙。
那么一旦分庫分表之后,采用兩臺數(shù)據(jù)庫服務器上部署主庫來支撐寫請求傅蹂,每臺服務器承載的寫并發(fā)就是500/s。每臺主庫掛載一個服務器部署從庫算凿,那么2個從庫每個從庫支撐的讀并發(fā)就是1000/s份蝴。
簡單總結(jié),并發(fā)量繼續(xù)增長時氓轰,我們就需要focus在數(shù)據(jù)庫層面:分庫分表婚夫、讀寫分離。
此時的架構(gòu)圖如下所示:
緩存集群引入
接著就好辦了案糙,如果注冊用戶量越來越大限嫌,此時你可以不停地加機器,比如說系統(tǒng)層面不停加機器时捌,就可以承載更高的并發(fā)請求怒医。
然后數(shù)據(jù)庫層面如果寫入并發(fā)越來越高,就擴容加數(shù)據(jù)庫服務器奢讨,通過分庫分表是可以支持擴容機器的稚叹,如果數(shù)據(jù)庫層面的讀并發(fā)越來越高,就擴容加更多的從庫拿诸。
但是這里有一個很大的問題:數(shù)據(jù)庫其實本身不是用來承載高并發(fā)請求的扒袖。所以通常來說,數(shù)據(jù)庫單機每秒承載的并發(fā)就在幾千的數(shù)量級亩码,而且數(shù)據(jù)庫使用的機器都是比較高配置季率,比較昂貴的機器,成本很高描沟。
如果不停地加機器飒泻,這是不對的。
在高并發(fā)架構(gòu)里通常都有緩存這個環(huán)節(jié)啊掏,緩存系統(tǒng)的設計就是為了承載高并發(fā)而生蠢络。
單機承載的并發(fā)量都在每秒幾萬,甚至每秒數(shù)十萬迟蜜,對高并發(fā)的承載能力比數(shù)據(jù)庫系統(tǒng)要高出一到兩個數(shù)量級刹孔。
可以根據(jù)系統(tǒng)的業(yè)務特性,對那種寫少讀多的請求娜睛,引入緩存集群髓霞。
具體來說,就是在寫數(shù)據(jù)庫的時候同時寫一份數(shù)據(jù)到緩存集群里畦戒,然后用緩存集群來承載大部分的讀請求方库。
這樣的話,通過緩存集群障斋,就可以用更少的機器資源承載更高的并發(fā)纵潦。
比如說上面那個圖里,讀請求目前是每秒2000/s垃环,兩個從庫各自抗了1000/s讀請求邀层,但是其中可能每秒1800次的讀請求都是可以直接讀緩存里的不怎么變化的數(shù)據(jù)的。
那么此時你一旦引入緩存集群遂庄,就可以抗下來這1800/s讀請求寥院,落到數(shù)據(jù)庫層面的讀請求就200/s。
同樣涛目,給大家來一張架構(gòu)圖秸谢,一起來感受一下:
按照上述架構(gòu),好處是什么呢估蹄?
可能未來你的系統(tǒng)讀請求每秒都幾萬次了塑煎,但是可能80%~90%都是通過緩存集群來讀的,而緩存集群里的機器可能單機每秒都可以支撐幾萬讀請求元媚,所以耗費機器資源很少轧叽,可能就兩三臺機器就夠了。
要是換成數(shù)據(jù)庫來試一下刊棕,可能就要不停地加從庫到10臺炭晒、20臺機器才能抗住每秒幾萬的讀并發(fā),那個成本是極高的甥角。
好了网严,我們再來簡單小結(jié),承載高并發(fā)需要考慮的第三個點:
不要盲目進行數(shù)據(jù)庫擴容嗤无,數(shù)據(jù)庫服務器成本昂貴震束,且本身就不是用來承載高并發(fā)的
針對寫少讀多的請求,引入緩存集群当犯,用緩存集群抗住大量的讀請求
引入消息中間件集群
接著再來看看數(shù)據(jù)庫寫這塊的壓力垢村,其實是跟讀類似的。
假如說所有寫請求全部都落地數(shù)據(jù)庫的主庫層嚎卫,當然是沒問題的嘉栓,但是寫壓力要是越來越大了呢?
比如每秒要寫幾萬條數(shù)據(jù)拓诸,此時難道也是不停的給主庫加機器嗎侵佃?
可以當然也可以,但是同理奠支,耗費的機器資源是很大的馋辈,這個就是數(shù)據(jù)庫系統(tǒng)的特點所決定的。
相同的資源下倍谜,數(shù)據(jù)庫系統(tǒng)太重太復雜迈螟,所以并發(fā)承載能力就在幾千/s的量級,所以此時你需要引入別的一些技術尔崔。
比如說消息中間件技術井联,也就是MQ集群,是非常好的做寫請求異步化處理您旁,實現(xiàn)削峰填谷的效果。
假如說轴捎,現(xiàn)在每秒是1000/s次寫請求鹤盒,其中比如500次請求是必須請求過來立馬寫入數(shù)據(jù)庫中的,但是另外500次寫請求是可以允許異步化等待個幾十秒,甚至幾分鐘后才落入數(shù)據(jù)庫內(nèi)的驯用。
那么此時完全可以引入消息中間件集群庶弃,把允許異步化的每秒500次請求寫入MQ,然后基于MQ做一個削峰填谷尺碰。比如就以平穩(wěn)的100/s的速度消費出來然后落入數(shù)據(jù)庫中即可挣棕,此時就會大幅度降低數(shù)據(jù)庫的寫入壓力。
此時亲桥,架構(gòu)圖變成了下面這樣:
大家看上面的架構(gòu)圖,首先消息中間件系統(tǒng)本身也是為高并發(fā)而生题篷,所以通常單機都是支撐幾萬甚至十萬級的并發(fā)請求的词身。
所以,這本身也跟緩存系統(tǒng)一樣番枚,可以用很少的資源支撐很高的并發(fā)請求法严,用來支撐部分允許異步化的高并發(fā)寫入是沒問題的,比使用數(shù)據(jù)庫直接支撐那部分高并發(fā)請求要減少很多的機器使用量葫笼。
而且經(jīng)過消息中間件的削峰填谷之后深啤,比如就用穩(wěn)定的100/s的速度寫數(shù)據(jù)庫,那么數(shù)據(jù)庫層面接收的寫請求壓力路星,不就成了500/s + 100/s = 600/s了么溯街?
大家看看,是不是發(fā)現(xiàn)減輕了數(shù)據(jù)庫的壓力奥额?
到目前為止苫幢,通過下面的手段,我們已經(jīng)可以讓系統(tǒng)架構(gòu)盡可能用最小的機器資源抗住了最大的請求壓力垫挨,減輕了數(shù)據(jù)庫的負擔韩肝。
系統(tǒng)集群化
數(shù)據(jù)庫層面的分庫分表+讀寫分離
針對讀多寫少的請求,引入緩存集群
針對高寫入的壓力九榔,引入消息中間件集群
初步來說哀峻,簡單的一個高并發(fā)系統(tǒng)的闡述是說完了。
但是哲泊,其實故事到這里還遠遠沒有結(jié)束剩蟀。
現(xiàn)在能Hold住高并發(fā)面試題了嗎?
看完了這篇文章切威,你覺得自己能回答好面試里的高并發(fā)問題了嗎育特?
很遺憾,答案是不能。而且我覺得單單憑借幾篇文章是絕對不可能真的讓你完全回答好這個問題的缰冤,這里有很多原因在里面犬缨。
首先,高并發(fā)這個話題本身是非常復雜的棉浸,遠遠不是一些文章可以說的清楚的怀薛,本質(zhì)就在于,真實的支撐復雜業(yè)務場景的高并發(fā)系統(tǒng)架構(gòu)其實是非常復雜的迷郑。
比如說每秒百萬并發(fā)的中間件系統(tǒng)枝恋、每日百億請求的網(wǎng)關系統(tǒng)、瞬時每秒幾十萬請求的秒殺大促系統(tǒng)嗡害、支撐幾億用戶的大規(guī)模高并發(fā)電商平臺架構(gòu)焚碌,等等。
為了支撐高并發(fā)請求就漾,在系統(tǒng)架構(gòu)的設計時呐能,會結(jié)合具體的業(yè)務場景和特點,設計出各種復雜的架構(gòu)抑堡,這需要大量底層技術支撐摆出,需要精妙的架構(gòu)和機制設計的能力。
最終首妖,各種復雜系統(tǒng)呈現(xiàn)出來的架構(gòu)復雜度會遠遠超出大部分沒接觸過的同學的想象偎漫。
但是那么復雜的系統(tǒng)架構(gòu),通過一些文章是很難說的清楚里面的各種細節(jié)以及落地生產(chǎn)的過程的有缆。
其次象踊,高并發(fā)這話題本身包含的內(nèi)容也遠遠不止本文說的這么幾個topic:分庫分表、緩存棚壁、消息杯矩。
一個完整而復雜的高并發(fā)系統(tǒng)架構(gòu)中,一定會包含各種復雜的自研基礎架構(gòu)系統(tǒng)袖外、各種精妙的架構(gòu)設計(比如熱點緩存架構(gòu)設計史隆、多優(yōu)先級高吞吐MQ架構(gòu)設計、系統(tǒng)全鏈路并發(fā)性能優(yōu)化設計曼验,等等)泌射、還有各種復雜系統(tǒng)組合而成的高并發(fā)架構(gòu)整體技術方案、還有NoSQL(Elasticsearch等)/負載均衡/Web服務器等相關技術鬓照。
所以大家切記要對技術保持敬畏之心熔酷,這些東西都很難通過一些文章來表述清楚。
最后豺裆,真正在生產(chǎn)落地的時候拒秘,高并發(fā)場景下系統(tǒng)會出現(xiàn)大量的技術問題。
比如說消息中間件吞吐量上不去需要優(yōu)化、磁盤寫壓力過大性能太差翼抠、內(nèi)存消耗過大容易撐爆咙轩、分庫分表中間件不知道為什么丟了數(shù)據(jù)等等吧。
諸如此類的問題非常多阴颖,這些也不可能通過文章給全部說清楚。
本文能帶給你什么啟發(fā)丐膝?
其實本文的定位量愧,就是對高并發(fā)這個面試題做一個掃盲,因為我發(fā)現(xiàn)大部分來問我這個問題的同學帅矗,連本文闡述的最基本的高并發(fā)架構(gòu)演進思路可能都沒理解偎肃。
當然,也是因為畢竟沒真的做過高并發(fā)系統(tǒng)浑此,沒相關經(jīng)驗累颂,確實很難理解好這個問題。
所以本文就是讓很多沒接觸過的同學有一個初步的感知凛俱,這個高并發(fā)到底是怎么回事兒紊馏,到底對系統(tǒng)哪里有壓力,要在系統(tǒng)架構(gòu)里引入什么東西蒲犬,才可以比較好的支撐住較高的并發(fā)壓力朱监。
而且你可以順著本文的思路繼續(xù)思考下去,結(jié)合你自己熟悉和知道的一些技術繼續(xù)思考原叮。
比如說赫编,你熟悉ElasticSearch技術,那么你就可以思考:在高并發(fā)的架構(gòu)之下奋隶,是不是可以通過分布式架構(gòu)的ES技術支撐高并發(fā)的搜索擂送?
上面所說,權當拋磚引玉唯欣。大家自己平時一定要多思考嘹吨,自己多畫圖,盤點盤點自己手頭系統(tǒng)的請求壓力黍聂。計算一下分散到各個中間件層面的請求壓力躺苦,到底應該如何利用最少的機器資源最好的支撐更高的并發(fā)請求。
這才是一個好的高并發(fā)架構(gòu)設計思路产还。
如果起到這個效果匹厘,本文就成功了。剩下的還是建議各位同學脐区,對高并發(fā)這個話題愈诚,結(jié)合自己手頭負責的系統(tǒng)多做思考。
比如當前業(yè)務場景下,你的系統(tǒng)有多大的請求壓力炕柔?如果請求壓力增長10倍你的架構(gòu)如何支撐酌泰?如果請求壓力增長100倍,你的架構(gòu)如何支撐匕累?如果請求壓力增長1000倍陵刹,你的架構(gòu)如何支撐?
平時一定多給自己設置一些技術挑戰(zhàn)欢嘿,敦促自己去思考自己的系統(tǒng)衰琐,最好多做寫架構(gòu)上的演練、落地和實踐炼蹦,自己實際操作一下羡宙,才有更好的感知。
然后在面試的時候掐隐,起碼自己做過一定深度的思考狗热,結(jié)合自己負責的系統(tǒng)做過一些實踐,可以跟面試官有一個較為清晰和系統(tǒng)的闡述虑省。
雖然大部分同學可能沒機會經(jīng)歷那種真正大規(guī)模超高并發(fā)的系統(tǒng)架構(gòu)的設計匿刮,但是本文如果能讓大家平時對自己的項目多一些思考。在面試的時候慷妙,有一些系統(tǒng)性的思路和闡述僻焚,那么也就達到本文的目的了
歡迎工作一到五年的Java工程師朋友們加入Java高并發(fā)QQ群:219571750,群內(nèi)提供免費的Java架構(gòu)學習資料(里面有高可用膝擂、高并發(fā)虑啤、高性能及分布式、Jvm性能調(diào)優(yōu)架馋、Spring源碼狞山,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多個知識點的架構(gòu)資料)合理利用自己每一分每一秒的時間來學習提升自己叉寂,不要再用"沒有時間“來掩飾自己思想上的懶惰萍启!趁年輕,使勁拼屏鳍,給未來的自己一個交代勘纯!