標籤:http io os 使用 java ar strong for 檔案
問題描述我開發的網站加了個新功能:需要線上上處理表資料的批量合并和更新,昨天下午發布上線,執行該功能後,伺服器的load突然增高,變化曲線異常,SA教育了我一番,讓我儘快處理,將CPU負載降低。
工作所需,我經常要寫些程式批量處理資料,每次執行幾十萬資料處理的時候,我機子的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();// 關閉sessionsession.close();
附測試資料:
// 測試方法:迴圈插入10000條資料,拆分10頁,每頁1000條。// 直接Hibernate的save()方法,不做任何處理。page 0 process time : 5925page 1 process time : 6722page 2 process time : 8019page 3 process time : 9456page 4 process time : 10263page 5 process time : 11511page 6 process time : 12988page 7 process time : 13969page 8 process time : 15196page 9 process time : 16820// Hibernate的save()方法,但每1個清除緩衝。page 0 process time : 10257page 1 process time : 10709page 2 process time : 11223page 3 process time : 10595page 4 process time : 10990page 5 process time : 10222page 6 process time : 10453page 7 process time : 10196page 8 process time : 9645page 9 process time : 10295// Hibernate的save()方法,但每50個清除緩衝。page 0 process time : 5848page 1 process time : 5480page 2 process time : 5739page 3 process time : 5960page 4 process time : 6287page 5 process time : 5947page 6 process time : 7012page 7 process time : 6235page 8 process time : 6063page 9 process time : 6055// JDBC的auto commit 方式page 0 process time : 840page 1 process time : 800page 2 process time : 800page 3 process time : 847page 4 process time : 806page 5 process time : 829page 6 process time : 1042page 7 process time : 893page 8 process time : 857page 9 process time : 854// JDBC的batch方式,每50個commitpage