簡體   English   中英

使用Java的BufferedInputStream將大文件存儲到MySQL數據庫時,獲取java.lang.outOfMemoryError

[英]Getting java.lang.outOfMemoryError when Storing Large Files to a MySQL database using Java's BufferedInputStream

我目前正在嘗試使用Java在MySQL 5.5數據庫上存儲大文件。 我的主類稱為FileDatabaseTest。 它具有以下方法:

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();
        }
    }

...

}

該數據庫只有一個表-“ FILES”表,並且具有以下列。

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將500MB的文件上傳到Derby數據庫中的1.5G文件中。 我還觀察到,在MySQL服務器上使用BufferedInputStream上傳大文件時,JVM占用了大量內存,而在Derby數據庫中使用JVM時,JVM的內存使用量保持在85MB至100MB左右。

我是MySQL的新手,我只是使用它的默認配置。 我對其配置進行的唯一更改是“ max_allowed_pa​​cket”的大小,因此我可以將最多2GB的文件上傳到數據庫。 所以我想知道錯誤是從哪里來的。 它是MySQL的錯誤還是MySQL連接器/ J的錯誤? 還是我的代碼有問題?

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

如果不想增加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();

現在您將一一讀取結果行

似乎更多是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(); //?

我認為默認緩沖區大小為8 KB,較大的緩沖區可能會顯示不同的內存行為,也許可以減輕一些問題的困擾。

閉上自己的手應該不會受傷。

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

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM