高併發,你真的理解透徹了嗎?

前言

高併發,幾乎是每一個程序員都想擁有的經驗。緣由很簡單:隨着流量變大,會遇到各類各樣的技術問題,好比接口響應超時、CPU load升高、GC頻繁、死鎖、大數據量存儲等等,這些問題能推進咱們在技術深度上不斷精進。前端

做爲閱讀福利,小編也整理一套高併發相關的學習筆記(包含腦圖、面試、手寫pdf等)如今免費分享給閱讀到本篇的Java程序員朋友們,須要的自行領取程序員

最全 高併發 學習筆記+面試真題面試

在過往的面試中,若是候選人作太高併發的項目,我一般會讓對方談談對於高併發的理解,可是能系統性地回答好此問題的人並很少,大概分紅這樣幾類:redis

一、對數據化的指標沒有概念:不清楚選擇什麼樣的指標來衡量高併發系統?分不清併發量和QPS,甚至不知道本身系統的總用戶量、活躍用戶量,平峯和高峯時的QPS和TPS等關鍵數據。算法

二、設計了一些方案,可是細節掌握不透徹:講不出該方案要關注的技術點和可能帶來的反作用。好比讀性能有瓶頸會引入緩存,可是忽視了緩存命中率、熱點key、數據一致性等問題。數據庫

三、理解片面,把高併發設計等同於性能優化:大談併發編程、多級緩存、異步化、水平擴容,卻忽視高可用設計、服務治理和運維保障。編程

四、掌握大方案,卻忽視最基本的東西:能講清楚垂直分層、水平分區、緩存等大思路,卻沒意識去分析數據結構是否合理,算法是否高效,沒想過從最根本的IO和計算兩個維度去作細節優化。緩存

這篇文章,我想結合本身的高併發項目經驗,系統性地總結下高併發須要掌握的知識和實踐思路,但願對你有所幫助。內容分紅如下3個部分:性能優化

  • 如何理解高併發?
  • 高併發系統設計的目標是什麼?
  • 高併發的實踐方案有哪些?

如何理解高併發?

高併發意味着大流量,須要運用技術手段抵抗流量的衝擊,這些手段比如操做流量,能讓流量更平穩地被系統所處理,帶給用戶更好的體驗。服務器

咱們常見的高併發場景有:淘寶的雙十一、春運時的搶票、微博大V的熱點新聞等。除了這些典型事情,每秒幾十萬請求的秒殺系統、天天千萬級的訂單系統、天天億級日活的信息流系統等,均可以歸爲高併發。

很顯然,上面談到的高併發場景,併發量各不相同,那到底多大併發纔算高併發呢?

一、不能只看數字,要看具體的業務場景。不能說10W QPS的秒殺是高併發,而1W QPS的信息流就不是高併發。信息流場景涉及複雜的推薦模型和各類人工策略,它的業務邏輯可能比秒殺場景複雜10倍不止。所以,不在同一個維度,沒有任何比較意義。

二、業務都是從0到1作起來的,併發量和QPS只是參考指標,最重要的是:在業務量逐漸變成原來的10倍、100倍的過程當中,你是否用到了高併發的處理方法去演進你的系統,從架構設計、編碼實現、甚至產品方案等維度去預防和解決高併發引發的問題?而不是一味的升級硬件、加機器作水平擴展。

此外,各個高併發場景的業務特色徹底不一樣:有讀多寫少的信息流場景、有讀多寫多的交易場景,那是否有通用的技術方案解決不一樣場景的高併發問題呢?

我以爲大的思路能夠借鑑,別人的方案也能夠參考,可是真正落地過程當中,細節上還會有無數的坑。另外,因爲軟硬件環境、技術棧、以及產品邏輯都無法作到徹底一致,這些都會致使一樣的業務場景,就算用相同的技術方案也會面臨不一樣的問題,這些坑還得一個個趟。

所以,這篇文章我會將重點放在基礎知識、通用思路、和我曾經實踐過的有效經驗上,但願讓你對高併發有更深的理解。

高併發系統設計的目標是什麼?

先搞清楚高併發系統設計的目標,在此基礎上再討論設計方案和實踐經驗纔有意義和針對性。

2.1 宏觀目標

高併發毫不意味着只追求高性能,這是不少人片面的理解。從宏觀角度看,高併發系統設計的目標有三個:高性能、高可用,以及高可擴展。

一、高性能:性能體現了系統的並行處理能力,在有限的硬件投入下,提升性能意味着節省成本。同時,性能也反映了用戶體驗,響應時間分別是100毫秒和1秒,給用戶的感覺是徹底不一樣的。

二、高可用:表示系統能夠正常服務的時間。一個整年不停機、無端障;另外一個隔三差五出線上事故、宕機,用戶確定選擇前者。另外,若是系統只能作到90%可用,也會大大拖累業務。

三、高擴展:表示系統的擴展能力,流量高峯時可否在短期內完成擴容,更平穩地承接峯值流量,好比雙11活動、明星離婚等熱點事件。 這3個目標是須要通盤考慮的,由於它們互相關聯、甚至也會相互影響。

好比說:考慮系統的擴展能力,你會將服務設計成無狀態的,這種集羣設計保證了高擴展性,其實也間接提高了系統的性能和可用性。

再好比說:爲了保證可用性,一般會對服務接口進行超時設置,以防大量線程阻塞在慢請求上形成系統雪崩,那超時時間設置成多少合理呢?通常,咱們會參考依賴服務的性能表現進行設置。

2.2 微觀目標

再從微觀角度來看,高性能、高可用和高擴展又有哪些具體的指標來衡量?爲何會選擇這些指標呢?

2.2.1 性能指標

經過性能指標能夠度量目前存在的性能問題,同時做爲性能優化的評估依據。通常來講,會採用一段時間內的接口響應時間做爲指標。

一、平均響應時間:最經常使用,可是缺陷很明顯,對於慢請求不敏感。好比1萬次請求,其中9900次是1ms,100次是100ms,則平均響應時間爲1.99ms,雖然平均耗時僅增長了0.99ms,可是1%請求的響應時間已經增長了100倍。

二、TP90、TP99等分位值:將響應時間按照從小到大排序,TP90表示排在第90分位的響應時間, 分位值越大,對慢請求越敏感。 三、吞吐量:和響應時間呈反比,好比響應時間是1ms,則吞吐量爲每秒1000次。

一般,設定性能目標時會兼顧吞吐量和響應時間,好比這樣表述:在每秒1萬次請求下,AVG控制在50ms如下,TP99控制在100ms如下。對於高併發系統,AVG和TP分位值必須同時要考慮。

另外,從用戶體驗角度來看,200毫秒被認爲是第一個分界點,用戶感受不到延遲,1秒是第二個分界點,用戶能感覺到延遲,可是能夠接受。

所以,對於一個健康的高併發系統,TP99應該控制在200毫秒之內,TP999或者TP9999應該控制在1秒之內。

2.2.2 可用性指標

高可用性是指系統具備較高的無端障運行能力,可用性 = 正常運行時間 / 系統總運行時間,通常使用幾個9來描述系統的可用性。 對於高併發系統來講,最基本的要求是:保證3個9或者4個9。緣由很簡單,若是你只能作到2個9,意味着有1%的故障時間,像一些大公司每一年動輒千億以上的GMV或者收入,1%就是10億級別的業務影響。

2.2.3 可擴展性指標

面對突發流量,不可能臨時改造架構,最快的方式就是增長機器來線性提升系統的處理能力。

對於業務集羣或者基礎組件來講,擴展性 = 性能提高比例 / 機器增長比例,理想的擴展能力是:資源增長几倍,性能提高几倍。一般來講,擴展能力要維持在70%以上。

可是從高併發系統的總體架構角度來看,擴展的目標不只僅是把服務設計成無狀態就好了,由於當流量增長10倍,業務服務能夠快速擴容10倍,可是數據庫可能就成爲了新的瓶頸。

像MySQL這種有狀態的存儲服務一般是擴展的技術難點,若是架構上沒提早作好規劃(垂直和水平拆分),就會涉及到大量數據的遷移。

所以,高擴展性須要考慮:服務集羣、數據庫、緩存和消息隊列等中間件、負載均衡、帶寬、依賴的第三方等,當併發達到某一個量級後,上述每一個因素均可能成爲擴展的瓶頸點。

高併發的實踐方案有哪些?

瞭解了高併發設計的3大目標後,再系統性總結下高併發的設計方案,會從如下兩部分展開:先總結下通用的設計方法,而後再圍繞高性能、高可用、高擴展分別給出具體的實踐方案。

3.1 通用的設計方法

通用的設計方法主要是從「縱向」和「橫向」兩個維度出發,俗稱高併發處理的兩板斧:縱向擴展和橫向擴展。

3.1.1 縱向擴展(scale-up)

它的目標是提高單機的處理能力,方案又包括:

一、提高單機的硬件性能:經過增長內存、 CPU核數、存儲容量、或者將磁盤 升級成SSD 等堆硬 件 的 方 式 來 提高 。

二、提高單機的軟件性能:使用緩存減小IO次數,使用併發或者異步的方式增長吞吐量。

3.1.2 橫向擴展(scale-out)

由於單機性能總會存在極限,因此最終還須要引入橫向擴展,經過集羣部署以進一步提升併發處理能力,又包括如下2個方向:

一、作好分層架構:這是橫向擴展的提早,由於高併發系統每每業務複雜,經過分層處理能夠簡化複雜問題,更容易作到橫向擴展。 上面這種圖是互聯網最多見的分層架構,固然真實的高併發系統架構會在此基礎上進一步完善。好比會作動靜分離並引入CDN,反向代理層能夠是LVS+Nginx,Web層能夠是統一的API網關,業務服務層可進一步按垂直業務作微服務化,存儲層能夠是各類異構數據庫。

二、各層進行水平擴展:無狀態水平擴容,有狀態作分片路由。業務集羣一般能設計成無狀態的,而數據庫和緩存每每是有狀態的,所以須要設計分區鍵作好存儲分片,固然也能夠經過主從同步、讀寫分離的方案提高讀性能。

3.2 具體的實踐方案

下面再結合個人我的經驗,針對高性能、高可用、高擴展3個方面,總結下可落地的實踐方案。

3.2.1 高性能的實踐方案

一、集羣部署,經過負載均衡減輕單機壓力。

二、多級緩存,包括靜態數據使用CDN、本地緩存、分佈式緩存等,以及對緩存場景中的熱點key、緩存穿透、緩存併發、數據一致性等問題的處理。

三、分庫分表和索引優化,以及藉助搜索引擎解決複雜查詢問題。

四、考慮NoSQL數據庫的使用,好比HBase、TiDB等,可是團隊必須熟悉這些組件,且有較強的運維能力。

五、異步化,將次要流程經過多線程、MQ、甚至延時任務進行異步處理。

六、限流,須要先考慮業務是否容許限流(好比秒殺場景是容許的),包括前端限流、Nginx接入層的限流、服務端的限流。

七、對流量進行 削峯填谷 ,經過 MQ承接流量。

八、併發處理,經過多線程將串行邏輯並行化。

九、預計算,好比搶紅包場景,能夠提早計算好紅包金額緩存起來,發紅包時直接使用便可。

十、 緩存預熱 ,經過異步 任務 提早 預熱數據到本地緩存或者分佈式緩存中。

十一、減小IO次數,好比數據庫和緩存的批量讀寫、RPC的批量接口支持、或者經過冗餘數據的方式幹掉RPC調用。

十二、減小IO時的數據包大小,包括採用輕量級的通訊協議、合適的數據結構、去掉接口中的多餘字段、減小緩存key的大小、壓縮緩存value等。

1三、程序邏輯優化,好比將大機率阻斷執行流程的判斷邏輯前置、For循環的計算邏輯優化,或者採用更高效的算法。

1四、各類池化技術的使用和池大小的設置,包括HTTP請求池、線程池(考慮CPU密集型仍是IO密集型設置核心參數)、數據庫和Redis鏈接池等。

1五、JVM優化,包括新生代和老年代的大小、GC算法的選擇等,儘量減小GC頻率和耗時。

1六、鎖選擇,讀多寫少的場景用樂觀鎖,或者考慮經過分段鎖的方式減小鎖衝突。

上述方案無外乎從計算和 IO 兩個維度考慮全部可能的優化點,須要有配套的監控系統實時瞭解當前的性能表現,並支撐你進行性能瓶頸分析,而後再遵循二八原則,抓主要矛盾進行優化。

3.2.2 高可用的實踐方案

一、對等節點的故障轉移,Nginx和服務治理框架均支持一個節點失敗後訪問另外一個節點。

二、非對等節點的故障轉移,經過心跳檢測並實施主備切換(好比redis的哨兵模式或者集羣模式、MySQL的主從切換等)。

三、接口層面的超時設置、重試策略和冪等設計。

四、降級處理:保證核心服務,犧牲非核心服務,必要時進行熔斷;或者核心鏈路出問題時,有備選鏈路。

五、限流處理:對超過系統處理能力的請求直接拒絕或者返回錯誤碼。

六、MQ場景的消息可靠性保證,包括producer端的重試機制、broker側的持久化、consumer端的ack機制等。

七、灰度發佈,能支持按機器維度進行小流量部署,觀察系統日誌和業務指標,等運行平穩後再推全量。

八、監控報警:全方位的監控體系,包括最基礎的CPU、內存、磁盤、網絡的監控,以及Web服務器、JVM、數據庫、各種中間件的監控和業務指標的監控。

九、災備演練:相似當前的「混沌工程」,對系統進行一些破壞性手段,觀察局部故障是否會引發可用性問題。

高可用的方案主要從冗餘、取捨、系統運維3個方向考慮,同時須要有配套的值班機制和故障處理流程,當出現線上問題時,可及時跟進處理。

3.2.3 高擴展的實踐方案

一、合理的分層架構:好比上面談到的互聯網最多見的分層架構,另外還能進一步按照數據訪問層、業務邏輯層對微服務作更細粒度的分層(可是須要評估性能,會存在網絡多一跳的狀況)。

二、存儲層的拆分:按照業務維度作垂直拆分、按照數據特徵維度進一步作水平拆分(分庫分表)。

三、業務層的拆分:最多見的是按照業務維度拆(好比電商場景的商品服務、訂單服務等),也能夠按照核心接口和非核心接口拆,還能夠按照請求源拆(好比To C和To B,APP和H5 )。

寫在最後

高併發確實是一個複雜且系統性的問題,因爲篇幅有限,諸如分佈式Trace、全鏈路壓測、柔性事務都是要考慮的技術點。另外,若是業務場景不一樣,高併發的落地方案也會存在差別,可是整體的設計思路和可借鑑的方案基本相似。

高併發設計一樣要秉承架構設計的3個原則:簡單、合適和演進。"過早的優化是萬惡之源",不能脫離業務的實際狀況,更不要過分設計,合適的方案就是最完美的。

但願這篇文章能帶給你關於高併發更全面的認識,若是你也有可借鑑的經驗和深刻的思考,歡迎評論區留言討論。最後也不要忘記點贊+關注啊,後面也會更新更多的Java相關的知識的!

相關文章
相關標籤/搜索