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