2012-01-23 14 views
0

當前正在嘗試使用java在MySQL 5.5數據庫上存儲大文件。我的主類叫做FileDatabaseTest。它有以下方法:使用Java的BufferedInputStream將大文件存儲到MySQL數據庫時獲取java.lang.outOfMemoryError

import java.sql.*; 
import java.io.*; 

... 

public class FileDatabaseTest { 

... 

private void uploadToDatabase(File file, String description) { 
     try { 
      PreparedStatement stmt = connection.prepareStatement(
       "INSERT INTO FILES (FILENAME, FILESIZE, FILEDESCRIPTION, FILEDATA) " + 
        "VALUES (?, ?, ?, ?)"); 
      stmt.setString(1, file.getName()); 
      stmt.setLong(2, file.length()); 
      stmt.setString(3, description); 
      stmt.setBinaryStream(4, new FileInputStream(file)); 
      stmt.executeUpdate(); 
      updateFileList(); 
      stmt.close(); 
     } catch(SQLException e) { 
      e.printStackTrace(); 
     } catch(FileNotFoundException e) {//thrown by FileInputStream constructor 
      e.printStackTrace(); 
     } catch(SecurityException e) { //thrown by FileInputStream constructor 
      e.printStackTrace(); 
     } 
    } 

... 

} 

該數據庫只有一個表 - 「文件」表,它有以下列。

ID - AUTOINCREMENT, PRIMARY KEY 

FILENAME - VARCHAR(100) 

FILESIZE - BIGINT 

FILEDESCRIPTION - VARCHAR(500) 

FILEDATA - LONGBLOB 

上傳小文件時程序工作正常,但是當我上傳20MB等文件時,上傳過程非常緩慢。於是,我把一個的FileInputStream裏面的BufferedInputStream在下面的代碼:

stmt.setBinaryStream(4, new BufferedInputStream(new FileInputStream(file)); 

上傳過程變得非常快。就像將文件複製到另一個目錄一樣。但是,當我試圖更上傳文件超過400MB,我得到了以下錯誤:

Exception in thread "Thread-5" java.lang.OutOfMemoryError: Java heap space 
    at com.mysql.jdbc.Buffer.ensureCapacity(Buffer.java:156) 
    at com.mysql.jdbc.Buffer.writeBytesNoNull(Buffer.java:514) 
    at com.mysql.jdbc.PreparedStatement.escapeblockFast(PreparedStatement.java:1169) 
    at com.mysql.jdbc.PreparedStatement.streamToBytes(PreparedStatement.java:5064) 
    at com.mysql.jdbc.PreparedStatement.fillSendPacket(PreparedStatement.java:2560) 
    at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2401) 
    at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2345) 
    at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2330) 
    at FileDatabaseTest$2.run(FileDatabaseTest.java:312) 
    at java.lang.Thread.run(Thread.java:662) 

所以我使用嵌入式Apache的Derby數據庫,而不是MySQL的審判,我沒有得到這個錯誤。我能夠使用BufferedInputStream在Derby數據庫中上傳500MB到1.5G的文件。我還觀察到,當使用BufferedInputStream和MySQL服務器上傳大文件時,JVM佔用大量內存,而當我在Derby數據庫中使用它時,JVM的內存使用率保持在85MB到100MB左右。

我對MySQL比較新,我只是使用它的默認配置。我在配置中唯一改變的是「max_allowed_pa​​cket」大小,所以我可以上傳2GB文件到數據庫。所以我想知道錯誤來自哪裏。它是MySQL或MySQL連接器/ J的錯誤嗎?或者我的代碼有什麼問題?

我想在這裏實現的是能夠使用java將大文件(最多2GB)上傳到MySQL服務器,而不增加Java堆空間。

+0

Mysql jdbc驅動程序有一個名爲「maxAllowedPacket」的選項,默認情況下該選項與max_allowed_pa​​cket的值匹配。所以這聽起來像是驅動程序在內部爲整個數據包分配了足夠的緩衝空間。 – Vadim

+0

我已經嘗試更改「maxAllowedPacket」屬性,但是當我將其更改爲更小的值時,發送大於「maxAllowedProperty」值的文件時出現錯誤。當我將其更改爲更高值時,上傳大文件時出現java.lang.outOfMemory錯誤。我觀察到的是,在執行sql語句之前,使用MySQL,java會將整個文件的內容讀入內存,我認爲這是上傳大文件時出現outOfMemory錯誤的原因。有沒有辦法阻止java發送到MySQL之前將整個文件加載到內存中? – Esjee

+0

你解決了這個問題嗎? – Rafaesp

回答

0

運行Java代碼時正在增加JVM堆大小:

right click your java file 
    ->run as->run configurations->arguments->VM arguments 
1

有另一種解決方法,如果你不想你正在增加JVM堆大小:

首先,你的MySQL版本應該比5.0更新。

其次,Statement.getResultSetType()應該是TYPE_FORWARD_ONLY,並且ResultSetConcurrency應該是CONCUR_READ_ONLY(默認值)。

三,包括以下其中一行: 1).statement.setFetchSize(Integer.MIN_VALUE); 2)。((com.mysql.jdbc.Statement)stat).enableStreamingResults();

現在你會被一個

0

這似乎更是一個MySQL JDBC問題讀取結果行之一。當然你也可以考慮使用GZip +管道I/O。

我還發現了一個可怕的解決方案,做插入的部分:

UPDATE FILES SET FILEDATA = CONCAT(FILEDATA, ?) 

我們可以得出結論,對於大文件,最好將其保存在磁盤上。

不過:

final int SIZE = 1024*128; 
InputStream in = new BufferedInputStream(new FileInputStream(file), SIZE); 
stmt.setBinaryStream(4, in); 
stmt.executeUpdate(); 
updateFileList(); 
stmt.close(); 
in.close(); //? 

默認的緩衝區大小爲8KB,我認爲,一個更大的緩衝區會顯示不同的記憶行爲,可能脫落的問題的一些情況。

閉上自己不應該傷害嘗試。

相關問題