天天看點

java性能優化實戰:談一談服務性能衡量名額有哪些?

作者:一個即将退役的碼農

初衷:

随着網際網路的發展,高可靠、高并發以及降本增效,已成為各大公司面臨的現實挑戰,性能優化需求愈發迫切,大到分布式系統,小到代碼塊的算法優化,都已經成為你日常工作中必須要面對的事情。對于開發者而言,性能優化也從加分項變為一個熱門技能,缺乏相關知識将很難在面試或工作中脫穎而出。

該篇主要從理論分析入手來介紹性能優化的衡量名額,及其理論方法和注意點。

名額是我們衡量很多事物,以及做出行為決策的重要參考。例如在生活中,當你打算買汽車時,會關注很多名額,比如動力性、燃油經濟性、制動性、操縱穩定性、平順性、通過性、排放與噪聲等,而這些名額也都有相關的測試和參數,同時也會對這些名額進行一一參考。

這個道理大家都懂,但一旦到了性能優化上,卻往往因為缺乏理論依據而選擇了錯誤的優化方向,陷入了盲猜的窘境。在衡量一項優化是否能達到目的之時,不能僅靠感覺,它同樣有一系列的名額來衡量你的改進。如果在改動之後,性能不升反降,那就不能叫性能優化了。

所謂性能,就是使用有限的資源在有限的時間内完成工作。最主要的衡量因素就是時間,是以很多衡量名額,都可以把時間作為橫軸。

加載緩慢的網站,會受到搜尋排名算法的懲罰,進而導緻網站排名下降。 是以加載的快慢是性能優化是否合理的一個非常直覺的判斷因素,但性能名額不僅僅包括單次請求的速度,它還包含更多因素。

接下來看一下,都有哪些衡量名額能夠幫我們進行決策。

衡量名額有哪些?

java性能優化實戰:談一談服務性能衡量名額有哪些?

1. 吞吐量和響應速度

分布式的高并發應用并不能把單次請求作為判斷依據,它往往是一個統計結果。其中最常用的衡量名額就是吞吐量和響應速度,而這兩者也是考慮性能時非常重要的概念。要了解這兩個名額的意義,我們可以類比為交通環境中的十字路口。

在交通非常繁忙的情況下,十字路口是典型的瓶頸點,當紅綠燈放行時間非常長時,後面往往會排起長隊。

從我們開車開始排隊,到車經過紅綠燈,這個過程所花費的時間,就是響應時間。

當然,我們可以适當地調低紅綠燈的間隔時間,這樣對于某些車輛來說,通過時間可能會短一些。但是,如果信号燈頻繁切換,反而會導緻機關時間内通過的車輛減少,換一個角度,我們也可以認為這個十字路口的車輛吞吐量減少了。

java性能優化實戰:談一談服務性能衡量名額有哪些?

像我們平常開發中經常提到的,QPS 代表每秒查詢的數量,TPS 代表每秒事務的數量,HPS 代表每秒的 HTTP 請求數量等,這都是常用的與吞吐量相關的量化名額。

在性能優化的時候,我們要搞清楚優化的目标,到底是吞吐量還是響應速度。 有些時候,雖然響應速度比較慢,但整個吞吐量卻非常高,比如一些資料庫的批量操作、一些緩沖區的合并等。雖然資訊的延遲增加了,但如果我們的目标就是吞吐量,那麼這顯然也可以算是比較大的性能提升。

一般情況下,我們認為:

  • 響應速度是串行執行的優化,通過優化執行步驟解決問題;
  • 吞吐量是并行執行的優化,通過合理利用計算資源達到目标。

我們平常的優化主要側重于響應速度,因為一旦響應速度提升了,那麼整個吞吐量自然也會跟着提升。

但對于高并發的網際網路應用來說,響應速度和吞吐量兩者都需要。這些應用都會标榜為高吞吐、高并發的場景,使用者對系統的延遲忍耐度很差,我們需要使用有限的硬體資源,從中找到一個平衡點。

2. 響應時間衡量

既然響應時間這麼重要,我們就着重看一下響應時間的衡量方法。

(1)平均響應時間

我們最常用的名額,即平均響應時間(AVG),該名額能夠展現服務接口的平均處理能力。它的本質是把所有的請求耗時加起來,然後除以請求的次數。舉個最簡單的例子,有 10 個請求,其中有 2 個 1ms、3 個 5ms、5 個 10ms,那麼它的平均耗時就是(21+35+5*10)/10=6.7ms。

除非服務在一段時間内出現了嚴重的問題,否則平均響應時間都會比較平緩。因為高并發應用請求量都特别大,是以長尾請求的影響會被很快平均,導緻很多使用者的請求變慢,但這不能展現在平均耗時名額中。

為了解決這個問題,另外一個比較常用的名額,就是百分位數(Percentile)。

(2)百分位數

java性能優化實戰:談一談服務性能衡量名額有哪些?

這個也比較好了解。我們圈定一個時間範圍,把每次請求的耗時加入一個清單中,然後按照從小到大的順序将這些時間進行排序。這樣,我們取出特定百分位的耗時,這個數字就是 TP 值。可以看到,TP 值(Top Percentile)和中位數、平均數等是類似的,都是一個統計學裡的術語。

它的意義是,超過 N% 的請求都在 X 時間内傳回。比如 TP90 = 50ms,意思是超過 90th 的請求,都在 50ms 内傳回。

這個名額也是非常重要的,它能夠反映出應用接口的整體響應情況。比如,某段時間若發生了長時間的 GC,那它的某個時間段之上的名額就會産生嚴重的抖動,但一些低百分位的數值卻很少有變化。

我們一般分為 TP50、TP90、TP95、TP99、TP99.9 等多個段,對高百分位的值要求越高,對系統響應能力的穩定性要求越高。

在這些高穩定性系統中,目标就是要幹掉嚴重影響系統的長尾請求。這部分接口性能資料的收集,我們會采用更加詳細的日志記錄方式,而不僅僅靠名額。比如,我們将某個接口,耗時超過 1s 的入參及執行步驟,詳細地輸出在日志系統中。

3. 并發量

并發量是指系統同時能處理的請求數量,這個名額反映了系統的負載能力。

在高并發應用中,僅僅高吞吐是不夠的,它還必須同時能為多個使用者提供服務。并發高時,會導緻很嚴重的共享資源争用問題,我們需要減少資源沖突,以及長時間占用資源的行為。

針對響應時間進行設計,一般來說是萬能的。因為響應時間減少,同一時間能夠處理的請求必然會增加。值得注意的是,即使是一個秒殺系統,經過層層過濾處理,最終到達某個節點的并發數,大概也就五六十左右。我們在平常的設計中,除非并發量特别低,否則都不需要太過度關注這個名額。

4. 秒開率

在移動網際網路時代,尤其對于 App 中的頁面,秒開是一種極佳的使用者體驗。如果能在 1 秒内加載完成頁面,那使用者可以獲得流暢的體驗,并且不會産生更多的焦慮感。

通常而言,可以根據業務情況設定不同的頁面打開标準,比如低于 1 秒内的資料占比是秒開率。業界優秀的公司,比如手淘,其頁面的秒開率基本可達到 80% 以上。

5. 正确性

說一個比較有意思的事情。我們有個技術團隊,在進行測試的時候,發現接口響應非常流暢,把并發數增加到 20 以後,應用接口響應依舊非常迅速。

但等應用真正上線時,卻發生了重大事故,這是因為接口傳回的都是無法使用的資料。

其問題原因也比較好定位,就是項目中使用了熔斷。在壓測的時候,接口直接超出服務能力,觸發熔斷了,但是壓測并沒有對接口響應的正确性做判斷,造成了非常低級的錯誤。

是以在進行性能評估的時候,不要忘記正确性這一關鍵要素。

有哪些理論方法?

性能優化有很多理論方法,比如木桶理論、基礎測試、Amdahl 定律等。下面我們簡單地講解一下最常用的兩個理論。

1. 木桶理論

一隻木桶若想要裝最多的水,則需要每塊木闆都一樣長而且沒有破損才行。如果有一塊木闆不滿足條件,那麼這隻桶就無法裝最多的水。

能夠裝多少水,取決于最短的那塊木闆,而不是最長的那一塊。

木桶效應在解釋系統性能上,也非常适合。組成系統的元件,在速度上是良莠不齊的。系統的整體性能,就取決于系統中最慢的元件。

比如,在資料庫應用中,制約性能最嚴重的是落盤的 I/O 問題,也就是說,硬碟是這個場景下的短闆,我們首要的任務就是補齊這個短闆。

2. 基準測試、預熱

基準測試(Benchmark)并不是簡單的性能測試,是用來測試某個程式的最佳性能。

應用接口往往在剛啟動後都有短暫的逾時。在測試之前,我們需要對應用進行預熱,消除 JIT 編譯器等因素的影響。而在 Java 裡就有一個元件,即 JMH,就可以消除這些差異。

注意點

java性能優化實戰:談一談服務性能衡量名額有哪些?

1. 依據數字而不是猜想

有些同學對程式設計有很好的感覺,能夠靠猜測列出系統的瓶頸點,這種情況固然存在,但卻非常不可取。複雜的系統往往有多個影響因素,我們應将性能分析放在第一位,把性能優化放在次要位置,直覺隻是我們的輔助,但不能作為下結論的工具。

進行性能優化時,我們一般會把分析後的結果排一個優先級(根據難度和影響程度),從大處着手,首先擊破影響最大的點,然後将其他影響因素逐一擊破。

有些優化會引入新的性能問題,有時候這些新問題會引起更嚴重的性能下降,你需要評估這個連鎖反應,確定這種優化确實需要,同時需要使用數字去衡量這個過程,而不是靠感覺猜想。

2. 個體資料不足信

你是否有這樣的經曆:某個知名網站的通路速度真慢,光加載就花費了 x 秒。其實,僅憑一個人的一次請求,就下了“慢”這個結論,是不合适的,而在我們進行性能評估的時候,也往往會陷入這樣的誤區。

這是因為個體請求的小批量資料,可參考價值并不是非常大。響應時間可能因使用者的資料而異,也可能取決于裝置和網絡條件。

合理的做法,是從統計資料中找到一些規律,比如上面所提到的平均響應時間、TP 值等,甚至是響應時間分布的直方圖,這些都能夠幫我們評估性能品質。

3. 不要過早優化和過度優化

雖然性能優化有這麼多好處,但并不代表我們要把每個地方都做到極緻,性能優化也是要有限度的。程式要運作地正确,要比程式運作得更快還要困難。

計算機科學的鼻祖"Donald Knuth" 曾說:“過早的優化是萬惡之源”,就是這個道理。

如果一項改進并不能産生明顯的價值,那我們為什麼還要花大力氣耗在上面呢?比如,某個應用已經滿足了使用者的吞吐量需求和響應需求,但有的同學熱衷于 JVM 的調優,依然花很大力氣在參數測試上,這種優化就屬于過度優化。

時間要花在刀刃上,我們需要找到最迫切需要解決的性能點,然後将其擊破。比如,一個系統主要是慢在了資料庫查詢上,結果你卻花了很大的精力去優化 Java 編碼規範,這就是偏離目标的典型情況。

一般地,性能優化後的代碼,由于太過于追求執行速度,讀起來都比較晦澀,在結構上也會有很多讓步。很顯然,過早優化會讓這種難以維護的特性過早介入到你的項目中,等代碼重構的時候,就會花更大的力氣去解決它。

正确的做法是,項目開發和性能優化,應該作為兩個獨立的步驟進行,要做性能優化,要等到整個項目的架構和功能大體進入穩定狀态時再進行。

4. 保持良好的編碼習慣

我們上面提到,不要過早地優化和過度優化,但并不代表大家在編碼時就不考慮這些問題。

比如,保持好的編碼規範,就可以非常友善地進行代碼重構;使用合适的設計模式,合理的劃分子產品,就可以針對性能問題和結構問題進行聚焦、優化。

在追求高性能、高品質編碼的過程中,一些好的習慣都會積累下來,形成人生道路上優秀的修養和品質,這對我們是大有裨益的。

小結

我們簡單地了解了衡量性能的一些名額,比如常見的吞吐量和響應速度,還探讨了一些其他的影響因素,比如并發量、秒開率、容錯率等。

同時,我們也談到了木桶理論和基準測試等兩種過程方法,并對性能測試中的一些誤區和注意點進行了介紹,現在你應該對如何描述性能有了更好的了解。像一些專業的性能測試軟體,如 JMeter、LoadRunner 等,就是在這些基礎性能名額上進行的擴充。我們在平常的工作中,也應該盡量使用專業術語,這樣才能對系統性能進行正确評估。

了解了優化名額後,有了行動導向,那接下來該從哪些方面入手呢? Java 性能優化是否有可以遵循的規律呢?

後續系列敬請期待!!!

#頭條創作挑戰賽#