當前位置:
首頁 > 知識 > Spring/Hibernate 應用性能調優

Spring/Hibernate 應用性能調優

來源:ImportNew - 陳曉舜


對大部分典型的Spring/Hibernate企業應用來說,應用的性能大部分由持久層的性能決定。


這篇文章會重溫一下怎麼去確認我們的應用是否是」資料庫依賴(data-bound)」(譯者註:即非常依賴資料庫,大量時間花在資料庫操作上),然後會大概過一下7個常用的提升應用性能的速效方案。


怎麼確定應用是否是「資料庫依賴」


確認一個應用是是否是資料庫依賴,首先通過在一些開發環境中做基本的運行,可以使用VisualVM來進行監控。VisualVM是一個和JDK一起發布的Java性能調優器,可以通過命令行jvisualvm運行。

執行Visual VM後,嘗試下面的步驟:


雙擊你正在運行的應用


選擇抽樣器(Sampler)


點擊設置複選框


選擇只調優包,並且限定如下的包類型:


你的應用程序包


org.hibernate.*


org.springframework.*


你的資料庫jar包名,如oracle.*


點擊抽樣(Sample) CPU

CPU抽樣一個典型的「資料庫依賴」應用將會得到類似下面的結果:

Spring/Hibernate 應用性能調優



我們可以看到Java客戶端進程花費了56%的時間在等待資料庫從網路中返回結果。


這是一個很好的標誌,表示正是資料庫查詢造成了應用的緩慢。Hibernate反射調用佔了32.7%是正常,而且我們對此也無能為力。


性能調優第一步 —— 得到基準運行值(baseline run)


性能調優的第一步是為程序定義一個基準運行值。我們需要一系列可以使程序運行的有效輸入數據,它必須跟在生產環境運行類似。


最主要的區別是基準運行需要在更短的時間內運行完成,比較理想的指導值是執行時間為5-10分鐘。


什麼是好的基準(baseline)?


一個好的基準需要有下面的特性:

保證功能正確


輸入數據在可變性上和生產環境類似


在短時間內可以完成


在基準運行中做的優化可以直接影響到完整運行


取一個好的基準可以解決一大半的問題。


什麼是不好的基準


例如,在一個批處理運行的執行電話數據記錄的電信系統中,取得前10000條記錄會是一個錯誤的做法。


原因是:前10000條有可能大部分是語音電話,但未知的性能問題卻是在處理簡訊通道(SMS traffic)。在一個大批量執行的過程中獲取前面的一些記錄不是一個好的基準,有可能會得到錯誤的結論。


收集SQL日誌和查詢時間


SQL查詢和執行時間可以使用如log4jdbc來進行收集。可以看這篇博客關於如何使用log4jdbc來收集SQL查詢 —— 通過log4jdbc來改進Spring/Hibernate的SQL日誌(http://blog.jhades.org/logging-the-actualreal-sql-queries-of-a-springhibernate-application/).

查詢執行時間是在Java客戶端進行計算的,它包含了到資料庫的網路往返請求耗時。SQL查詢日誌看起來就像這樣:


16 avr. 2014 11:13:48 | SQL_QUERY /* insert your.package.YourEntity */ insert into YOUR_TABLE (...) values (...)


Prepared statements自己也是很好的信息源——它允許識別經常執行的查詢類型。根據這篇博客,可以很簡單地記錄——Hibernate在哪裡,為什麼做這個SQL查詢(http://blog.jhades.org/how-to-find-out-why-hibernate-is-doing-a-certain-sql-query/)。


SQL日誌可以得到什麼數據


SQL日誌可以回答這些問題:


最慢的查詢是什麼?


最頻繁的查詢是什麼?


生成主鍵花了多少時間?


是否有數據可以通過緩存受益?


怎麼轉換SQL日誌

也許對於大日誌文件最可行的方案就是使用命令行工具。這個方法的優點是比較靈活。


只需要耗費點時間寫一小段腳本或命令,我們可以抽取大部分任何需要的數據。任何命令行都可以按你喜歡的方式去使用。


如果你使用Unix命令行,bash會是一個很好的選擇。Bash也可以在Windows工作站中使用,使用例如Cygwin或Git這些包含bash命令行的工具。


常用的速效方案


下面的速效方案可以識別Spring/Hibnerate應用中的常見性能問題和對應的解決方案。


速效方案1 —— 減少主鍵提前生成


在一些插入密集(intert-intensive)的處理中,主鍵生成策略的選擇有很大的影響。一個常見的生成ID的方法是使用資料庫的序列(sequences),通常每個表一個,以避免插入不同表時的衝突。


問題在於,如果插入50條記錄,我們希望可以避免50次通過資料庫獲取50個ID的網路往返,而不使Java進程在大部分時間內等待。


Hibernate通常是怎麼處理這個的?


Hibernate提供了新優化的ID生成器可以避免這個問題。對於sequences,會默認使用一個HiLo id生成器。HiLo序列生成器的工作過程如下:

調用一次sequence返回1000(最大值)


如下計算50個ID:


所以從第一次sequence調用時,就已經生成了50個key了,減少了大量的網路往返耗時。


這些新優化的主鍵生成器在Hibernate4中是默認開啟的,在需要時,可以通過設置hibernate.id.new_generator_mappings為false進行關閉。


為什麼主鍵生成仍然是個問題?


問題在於,如果你定義主鍵生成策略為AUTO,優化生成器仍然是關閉的,你的應用仍然還是會進行很大數量的sequence調用。


為了保證新的優化生成器被啟用,確保使用SEQUENCE策略而不是AUTO:


@Id


@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "your_key_generator")


private Long id;

有了這個小改變,』插入密集』的應用會有10%-20%的提升,而並不需要做其他的代碼修改。


速效方案2 —— 使用JDBC批量插入/修改


對於批量的程序,JDBC驅動通常會提供稱之為』JDBC批量插入/修改』的優化方案用於減少網路往返消耗。在使用它們時,插入/修改在發送到資料庫前會在驅動層排隊(譯者註:達到一定的數量後會一次性發送多條SQL進行執行)。


當指定的閥值達到後,隊列中的批量語句將會被一次性發送到資料庫。這防止了驅動一個接一個的發送請求,浪費多個網路請求。


下面是用於啟用批量插入/更新的entity manager factory的配置:


true


true


只是設置JDBC batch size不會生效。這是因為JDBC驅動只有在具體某個相同的表接收到插入/更新時才會把插入當成批量處理。


如果接收到對一個新表的插入命令,JDBC驅動會在執行新表的批量語句前先送出上一個表的批量的語句。


使用Spring Batch時也有隱晦地使用到一個類似的功能。這個優化可以很簡單地為你的「插入密集」應用節省30%到40%的時間,而不需要修改一行代碼。

速效方案3 —— 定期刷新和清空Hibernate session


當添加/修改資料庫數據時,為了防止它們在session關閉後被重新修改,Hibnerate會在session中保持已經持久化的實體的版本。


但很多時候,在插入資料庫完成後,我們可以安全地丟棄實體。這可以在Java客戶端釋放內存,防止由於長時間運行Hibernate session造成的性能問題。


這種長時間運行的session應該被盡量避免,但如果由於某些原因確實需要使用,下面的代碼展示了怎麼繼續保存內存引用:


entityManager.flush();


entityManager.clear();


這個flush會觸發發送操作,新實體的插入操作會被立刻發送到資料庫。clear會從session中釋放新實體。


速效方案4 —— 減少Hibernate提前的dirty-check


Hibernate使用稱之為dirty-checking的內部的機制來跟蹤修改的實體。這個機制並不基於實體的equals和hashcode方法。


Hibnerate竭盡所能使dirty-checking的性能損耗降到最小,只有在需要的時候才進行dirty-check,但這個機制依然是會有損耗的。在有大量欄位的表時尤其需要注意。

在執行任何優化前,最重要的就是使用VisualVM計算一下dirty-check的損耗。


怎麼避免dirty-check


在Spring中,我們所知的業務方法是只讀的,dirty-check可以通過下面的方法進行關閉:


@Transactional(readOnly=true)


public void someBusinessMethod() {


....


}


另外一個可選的避免dirty-check的方法就是使用Hibnerate無狀態Session(Stateless Session),在文檔中有詳細描述。


速效方案5 —— 查找「壞」查詢方案


檢查一下在最慢查詢列表中的查詢,看看它們是否有好的查詢方案。最常見的「壞」查詢方案是:


全表查詢(Full table scans):它發生在當表由於缺失索引或過期的表數據而被全量掃描。


完全笛卡爾連接(Full cartesian joins):這意味著多個表計算完全笛卡爾積。檢查一下是否缺少連接條件,或是否可以通過分割語句來避免。


速效方案6 —— 檢查錯誤的提交間隔


如果你正在做批量處理,提交的間隔在性能結果中可以造成巨大的差別,可以達到10-100倍。


確認一個提交的間隔是所期望的(Spring Batch一般是100-1000)。它通常是因為這個參數沒有正確配置。


速效方案7 —— 使用二級和查詢緩存


如果發現某些數據很適合緩存,那麼看一下這篇文章怎麼去配置Hibernate緩存:Hibernate二級/查詢緩存的陷阱(http://blog.jhades.org/setup-and-gotchas-of-the-hibernate-second-level-and-query-caches/)。


結論


要解決應用的性能問題,要做的最重要的就是收集一些可以找到當前瓶頸所在的數據。沒有一些數據,基本上不可能在有效的時間內猜到問題在哪裡。並且,雖然不是所有,但很多的典型的「資料庫依賴」的應用性能陷阱都可以通過使用Spring Batch框架在第一時間避免。


最近熱文:


1、邀你參加11期 程序員專場相親活動


2、PHP高工/架構師,18-50K,深圳南山


3、JAVA/Web前端,深圳前海,15-30K


4、如何判斷是否到了該辭職的時候?


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 程序源 的精彩文章:

iOS 編譯過程的原理和應用
Java利用DES/3DES/AES這三種演算法分別實現對稱加密
程序員如何面對自己三十歲後的下坡路?
Java開發/PHP開發/Web前端,架構師,深圳南山

TAG:程序源 |

您可能感興趣

Instagram的Python性能調優方法介紹
利用Kubernetes和Helm進行高效的超參數調優
前端學Serverless系列-性能調優
3M 反光鞋面!低調優雅的 Air Jordan 1 「Rox Brown」 即將上架
DDN收購Intel Lustre系統業務,詳解Lustre系統架構、配置和調優
Spark調優的關鍵—RDD Cache緩存使用詳解
Tomcat 調優測試
keras參數調優
低調優雅!近觀全新配色Nike Premier II足球鞋
Tomcat 運維常用調優方式
Spark 數據傾斜調優
Hadoop虛擬化的性能對比和調優經驗
Kafka參數調優實戰
Python 環境下的自動化機器學習超參數調優
Sql性能調優
MySQL 性能調優的10個方法
Lunasol秋季限量新品眼影,你想要的精緻低調優雅就是它!
阿里P8架構師談:資料庫、JVM、緩存、SQL等性能調優方法和原則
BERT模型的標準調優和花式調優
上海旭輝M-CITY85㎡戶型樣板間,低調優雅的韻律