技巧1:使用JDBC批處理插入/更新
對於批處理程序,JDBC驅動程序通常提供優化來減少網絡流量,這就是‘JDBC 批量插入/更新’。當使用這些的時候,驅動級別的插入/更新在發送到數據庫前被排入隊列。
當達到閾值後整個批處理隊列語句一次性發送到數據庫。這可以防止驅動逐條發送語句,這可以進行多網絡傳輸。
這是工廠配置的實體管理來激活插入/更新的批處理:
1
2
3
|
<prop
key= "hibernate.jdbc.batch_size" > 100 </prop> <prop
key= "hibernate.order_inserts" > true </prop> <prop
key= "hibernate.order_updates" > true </prop> |
只配置JDBC批處理的大小是不能正常工作的。這是因爲JDBC驅動程序只有當接收到插入/更新完全相同的表時纔會進行批處理插入操作。
如果新表收到一條插入語句,那麼JDBC驅動會在開始對新表進行批處理操作前首先刷新前一張表的批處理語句。
如果使用Spring批處理的話,一個類似的功能是隱式地進行使用。這種優化可以很容易地完成30%
到 40%
的‘insert
intensive’程序,而無需改動一行代碼。
技巧2:定期刷新和清理Hibernate會話
當添加/修改數據庫的數據時,Hibernate保持了一個已經存在的實體版本的會話,以防在會話關閉之前進行修改。
但是很多時候,一定在數據庫中有匹配的插入時我們就可以安全地丟棄實體。這在Java客戶機進程中釋放了內存,防止長時間運行Hibernate會話所導致的性能問題。
像這樣長時間運行的會話應該儘可能被阻止,但是由於某種原因需要它們的話就應該包含內存是如何消耗的:
1
2
|
entityManager.flush(); entityManager.clear(); |
flush
將觸發插入新實體從而發送到數據庫。clear
則從會話釋放新的實體。
技巧3:減少Hibernate過多的dirty-checking
Hibernate使用內部的一種機制來保持記錄修改的實體的方式就叫做 dirty-checking。這種機制不是基於實體的equals和hashcode方法的類。
Hibernate能讓dirty-checking的性能成本降至最低,dirty-check只會在需要的時候出現,但是這種機制也是有代價的,它有更多的表和列。
在應用做任何優化前,最重要的是測量使用VisualVM所耗費的dirty-checking的成本。
如何避免dirty-checking?
我們所知道的Spring事務方法是隻讀的,dirty-checking可以像這樣來關閉:
1
2
3
4
|
@Transactional (readOnly= true ) public
void
someBusinessMethod() { .... } |
另一種避免dirty-checking的方式是使用Hibernate無狀態會話,這在documentation有詳細描述。
技巧4:搜尋 “差的” 查詢計劃
在最慢的查詢列表裏進行檢查來看他們是否有良好的查詢計劃。最常見的“差勁的”查詢計劃是:
- 全表掃描:表完全地被掃描是因爲經常缺少索引或者過時的表統計。
- 笛卡爾連接:這意味着幾張表進行笛卡兒積後的結果正在進行計算。檢查正在丟失的連接條件,或者可以通過將一個步驟分爲幾步來完成可以避免發生這個問題。
技巧5:檢查錯誤的提交時間間隔
如果你是正在做批處理,那麼提交間隔會在性能結果上產生很大的影響, 能達到10-100倍甚至更多。
確認提交間隔是所預期的(通常是Spring批處理作業的100-1000倍)。參數配置錯誤的情況時有發生。
技巧6:使用二級查詢緩存
如果某些數據被確定爲合格緩存,那麼看看這篇博客如何設置Hibernate緩存的:Pitfalls of the Hibernate Second-Level / Query Caches