高並發系統設計(三):【系統設計目標①】如何提升系統性能?


高並發系統設計的三大目標:高性能、高可用、可擴展

高性能:反應了系統的使用體驗,想象一下,同樣承擔每秒一萬次請求的兩個系統,一個響應時間是毫秒級,一個響應時間在秒級別,它們帶給用戶的體驗肯定是不同的。

高可用:則表示系統可以正常服務用戶的時間。舉例,還是兩個承擔每秒一萬次的系統,一個可以做到全年不停機、無故障,一個隔三差五宕機維護,哪個算是高可用?

可擴展:流量分為平時流量和峰值流量兩種,峰值流量可能會是平時流量的幾倍甚至幾十倍,在應對峰值流量的時候,我們通常需要在架構和方案上做更多的准備。而易於擴展的系統能在短時間內迅速完成擴容,更加平穩地承擔峰值流量。

性能優化原則

“天下武功,唯快不破”。性能是系統設計成功與否的關鍵,實現高性能也是對程序員個人能力的挑戰。不過在了解實現高性能的方法之前,我們先明確一下性能優化的原則。

  1. 首先,性能優化一定不能盲目,一定是問題導向的。脫離了問題,盲目地提早優化會增加系統的復雜度,浪費開發人員的時間,也因為某些優化可能會對業務上有些折中的考慮,所以也會損傷業務。
  2. 其次,性能優化也遵循“八二原則”,即你可以用20%的精力解決80%的性能問題。所以我們在優化過程中一定要抓住主要矛盾,優先優化主要的性能瓶頸點。
  3. 再次,性能優化也要有數據支撐。在優化過程中,你要時刻了解你的優化讓響應時間減少了多少,提升了多少的吞吐量。
  4. 最后,性能優化的過程是持續的。高並發的系統通常是業務邏輯相對復雜的系統,那么在這類系統中出現的性能問題通常也會有多方面的原因。因此,我們在做性能優化的時候要明確目標,比方說,支撐每秒1萬次請求的吞吐量下響應時間在10ms,那么我們就需要持續不斷地尋找性能瓶頸,制定優化方案,直到達到目標為止。

性能的度量指標

性能優化的第三點原則中提到,對於性能我們需要有度量的標准,有了數據才能明確目前存在的性能問題,也能夠用數據來評估性能優化的效果。所以明確性能的度量指標十分重要。

一般來說,度量性能的指標是系統接口的響應時間,但是單次的響應時間是沒有意義的,你需要知道一段時間的性能情況是什么樣的。所以,我們需要收集這段時間的響應時間數據,然后依據一些統計方法計算出特征值,這些特征值就能夠代表這段時間的性能情況。我們常見的特征值有以下幾類。

  • 平均值

顧名思義,平均值是把這段時間所有請求的響應時間數據相加,再除以總請求數。平均值可以在一定程度上反應這段時間的性能,但它敏感度比較差,如果這段時間有少量慢請求時,在平均值上並不能如實的反應。

舉個例子,假設我們在30s內有10000次請求,每次請求的響應時間都是1ms,那么這段時間響應時間平均值也是1ms。這時,當其中100次請求的響應時間變成了100ms,那么整體的響應時間是(100 * 100 + 9900 * 1) / 10000 = 1.99ms。你看,雖然從平均值上來看僅僅增加了不到1ms,但是實際情況是有1%的請求(100/10000)的響應時間已經增加了100倍。所以,平均值對於度量性能來說只能作為一個參考。

  • 最大值

這個更好理解,就是這段時間內所有請求響應時間最長的值,但它的問題又在於過於敏感了。

還拿上面的例子來說,如果10000次請求中只有一次請求的響應時間達到100ms,那么這段時間請求的響應耗時的最大值就是100ms,性能損耗為原先的百分之一,這種說法明顯是不准確的。

  • 分位值

分位值有很多種,比如90分位、95分位、75分位。以90分位為例,我們把這段時間請求的響應時間從小到大排序,假如一共有100個請求,那么排在第90位的響應時間就是90分位值。分位值排除了偶發極慢請求對於數據的影響,能夠很好地反應這段時間的性能情況,分位值越大,對於慢請求的影響就越敏感。

 

分位值是最適合作為時間段內,響應時間統計值來使用的,在實際工作中也應用最多。除此之外,平均值也可以作為一個參考值來使用。

我們通常使用吞吐量或者同時在線用戶數來度量並發和流量,使用吞吐量的情況會更多一些。但是你要知道,這兩個指標是呈倒數關系的。

這很好理解,響應時間1s時,吞吐量是每秒1次,響應時間縮短到10ms,那么吞吐量就上升到每秒100次。所以,一般我們度量性能時都會同時兼顧吞吐量和響應時間,比如我們設立性能優化的目標時通常會這樣表述:在每秒1萬次的請求量下,響應時間99分位值在10ms以下。

那么,響應時間究竟控制在多長時間比較合適呢?這個不能一概而論。

從用戶使用體驗的角度來看,200ms是第一個分界點:接口的響應時間在200ms之內,用戶是感覺不到延遲的,就像是瞬時發生的一樣。而1s是另外一個分界點:接口的響應時間在1s之內時,雖然用戶可以感受到一些延遲,但卻是可以接受的,超過1s之后用戶就會有明顯等待的感覺,等待時間越長,用戶的使用體驗就越差。所以,健康系統的99分位值的響應時間通常需要控制在200ms之內,而不超過1s的請求占比要在99.99%以上。

現在你了解了性能的度量指標,那我們再來看一看,隨着並發的增長我們實現高性能的思路是怎樣的。

高並發下的性能優化

假如說,你現在有一個系統,這個系統中處理核心只有一個,執行的任務的響應時間都在10ms,它的吞吐量是在每秒100次。那么我們如何來優化性能從而提高系統的並發能力呢?主要有兩種思路:一種是提高系統的處理核心數,另一種是減少單次任務的響應時間。

1.提高系統的處理核心數

提高系統的處理核心數就是增加系統的並行處理能力,這個思路是優化性能最簡單的途徑。拿上一個例子來說,你可以把系統的處理核心數增加為兩個,並且增加一個進程,讓這兩個進程跑在不同的核心上。這樣從理論上,你系統的吞吐量可以增加一倍。當然了,在這種情況下,吞吐量和響應時間就不是倒數關系了,而是:吞吐量=並發進程數/響應時間。

是不是無限制地增加處理核心數就能無限制地提升性能,從而提升系統處理高並發的能力呢?但隨着並發進程數的增加,並行的任務對於系統資源的爭搶也會愈發嚴重。在某一個臨界點上繼續增加並發進程數,反而會造成系統性能的下降,這就是性能測試中的拐點模型。

 

 

所以我們在評估系統性能時通常需要做壓力測試,目的就是找到系統的“拐點”,從而知道系統的承載能力,也便於找到系統的瓶頸,持續優化系統性能。

 

2.減少單次任務響應時間

想要減少任務的響應時間,首先要看你的系統是CPU密集型還是IO密集型的,因為不同類型的系統性能優化方式不盡相同。

CPU密集型系統中,需要處理大量的CPU運算,那么選用更高效的算法或者減少運算次數就是這類系統重要的優化手段。比方說,如果系統的主要任務是計算Hash值,那么這時選用更高性能的Hash算法就可以大大提升系統的性能。發現這類問題的主要方式,是通過一些Profile工具來找到消耗CPU時間最多的方法或者模塊,比如Linux的perf、eBPF等。

IO密集型系統指的是系統的大部分操作是在等待IO完成,這里IO指的是磁盤IO和網絡IO。我們熟知的系統大部分都屬於IO密集型,比如數據庫系統、緩存系統、Web系統。這類系統的性能瓶頸可能出在系統內部,也可能是依賴的其他系統,而發現這類性能瓶頸的手段主要有兩類。

第一類是采用工具,Linux的工具集很豐富,完全可以滿足你的優化需要,比如網絡協議棧、網卡、磁盤、文件系統、內存,等等。這些工具的用法很多,你可以在排查問題的過程中逐漸積累。除此之外呢,一些開發語言還有針對語言特性的分析工具,比如說Java語言就有其專屬的內存分析工具。

另外一類手段就是可以通過監控來發現性能問題。在監控中我們可以對任務的每一個步驟做分時的統計,從而找到任務的哪一步消耗了更多的時間。

那么找到了系統的瓶頸點,我們要如何優化呢?優化方案會隨着問題的不同而不同。比方說,如果是數據庫訪問慢,那么就要看是不是有鎖表的情況、是不是有全表掃描、索引加得是否合適、是否有JOIN操作、需不需要加緩存,等等;如果是網絡的問題,就要看網絡的參數是否有優化的空間,抓包來看是否有大量的超時重傳,網卡是否有大量丟包等。

有時候你在遇到性能問題的時候會束手無策,注意點:

  • 數據優先,你做一個新的系統在上線之前一定要把性能監控系統做好;
  • 掌握一些性能優化工具和方法,這就需要在工作中不斷的積累;
  • 計算機基礎知識很重要,比如說網絡知識、操作系統知識等等,掌握了基礎知識才能讓你在優化過程中抓住性能問題的關鍵。

參考

1.業務價值->承載高並發->性能優化。一切的前提是業務價值需要。如果沒有足夠的價值,那么可讀性才是第一,性能在需要的地方是no.1,但不需要的地方可能就是倒數第一稞。當下技術框架出來的軟件差不到哪去,沒有這種及時響應訴求的地方,削峰下慢慢跑就是了。(工作需要,常在缺少價值的地方着手性能優化,讓我對這種就為個數字的操作很反感。要知道,異步,並發編程,邏輯緩存,算法真的會加劇系統的復雜度,得不償失。如果沒那個價值,簡單才是王道)

2.提高並發度。要么加硬件,要么降低服務響應時間。做為開發,我們的目光更聚焦在降低響應時間這塊。
1.采用非阻塞的rpc調用(高效的遠端請求模式,采用容器的覆蓋網絡我認為也算)
2.將計算密集和io密集的的邏輯分割開,單獨線程池,調整線程比例壓榨單機性能(或者說找拐點)。
3.做緩存,io耗時的緩存和計算耗時的緩存(多級緩存,數據壓縮降低帶寬)。
4.采用享元模式,用好對象池和本地線程空間,盡量減少對象創建與銷毀的開銷,提高復用。
5.業務拆分,像狀態變化后的外部系統通知,業務監控,es或solr等副本數據同步等操作,無需在主流程中做的事都拆掉。走canal監聽表數據變化,推mq保最終一致的方式從業務項目完全解偶出來。
6.fork_join,分而治之的處理大任務。並發編程,采用多線程並行的方式處理業務。(規避偽共享,減小鎖力度,采用合適的鎖)。
7.數據庫配置優化,查詢優化。(存儲優化比較頭疼,畢竟不按業務拆單點跑不掉,單點性能就要命。基本只能內存庫先行,后台同步數據做持久。然后內存庫多副本,自修復,保留一系列自修復失敗的修復手段)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM