Hibernate批處理操作優化 (批量插入、更新與刪除)


問題描述

  1. 我開發的網站加了個新功能:需要在線上處理表數據的批量合並和更新,昨天下午發布上線,執行該功能后,服務器的load突然增高,變化曲線異常,SA教育了我一番,讓我盡快處理,將CPU負載降低。

  2. 工作所需,我經常要寫些程序批量處理數據,每次執行幾十萬數據處理的時候,我機子的CPU都會飆高,而且數據處理速度會越來越慢。比如第一個1W條要5分鍾,第二個1W條就要10分鍾,要干其他事情的時候機子也卡的不行,只能等着處理完數據。

其實我一直認為是數據量太大,從來不認為是程序問題,所以一直沒怎么關注過。這次問題浮上表面,所以要好好解決下!

 

產生原因

主要原因:Hibernate的一級緩存影響。

我們每次保存的東西都會保存在Session緩存中,這就是Hibernate的一級緩存,如果我們一直循環執行save等操作,緩存里東西會越來越多,速度也就越來越慢,服務器一直在循環處理,自然也會增加負載。

這本來就是Hibernate不擅長的地方,而且一級緩存不可以不用,如果我們要保存的數據量十分巨大,那么在程序中執行添加、更新方法時,Session對象自身開辟的一級緩存會不斷消耗,直至OutOfMemoryError (內存溢出異常)。

這就需要我們管理好Hibernate的緩存,或者不使用Hibernate。

 

解決方案

批量插入優化

1、仍舊用Hibernate API來進行批處理,但在一定的量的時候,及時的清除緩存。

1)優化Hibernate,在配置文件中設置hibernate.jdbc.batch_size參數,來指定每次提交SQL的數量。配置hibernate.jdbc.batch_size參數的原因就是盡量少讀數據庫,hibernate.jdbc.batch_size參數值越大,讀數據庫的次數越少,速度越快。

<!--設置hibernate.jdbc.batch_size參數--> <hibernate-configuration> <session-factory> ......... <property name="hibernate.jdbc.batch_size">50</property> ......... <session-factory> <hibernate-configuration> 

 

2)程序及時清除緩存,即每插入一定量的數據后及時把它們從內部緩存中清除掉,釋放占用的內存。 Session實現了異步write-behind,它允許Hibernate顯式地寫操作批處理

示例代碼:

// 每處理50條清空緩存 session.save(myObject); if (i/50 == 0) { session.flush(); session.clear(); } // 在我的項目中寫法如下: if (i/50 == 0) { this.getHibernateTemplate().flush(); this.getHibernateTemplate().clear(); } 

 

2、通過JDBC API來做批量插入,繞過Hibernate API。這個方法性能上是最好的,也是最快的。

示例代碼:

String insertSql = "insert into user(name,address) values(?,?)"; Session session = getHibernateTemplate().getSessionFactory().openSession(); Connection conn = session.connection(); PrepareStatement stmt = conn.prepareStatement(insertSql); // 方式1:自動提交 conn.setAutoCommit(true); for(int i = 0; i++; i<10000) { stmt.setString(1, "testName"); stmt.setString(2, "testAddress"); stmt.execute(); } // 方式2:批量提交 conn.setAutoCommit(false); for(int i = 0; i++; i<10000) { stmt.setString(1, "testName"); stmt.setString(2, "testAddress"); stmt.addBatch(); if (i % 100 == 0) { stmt.executeBatch(); conn.commit(); } } stmt.executeBatch(); conn.commit(); // 關閉session session.close(); 

 

附測試數據:

// 測試方法:循環插入10000條數據,拆分10頁,每頁1000條。 // 直接Hibernate的save()方法,不做任何處理。 page 0 process time : 5925 page 1 process time : 6722 page 2 process time : 8019 page 3 process time : 9456 page 4 process time : 10263 page 5 process time : 11511 page 6 process time : 12988 page 7 process time : 13969 page 8 process time : 15196 page 9 process time : 16820 // Hibernate的save()方法,但每1個清除緩存。 page 0 process time : 10257 page 1 process time : 10709 page 2 process time : 11223 page 3 process time : 10595 page 4 process time : 10990 page 5 process time : 10222 page 6 process time : 10453 page 7 process time : 10196 page 8 process time : 9645 page 9 process time : 10295 // Hibernate的save()方法,但每50個清除緩存。 page 0 process time : 5848 page 1 process time : 5480 page 2 process time : 5739 page 3 process time : 5960 page 4 process time : 6287 page 5 process time : 5947 page 6 process time : 7012 page 7 process time : 6235 page 8 process time : 6063 page 9 process time : 6055 // JDBC的auto commit 方式 page 0 process time : 840 page 1 process time : 800 page 2 process time : 800 page 3 process time : 847 page 4 process time : 806 page 5 process time : 829 page 6 process time : 1042 page 7 process time : 893 page 8 process time : 857 page 9 process time : 854 // JDBC的batch方式,每50個commit page 0 process time : 827 page 1 process time : 801 page 2 process time : 918 page 3 process time : 828 page 4 process time : 856 page 5 process time : 831 page 6 process time : 815 page 7 process time : 842 page 8 process time : 817 page 9 process time : 937 

 

經測試:

1)若直接使用Hibernate,處理同樣數據的時間會遞增,甚至成倍增加,而且在測試過程中CPU使用率一直在70%上下。

2)若在使用Hibernate中每save一次都清空緩存的話,雖然時間不會遞增,但處理速度很慢。在本例中采用每50個清空一次緩存較為合適,實際應用視情況而定。一定量的時候清空緩存,雖然速度上沒有提升,但會比較穩定,不會隨着時間陡增,而且測試中CPU使用率也維持在20%上下,可以挽救一點性能損失,使系統相對穩定。

3)若使用JDBC API,不論auto commit方式還是batch方式,相比Hibernate在性能上都有近10倍的提升。不過在數據量較大的時候,推薦使用batch方式。

批量更新刪除優化

Hibernate2中,對於批量更新/刪除操作,都是先將符合要求的數據查出來,然后再做更新/刪除操作。這樣一來會占用大量內存,而且海量數據處理的時候性能很低。

而Hibernate3對批量更新/刪除提供了支持,能夠直接執行批量更新批量刪除語句,無需把被更新刪除的對象先加載到內存中,類似於JDBC的批量更新/刪除操作

不過對於循環處理數據更新刪除場景,建議還是使用JDBC,方法同上:批量插入的方法2。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM