正確使用MySQL JDBC setFetchSize()方法解決JDBC處理大結果集 java.lang.OutOfMemoryError: Java heap space


昨天在項目中需要對日志的查詢結果進行導出功能。

日志導出功能的實現是這樣的,輸入查詢條件,然后對查詢結果進行導出。由於日志數據量比較大。多的時候,有上億條記錄。

之前的解決方案都是多次查詢,然后使用limit 限制每次查詢的條數。然后導出。這樣的結果是效率比較低效。

那么能不能一次查詢就把所有結果倒出來了?於是我就使用一次查詢,不使用limit分頁。結果出現 java.lang.OutOfMemoryError: Java heap space問題。

看來是DB服務器端將一次將查詢到的結果集全部發送到Java端保存在內存中。由於結果集比較大,所以出現OOM問題。

首先我想到的是游標功能。那么是不是可以使用游標,一次從服務器端慢慢的取呢?上網查詢了一下,大家都說MySQL不支持游標功能等等。

后來就去看JDBC代碼。找到了setFetchSize()方法,結果設置以后,卻不能生效,還是出現OOM問題。

我的設置如下

ps=conn.con.prepareStatement("select * from bigTable"); ps.setFetchSize(1000);

 

后來老大在MySQL看到了這樣的方法:

ps = (PreparedStatement) con.prepareStatement("select * from bigTable", ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); ps.setFetchSize(Integer.MIN_VALUE); ps.setFetchDirection(ResultSet.FETCH_REVERSE);


設置以后,果然可以解決我的問題。

附上代碼:

package com.seven.dbTools.DBTools;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;

public class JdbcHandleMySQLBigResultSet {

public static long importData(String sql){
String url = "jdbc:mysql://ipaddress:3306/test?user=username&password=password";
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e1) {
e1.printStackTrace();
}
long allStart = System.currentTimeMillis();
long count =0;

Connection con = null;
PreparedStatement ps = null;
Statement st = null;
ResultSet rs = null;
try {
con = DriverManager.getConnection(url);

ps = (PreparedStatement) con.prepareStatement(sql,ResultSet.TYPE_FORWARD_ONLY,
              ResultSet.CONCUR_READ_ONLY);
  
ps.setFetchSize(Integer.MIN_VALUE);

ps.setFetchDirection(ResultSet.FETCH_REVERSE);

rs = ps.executeQuery();


while (rs.next()) {

//此處處理業務邏輯
count++;
if(count%600000==0){
System.out.println(" 寫入到第  "+(count/600000)+" 個文件中!");
long end = System.currentTimeMillis();
}

}
System.out.println("取回數據量為  "+count+" 行!");
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if(rs!=null){
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(ps!=null){
ps.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(con!=null){
con.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
return count;

}

public static void main(String[] args) throws InterruptedException {

String sql = "select * from test.bigTable ";
importData(sql);

}

}

 

最近對JDBC有了進一步的了解。關於JDBC,推薦我的另一篇文章,用於解決不寫文件,從Java IO流中直接導入數據到MySQL:

Java不寫文件,LOAD DATA LOCAL INFILE大批量導入數據到MySQL的實現  http://blog.csdn.net/chenyechao/article/details/9237495

推薦另外兩篇來自 阿里巴巴 葉正盛的文章我轉載的:

關於oracle與mysql官方jdbc的一些區別

http://blog.csdn.net/chenyechao/article/details/9303979    

這篇文章是我解決問題以后才看到的,上面已經說明了MySQL JDBC的setFetchSize的使用。


免責聲明!

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



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