Spring/Hibernate 應用性能優化的7種方法

對於大多數典型的 Spring/Hibernate 企業應用而言,其性能表現幾乎徹底依賴於持久層的性能。此篇文章中將介紹如何確認應用是否受數據庫約束,同時介紹七種經常使用的提升應用性能的速成法。本文系 OneAPM 工程師編譯整理。html

如何確認應用是否受限於數據庫

確認應用是否受限於數據庫的第一步,是在開發環境中進行測試,並使用 VisualVM 進行監控。VisualVM 是一款包含在 JDK 中的 Java 分析器,在命令行輸入 jvisualvm 便可調用。java

啓用 Visual VM 以後,嘗試如下步驟:git

  1. 雙擊你正在運行的應用spring

  2. 選擇 Samplersql

  3. 點擊 Settings 複選框數據庫

  4. 選擇Profile only packages,而後輸入下列包:緩存

your.application.packages.*性能優化

org.hibernate.*bash

org.springframework.*服務器

your.database.driver.package, 好比 oracle.*

點擊 Sample CPU

若是應用性能受限於數據庫,其 CPU 分析結果看起來會像下圖:

Spring/Hibernate 應用性能優化

咱們看到,客戶端 Java 進程花在等待數據庫從網絡中返回結果的時間佔56%。

看到數據庫查詢是致使應用運行緩慢的緣由,實際上是好兆頭。Hibernate 反射調用佔比32.7%是正常狀況,沒法進一步優化。

性能調優第一步:定義基準運行

性能調優的第一步是爲程序定義基準運行,咱們要定義一組能有效執行的輸入數據,讓程序基準運行與生產環境下的運行差很少。

主要的區別在於基準運行的耗時要小不少。做爲參考,5到10分鐘的執行時間比較不錯。

什麼是好的基準?

好的基準應該具有如下特徵:

  • 功能正確

  • 輸入數據的種類與生產環境下類似

  • 在短期內執行完畢

  • 基準運行的優化方案能夠外推至完整運行

定義好的基準是成功解決問題的一半。

什麼是很差的基準

例如,經過批量運行處理通信系統的電話數據記錄,選取10000條記錄就是錯誤的作法。

緣由是:前10000條記錄可能多爲語音電話,而未知的性能問題可能發生在短信流量的處理過程當中。一開始若是基準不夠好,就會致使錯誤的結論。

收集 SQL 日誌與查詢時間

SQL 查詢的執行語句與其執行時間能夠經過 log4jdbc等方式收集。詳細瞭解如何使用 log4jdbc 收集 SQL 查詢信息,點擊文章 使用 log4jdbc 優化 Spring/Hibernate 應用 SQL 日誌

查詢的執行時間是從 Java 客戶端收集的,該時間包含查詢數據庫的來回網絡調用。SQL 查詢的日誌以下:

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

預處理語句也是很重要的信息來源,它們經常會透露出經常使用的查詢類型。瞭解更多的日誌訊息,能夠查看文章:Hibernate 爲何/在何處使用該 SQL 查詢?

經過 SQL 日誌能夠了解哪些指標?

SQL 日誌能夠回答下列問題:

  • 哪些是執行過的最慢查詢?

  • 哪些是最經常使用的查詢?

  • 生成主鍵的耗時是多少?

  • 是否有數據適合緩存?

如何解析 SQL 日誌

對於大量的日誌文件,最可行的解析方式就是使用命令行工具,該方法的好處是很是靈活,只要寫一小段腳本或命令,咱們能夠抽取出幾乎大多數指標。只要你喜歡,任何命令行工具都適用。

如何你習慣了 Unix 命令行,bash 或是一個好選擇。Bash 也能夠在 Windows 工做站使用,CygwinGit 都包含了 bash 命令行。

經常使用的速成法

下面介紹的速成法能找出 Spring/Hibernate 應用中常見的性能問題,以及對應的解決方案。

速成法1——減小生成主鍵的代價

在插入操做頻繁的進程中,主鍵的生成策略很重要。生成 id 的一種常見方法是使用數據庫序列,一般一張表一個 id,從而避免在不一樣表間進行插入時的衝突。

問題在於,若是要插入50條記錄,咱們但願爲了獲取這50個 id,能夠避免50趟查詢數據庫的來回網絡調用,讓 Java 進程不一直等待。

Hibernate 一般如何解決此問題?

Hibernate 提供了優化的 ID 生成器以免此問題。也即,對於序列,會默認使用 HiLo id 生成器。如下是 HiLo 序列生成器的工做方式:

  • 調用一次序列,得到 1000 (高值)

  • 用如下方式計算50個 id

1000 * 50 + 0 = 50000

1000 * 50 + 1 = 50001

...

1000 * 50 + 49 = 50049, 達到低值 (50)

爲新的高值1001調用序列,依次類推

所以一次序列調用,可生成50個鍵,從而減小數次來回網絡調用致使的負擔。

這些優化的鍵生成器默認在 Hibernate 4中開啓。如要禁用,可將 hibernate.id.new_generator_mappings 設置爲 false。

爲何生成主鍵還是一個問題?

問題在於,若是你聲明鍵生成策略爲 AUTO,且未啓用優化的鍵生成器,那麼應用最後會面臨大量的序列調用。

爲了確保啓用優化的鍵生成器,請將鍵生成策略改成 SEQUENCE 而非 AUTO

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "your_key_generator")
private Long id;

改變設定以後,在插入操做頻繁的應用中能看到10%到20%的性能提高,並且幾乎沒有改動代碼。

速成法2——使用 JDBC 批處理 inserts/updates

對於批處理程序,JDBC 驅動程序提供了旨在減小網絡來回傳輸的優化方法:」JDBC batch inserts/updates「。使用該方法後,插入或更新會先在驅動層排隊,而後再傳送到數據庫。

當達到閾值後,全部排隊的語句都會一次性傳給數據庫。這能夠避免驅動程序逐一傳送語句,致使網絡來回傳送的負擔。

通過如下配置,就能激活批處理 inserts/updates:

<prop key="hibernate.jdbc.batch_size">100</prop>  
<prop key="hibernate.order_inserts">true</prop>  
<prop key="hibernate.order_updates">true</prop>

僅設置 JDBC 批處理大小並不夠。由於 JDBC 驅動程序只會在收到對同一張表 insert/updates 時批處理這些語句。

若是收到對一張新表的插入語句,JDBC 驅動程序會先清除對前一張表的批處理語句,而後開始分批處理針對新表的 SQL 語句。

Spring Batch 內置了類似的功能。該優化能在插入操做頻繁的應用中帶來30%到40%的性能提高,而不用改動任何代碼行。

速成法3——按期清理 Hibernate 會話

在向數據庫添加或修改數據時,Hibernate 會在會話中保留一版已經存在的實體,以防在會話關閉以前這些實體再度被修改。

可是,多數狀況下,一旦對應的插入操做已經在數據庫中完成,咱們就能夠安心地丟棄那些實體。這會釋放 Java 客戶端進程中的內存,避免太久的 Hibernate 會話致使的性能問題。

這種長久的會話應該儘可能避免。但若是出於某種緣由不得不使用它們,如下是控制內存消耗的方法:

entityManager.flush();
entityManager.clear();

flush 會觸使新實體中的插入語句傳送至數據庫。clear 則會釋放會話中的新實體。

速成法4——減小 Hibernate dirty-checking(髒數據檢查) 的代價

Hibernate 內部使用了一種機制用於追蹤被修改的實體,名爲 dirty-checking。該機制並不基於實體類中的 equals 和 hashcode 方法。

Hibernate 儘量將 dirty-checking 的性能成本保持在最低值,只在須要時使用 dirty-check。可是該機制也有成本,在列數不少的表中該成本尤爲可觀。

在進行任何優化以前,最重要的是使用 VisualVM 測量 dirty-checking 的成本。

如何避免 dirty-checking ?

dirty-checking 能夠經過如下方式禁用:

@Transactional(readOnly=true)
public void someBusinessMethod() {
....
}

禁用 dirty-checking 的另外一種方式是使用 Hibernate 無狀態會話,預知詳情請查看文檔

速成法5——搜索」壞「查詢計劃

檢查最慢查詢列表,看看有沒有好的查詢計劃。最多見的」壞「查詢計劃包括:

  • 全表搜索:一般缺乏一個索引或表統計過時時進行全表搜索。

  • 全笛卡爾鏈接:意思是計算多張表的全笛卡爾乘積。檢查一下缺乏的鏈接條件,或拆分爲幾個步驟以簡化查詢。

速成法6——檢查錯誤的提交間隔

若是你使用批處理程序,提交間隔會對性能形成十倍甚至百倍的影響。

請確保提交間隔是符合預期的(對於 Spring 批任務,一般是100到1000之間)。常常,該參數的配置不正確。

速成法7—— 使用二級查詢緩存

若是一些數據能夠緩存,則能夠查看本文了解如何設置 Hibernate 緩存:Hibernate 二級/查詢緩存的陷阱

結論

解決應用性能問題的關鍵,在於經過收集一些指標發現當前的瓶頸。

沒有一些測量指標,每每沒法在短期內找到真正的問題根源。

此外,不少典型的數據庫驅動應用的性能陷阱,若是一開始就使用了 Spring Batch,就可以避免。

原文地址:http://blog.jhades.org/performance-tuning-of-spring-hibernate-applications/

OneAPM for Java 可以深刻到全部 Java 應用內部完成應用性能管理和監控,包括代碼級別性能問題的可見性、性能瓶頸的快速識別與追溯、真實用戶體驗監控、服務器監控和端到端的應用性能管理。想閱讀更多技術文章,請訪問 OneAPM 官方博客

相關文章
相關標籤/搜索