2012-01-23 13 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,我认为,一个更大的缓冲区会显示不同的记忆行为,可能脱落的问题的一些情况。

闭上自己不应该伤害尝试。

相关问题