javascript
Spring / Hibernate应用程序的性能调优
對于大多數(shù)典型的Spring / Hibernate企業(yè)應(yīng)用程序,應(yīng)用程序性能幾乎完全取決于其持久層的性能。
這篇文章將討論如何確認我們是否存在“數(shù)據(jù)庫綁定”應(yīng)用程序,然后逐步講解7個經(jīng)常使用的“快速取勝”技巧,這些技巧可以幫助提高應(yīng)用程序性能。
如何確認應(yīng)用程序是“數(shù)據(jù)庫綁定的”
為了確認應(yīng)用程序是“數(shù)據(jù)庫綁定的”,首先在某些開發(fā)環(huán)境中使用VisualVM進行監(jiān)視以進行典型運行。 VisualVM是JDK附帶的Java Profiler,可通過調(diào)用jvisualvm通過命令行jvisualvm 。
啟動Visual VM后,請嘗試以下步驟:
- 雙擊正在運行的應(yīng)用程序
- 選擇采樣器
- 單擊Settings復選框
- 選擇Profile only packages ,然后輸入以下軟件包:
- your.application.packages.*
典型的“數(shù)據(jù)庫綁定”應(yīng)用程序的CPU配置文件應(yīng)如下所示:
我們可以看到,客戶端Java進程花費了56%的時間來等待數(shù)據(jù)庫通過網(wǎng)絡(luò)返回結(jié)果。
這是一個很好的信號,表明對數(shù)據(jù)庫的查詢使應(yīng)用程序運行緩慢。 Hibernate反射調(diào)用中的32.7%是正常的,對此無能為力。
調(diào)整的第一步–獲得基線運行
進行調(diào)整的第一步是為程序定義基線運行。 我們需要確定一組功能上有效的輸入數(shù)據(jù),以使程序通過類似于生產(chǎn)運行的典型執(zhí)行。
主要區(qū)別在于基線運行應(yīng)在更短的時間內(nèi)運行,作為指導原則,執(zhí)行時間約為5到10分鐘是一個很好的目標。
什么是好的基線?
良好的基線應(yīng)具有以下特征:
- 在功能上是正確的
- 輸入數(shù)據(jù)類似于生產(chǎn)中的各種數(shù)據(jù)
- 它在很短的時間內(nèi)完成
- 基線運行的優(yōu)化可以推斷為完整運行
獲得一個良好的基準可以解決一半的問題。
是什么導致基準差?
例如,在用于處理電信系統(tǒng)中的呼叫數(shù)據(jù)記錄的批處理運行中,采用前10000條記錄可能是錯誤的方法。
原因是,前10000個可能主要是語音呼叫,但是未知的性能問題在于SMS流量的處理。 取得大批運行的第一筆記錄會導致我們得出錯誤的基準,從而得出錯誤的結(jié)論。
收集SQL日志和查詢時間
可以使用例如log4jdbc來收集執(zhí)行時間已執(zhí)行的SQL查詢。 請參閱此博客文章,了解如何使用log4jdbc收集SQL查詢-Spring / Hibernate使用log4jdbc 改進了SQL日志記錄 。
查詢執(zhí)行時間是從Java客戶端測量的,它包括到數(shù)據(jù)庫的網(wǎng)絡(luò)往返時間。 SQL查詢?nèi)罩救缦滤?#xff1a;
16 avr. 2014 11:13:48 | SQL_QUERY /* insert your.package.YourEntity */ insert into YOUR_TABLE (...) values (...) {executed in 13 msec}準備好的語句本身也是很好的信息來源–它們使您可以輕松識別頻繁查詢的類型 。 可以通過關(guān)注此博文來記錄它們- 為什么Hibernate在哪里以及在哪里進行此SQL查詢?
可以從SQL日志中提取哪些指標
SQL日志可以給出以下問題的答案:
- 什么是執(zhí)行最慢的查詢?
- 最常見的查詢是什么?
- 生成主鍵花費的時間是多少?
- 是否有一些數(shù)據(jù)可以從緩存中受益?
如何解析SQL日志
對于大日志量,唯一可行的選擇可能是使用命令行工具。 這種方法具有非常靈活的優(yōu)點。
以編寫小的腳本或命令為代價,我們幾乎可以提取所需的任何度量。 只要您愿意,任何命令行工具都可以使用。
如果您習慣Unix命令行,bash可能是一個不錯的選擇。 Bash也可以在Windows工作站中使用,例如使用Cybwin或包含bash命令行的Git 。
經(jīng)常應(yīng)用的快速獲勝
Swift取得成功的波紋管確定了Spring / Hibernate應(yīng)用程序中的常見性能問題及其相應(yīng)的解決方案。
快速共贏的技巧1 –減少主鍵生成開銷
在“插入密集型”過程中,主鍵生成策略的選擇可能很重要。 生成ID的一種常見方法是使用數(shù)據(jù)庫序列,通常每個表使用一個序列,以避免不同表上的插入之間發(fā)生爭用。
問題在于,如果插入了50條記錄,我們希望避免對數(shù)據(jù)庫進行50次網(wǎng)絡(luò)往返以獲取50個ID,而使Java進程大部分時間處于掛起狀態(tài)。
Hibernate通常如何處理?
Hibernate提供了新的優(yōu)化ID生成器,可以避免此問題。 即,對于序列,默認情況下使用HiLo id生成器。 這是HiLo序列生成器的工作方式:
- 調(diào)用一次序列并獲得1000(高值)
- 計算50個id像這樣:
- 1000 * 50 + 0 = 50000
因此,從單個序列調(diào)用中生成了50個密鑰,這減少了開銷,導致了我無數(shù)次網(wǎng)絡(luò)往返。
這些新的經(jīng)過優(yōu)化的密鑰生成器默認在Hibernate 4中處于啟用狀態(tài),甚至可以通過將hibernate.id.new_generator_mappings設(shè)置為false來關(guān)閉。
為什么主鍵生成仍然是個問題?
問題是,如果您將密鑰生成策略聲明為AUTO ,則優(yōu)化的生成器仍然處于關(guān)閉狀態(tài),并且您的應(yīng)用程序最終將產(chǎn)生大量的序列調(diào)用。
為了確保啟用了新的優(yōu)化生成器,請確保使用SEQUENCE策略而不是AUTO :
@Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "your_key_generator") private Long id;通過這種簡單的更改,可以在“插入密集型”應(yīng)用程序中實現(xiàn)10%-20%的范圍內(nèi)的改進,而基本上無需更改代碼。
快速入門技巧2 –使用JDBC批處理插入/更新
對于批處理程序,JDBC驅(qū)動程序通常提供一種優(yōu)化措施,以減少名為“ JDBC批處理插入/更新”的網(wǎng)絡(luò)往返。 使用這些選項時,插入/更新在發(fā)送到數(shù)據(jù)庫之前會在驅(qū)動程序級別排隊。
當達到閾值時,整批排隊的語句將一次性發(fā)送到數(shù)據(jù)庫。 這樣可以防止驅(qū)動程序一個接一個地發(fā)送語句,這會導致多個網(wǎng)絡(luò)往返。
這是激活批量插入/更新所需的實體管理器工廠配置:
<prop key="hibernate.jdbc.batch_size">100</prop><prop key="hibernate.order_inserts">true</prop><prop key="hibernate.order_updates">true</prop>僅設(shè)置JDBC批處理大小將不起作用。 這是因為JDBC驅(qū)動程序僅在收到完全相同的表的插入/更新時才批處理插入。
如果收到對新表的插入,則JDBC驅(qū)動程序?qū)⑹紫人⑿律弦粋€表上的批處理語句,然后再開始對新表上的批處理語句進行處理。
如果使用Spring Batch,則隱式使用類似的功能。 通過這種優(yōu)化,您可以輕松地購買30%到40%來“插入密集型”程序,而無需更改任何代碼。
快速共贏的技巧3 –定期刷新并清除休眠會話
當在數(shù)據(jù)庫中添加/修改數(shù)據(jù)時,Hibernate在會話中保留一個已經(jīng)存在的實體版本,以防萬一在關(guān)閉會話之前再次對其進行了修改。
但是很多時候,一旦在數(shù)據(jù)庫中完成了相應(yīng)的插入操作,我們就可以安全地丟棄實體。 這樣可以釋放Java客戶端進程中的內(nèi)存,從而避免了由于長時間運行的Hibernate會話而導致的性能問題。
應(yīng)該盡可能避免這樣長時間運行的會話,但是如果出于某種原因需要它們,這就是控制內(nèi)存消耗的方法:
entityManager.flush(); entityManager.clear();flush將觸發(fā)來自新實體的插入將被發(fā)送到數(shù)據(jù)庫。 clear會從會話中釋放新實體。
快速共贏的秘訣4 –減少Hibernate臟檢查的開銷
Hibernate內(nèi)部使用一種稱為臟檢查的機制來跟蹤已修改的實體。 此機制不是基于實體類的equals和hashcode方法。
Hibernate盡最大努力將臟檢查的性能成本降到最低,并且僅在需要時才進行臟檢查,但是該機制的確有成本,這在具有大量列的表中更為明顯。
在應(yīng)用任何優(yōu)化之前,最重要的是使用VisualVM評估臟檢查的成本。
如何避免臟檢查?
在我們知道是只讀的Spring業(yè)務(wù)方法中,臟檢查可以像這樣關(guān)閉:
@Transactional(readOnly=true) public void someBusinessMethod() {.... }避免進行臟檢查的另一種方法是使用Hibernate Stateless Session,在文檔中對此進行了詳細介紹 。
快速共贏的秘訣5 –搜索“不良”查詢計劃
檢查最慢查詢列表中的查詢,看看它們是否有好的查詢計劃。 最常見的“不良”查詢計劃是:
- 全表掃描:通常由于索引缺失或表統(tǒng)計信息過時而在對表進行完全掃描時發(fā)生。
- 完全笛卡爾連接:這意味著正在計算多個表的完全笛卡爾乘積。 檢查是否缺少連接條件,或者是否可以通過將步驟分成幾個步驟來避免這種情況。
快速共贏的秘訣6 –檢查錯誤的提交間隔
如果您正在執(zhí)行批處理,則提交間隔可能會對性能結(jié)果產(chǎn)生很大的影響,例如快10到100倍。
確認提交間隔是預期的間隔(對于Spring Batch作業(yè),通常約為100-1000)。 經(jīng)常發(fā)生此參數(shù)配置不正確的情況。
快速雙贏的秘訣7 –使用二級和查詢緩存
如果某些數(shù)據(jù)被認為可以進行緩存,那么請查看此博客文章,了解如何設(shè)置Hibernate緩存: Hibernate二級/查詢緩存的陷阱
結(jié)論
為了解決應(yīng)用程序性能問題,最重要的措施是收集一些度量標準,以找出當前的瓶頸。
如果沒有一些度量標準,通常不可能在有用的時間內(nèi)猜測出正確的問題原因是什么。
另外,通過使用Spring Batch框架,可以首先避免“數(shù)據(jù)庫驅(qū)動”應(yīng)用程序的許多但不是全部典型性能缺陷。
翻譯自: https://www.javacodegeeks.com/2014/06/performance-tuning-of-springhibernate-applications.html
總結(jié)
以上是生活随笔為你收集整理的Spring / Hibernate应用程序的性能调优的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 小米集团董事长雷军一行访问武汉,将加大合
- 下一篇: 《女巫之火》游戏 FAQ 页面上线:以抢