Получение java.lang.outOfMemoryError при сохранении больших файлов в базе данных MySQL с использованием Java BufferedInputStream - PullRequest
0 голосов
/ 23 января 2012

В настоящее время я экспериментирую с хранением больших файлов в базе данных MySQL 5.5 с использованием Java.Мой основной класс называется 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

Программа работает нормально, когдазагрузка небольших документов, но когда я загружаю файлы размером около 20 МБ, процесс загрузки идет очень медленно.Поэтому я попытался поместить FileInputStream в BufferedInputStream в следующем коде:

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

Процесс загрузки стал очень быстрым.Это как копирование файла в другой каталог.Но когда я попытался загрузить файлы размером более 400 МБ, я получил следующую ошибку:

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, и я не получил ошибку.Мне удалось загрузить файлы размером 500 МБ в 1,5 ГБ в базу данных Derby с помощью BufferedInputStream.Я также заметил, что при использовании BufferedInputStream с сервером MySQL при загрузке больших файлов JVM потребляет много памяти, в то время как при использовании его в базе данных Derby использование памяти JVM поддерживается на уровне 85–100 МБ.

Я относительно новичок в MySQL, и я просто использую его настройки по умолчанию.Единственное, что я изменил в его конфигурации, это размер "max_allowed_packet", поэтому я могу загрузить до 2 ГБ файла в базу данных.Поэтому мне интересно, откуда появилась ошибка.Это ошибка MySQL или коннектор MySQL / J?или что-то не так с моим кодом?

То, чего я пытаюсь достичь, - это возможность загружать большие файлы (до 2 ГБ) на сервер MySQL, используя java, без увеличения пространства кучи java.

Ответы [ 4 ]

1 голос
/ 16 мая 2013

Существует другой метод разрешения, если вы не хотите увеличивать размер кучи 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 голосов
/ 16 мая 2013

Кажется, проблема в JDBC MySQL больше.Конечно, вы можете рассмотреть GZip + Piped 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 КБ. Думаю, больший буфер может показывать другое поведение памяти, возможно, пролить светна проблему.

Закрытие себя не должно повредить, чтобы попытаться.

0 голосов
/ 07 марта 2012

увеличение размера кучи JVM при запуске Java-кода:

right click your java file
    ->run as->run configurations->arguments->VM arguments
0 голосов
/ 24 января 2012

Только ради этого, попробуйте увеличить размер кучи JVM.

увеличить размер кучи Java навсегда? http://javahowto.blogspot.com/2006/06/6-common-errors-in-setting-java-heap.html

...