「時間」都去哪兒了?性能調優分析方法與案例詳解

原創 侯龍數據庫

一個好的軟件,功能和性能都相當重要,這固然離不開產品同窗的開光腦瓜、研發同窗的靈巧小手,也離不開測試同窗的金晶火眼。說到測試,可能你們就會想到頁面點點點呀,接口驗證呀,業務聯調呀等等,其實還有一個很重要的環節,那就是性能測試。緩存

那麼,什麼是性能測試?如何衡量系統性能?系統響應時間是怎麼計算的?如何進行性能調優?帶着這些問題,我們今天就來簡單地聊一聊性能調優那些事兒。服務器

1.性能測試是什麼

性能測試就是經過特定的方式,對被測系統按照必定的測試策略施加壓力,獲取該系統的響應時間、吞吐量、資源利用率等性能指標,來檢驗系統上線後可否知足用戶需求的過程,主要包括測試需求/目的、測試環境/工具、測試方案、測試執行、測試結果與分析。網絡

2.衡量系統的四大指標

衡量一個系統的性能,主要有如下四大指標:多線程

響應時間併發

指應用執行一個操做所需的時間,包括從發出請求開始到最後收到響應所須要的時間。響應時間是系統最重要的性能指標,直觀的反映了系統的快慢。運維

吞吐量異步

指單位時間內系統處理的請求數,體現系統的總體處理能力。TPS(Transaction per second)是吞吐量的一個經常使用量化指標,此外還有HPS(Hits per second)、QPS(Query per second)等。模塊化

資源利用率工具

指應用服務器、數據庫服務器及被測系統包含的中間件服務器的CPU、內存、磁盤、網絡等系統資源的使用狀況。

併發數

指的是同時提交請求的用戶數目。這四個指標之間的關係如圖1。

圖1

吞吐量= 併發數/平均響應時間吞吐量= 併發數/平均響應時間。

從圖1咱們能夠看到:

  • 當系統壓力較小時,響應時間幾乎無變化,吞吐量和系統資源隨併發數的增長呈線性增加趨勢;

  • 當系統壓力較大時,隨着併發數增長,響應時間也逐漸增長,系統資源達到極限,吞吐量再也不增加;

  • 繼續增長併發數,響應時間快速增加,系統資源仍然在極限狀態,吞吐量迅速降低。

通常狀況下,咱們但願系統可以支持更大的併發和更大的吞吐量。可是,從上面的分析咱們能夠看到,併發數的增加不會一直帶來吞吐量的增加,由於系統資源使用率達到極限後,響應時間將會是決定吞吐量的更大因素,那麼,時間都去哪兒了呢?

3.時間都去哪兒了

圖2

一個請求從發出到接收響應,如圖2所示。大體流程以下:

  1. 客戶端發送請求報文。客戶端發送請求報文,通過網絡傳輸後到達服務端;

  2. 服務端處理。服務端接收到請求報文後,進行業務邏輯處理和必要的數據讀寫操做;

  3. 服務端返回響應報文。服務端處理完後,將響應報文發送到客戶端。

咱們一般說的響應時間是第1步、第2步、第3步消耗的總時間。第1步主要是客戶端請求耗時和網絡耗時;第2步主要是業務邏輯、數據讀寫和網絡耗時;第3步主要是客戶端渲染和網絡耗時。

第一、二、3步每一步都有可能存在性能問題,致使響應時間變長。第1步中如客戶端主機配置低,反應慢等,第二步中如業務線程阻塞、數據庫查詢慢;第3步中如網絡傳輸延遲。根據各類問題的類型,咱們又能夠把問題歸爲硬件問題、網絡問題、代碼問題、中間件問題等。不一樣問題也有不一樣的調優方法,下面咱們簡單聊一聊性能調優。

4.抓住時間的小偷-性能調優

經常使用的調優方法有:

  • 空間換時間。如數據緩存,提早從磁盤上讀取數據緩存到內存中,CPU請求數據直接從內存中獲取,從而達到更高的效率;

  • 時間換空間,如上傳大附件,將數據分批次處理,用更少的空間完成任務處理;

  • 分而治之,把任務切分,分開執行,也方便並行執行來提升效率;

  • 異步處理,如互聯網應用最多見的MQ消息隊列,將業務鏈路上比較耗時的業務拆分出來,經過異步處理減小阻塞影響;

  • 並行,多個進程或者線程同時處理業務,縮短業務處理時間;

  • 離用戶更近一點,如CDN技術,把用戶請求的靜態資源放在離用戶更近的地方;

  • 一切可擴展,業務模塊化、服務化(同時無狀態化)、良好的水平擴展能力。

下面咱們舉幾個案例進行說明。

案例1

問題描述: 壓測某接口時,隨着壓測執行,響應時間愈來愈長。

問題分析:

  1. 打印線程堆棧,對比線程堆棧信息,發現線程堆棧中FailoverEvent的線程數愈來愈多,最終內存溢出;

  2. 查看代碼發現,程序中未判斷FailoverEvent線程隊列是否已經存在,致使FailoverEvent線程隊列重複建立。

解決方案:建立FailoverEvent線程隊列前,判斷其是否存在,若是不存在則建立,若是存在,則使用現有對象。

優化結果:內存溢出問題解決,響應時間正常。

調優建議

  1. 儘早釋放無用對象的引用;

  2. 程序進行字符串處理時,儘可能避免使用String,而應使用StringBuffer;

  3. 儘可能少用靜態變量;

  4. 避免集中建立對象尤爲是大對象;

  5. 儘可能運用對象池技術以提升系統性能;

  6. 不要在常常調用的方法中建立對象,尤爲是忌諱在循環中建立對象。

案例2

問題描述: 某批量處理接口,無積壓的狀況下,10000訂單,4500sku種類處理時間耗時433秒。

問題分析: 接口中採用單線程方式調用下游服務,查詢次數=sku種類數/11,4500sku種類約410次,且每次調用耗時約519ms。

解決方案:調用下游服務改用多線程方式。

優化結果:TP99由212秒降低到33秒,TPS由87筆/秒提高到127筆/秒。

調優建議:本案例採用多線程下降了響應時間,但並非說多線程必定比單線程快,由於幹活的是CPU,不是線程。咱們能夠經過確認系統有無磁盤/網絡IO來進行選擇,有,多線程;無,單線程。而且採用多線程時,必定要使用線程池。

案例3

問題描述: 數據查詢接口,TP99=727ms,加大併發,吞吐量沒法提高,應用服務器CPU使用率始終不到40%。

問題分析: 經過調用鏈分析咱們發現,一次請求,調用了11次selectList方法,致使接口總耗時飆升。

解決方案:去掉冗餘調用,一次請求調用一次selectList方法。

優化結果:TP99由727ms降低到19ms,提高38倍,TPS由17.5筆/秒提高至163.4筆/秒,提高9倍。

調優建議

  1. 設計先於代碼;

  2. 基本原則:把數據庫操做放在循環以外;

  3. 若是是查詢,使用IN查詢替換for循環(空間換時間);

  4. 若是是新增,使用批量插入。

案例4

問題描述: 某接口提交數據庫操做,更新數據時產生死鎖。

問題分析: 產生死鎖的事務如表1:

解決方案 :將事務1拆分,先查詢,而後根據查詢的結果批量刪除。

優化結果 :死鎖問題解決。

調優建議

  1. 避免大事務;

  2. 按同一順序訪問數據對;

  3. 避免編寫包含用戶交互的事務;

  4. 酌情使用低隔離級別,如RC;

  5. 爲表添加合理的索引,若是不走索引將會爲表的每一行記錄加鎖,死鎖的機率就會大大增大;

  6. 避免在同一時間點運行多個對同一表進行讀寫的腳本,特別注意加鎖且操做數據量比較大的語句;

  7. 設置鎖等待超時參數,innodb_lock_wait_timeout。

5.總結

響應時間一般只是問題的表現,根本緣由在於各類資源的利用是否合理,這裏的資源是指廣義的資源,包括硬件/軟件資源、系統/線程/數據等不一樣級別的資源。調優自己,就是對各類資源進行更合理的配置。調優的目的一般也是爲了知足業務需求,所以咱們沒必要追求過早和過分優化,而且咱們應該認識到,性能調優不可能一勞永逸,隨着業務的迭代,總會有新的問題出現,所以咱們應該具有打持久戰的共識和能力。

推薦閱讀

歡迎點擊【京東科技】,瞭解開發者社區

更多精彩技術實踐與獨家乾貨解析

歡迎關注【京東科技開發者】公衆號

相關文章
相關標籤/搜索