Пакетная вставка JDBC OutOfMemoryError - PullRequest
18 голосов
/ 09 февраля 2010

Я написал метод insert(), в котором я пытаюсь использовать пакет JDBC для вставки полмиллиона записей в базу данных MySQL:

public void insert(int nameListId, String[] names) {
        String sql = "INSERT INTO name_list_subscribers (name_list_id, name, date_added)"+
                     " VALUES (?, ?, NOW())";
        Connection conn = null;
        PreparedStatement ps = null;

        try{
            conn = getConnection();
            ps = conn.prepareStatement(sql);

            for(String s : names ){
                ps.setInt(1, nameListId); 
                ps.setString(2, s);
                ps.addBatch();
            }

            ps.executeBatch();

        }catch(SQLException e){
            throw new RuntimeException(e);
        }finally{
            closeDbResources(ps, null, conn);
        }
    }

Но всякий раз, когда я пытаюсь запустить этот метод, я получаю следующую ошибку:

java.lang.OutOfMemoryError: Java heap space
    com.mysql.jdbc.ServerPreparedStatement$BatchedBindValues.<init>(ServerPreparedStatement.java:72)
    com.mysql.jdbc.ServerPreparedStatement.addBatch(ServerPreparedStatement.java:330)
    org.apache.commons.dbcp.DelegatingPreparedStatement.addBatch(DelegatingPreparedStatement.java:171)

Если я заменим ps.addBatch() на ps.executeUpdate() и удалю ps.executeBatch(), он будет работать нормально, хотя и займет некоторое время. Пожалуйста, дайте мне знать, если вы знаете, подходит ли использование Batch в этой ситуации, и если да, то почему это дает OurOfMemoryError?

Спасибо

Ответы [ 2 ]

43 голосов
/ 09 февраля 2010

addBatch и executeBatch дают вам механизм для выполнения пакетной вставки, но вам все равно необходимо выполнить алгоритм пакетной обработки самостоятельно.

Если вы просто соберете все операторы в один и тот же пакет, как вы делаете, то у вас не хватит памяти. Вам необходимо выполнить / очистить пакет каждые n записей. Значение n зависит от вас, JDBC не может принять это решение за вас. Чем больше размер пакета, тем быстрее будут идти дела, но слишком большие, и вы получите голодание памяти, и все замедлится или потерпит неудачу. Это зависит от того, сколько у вас памяти.

Начните с размера партии, например, 1000, и поэкспериментируйте с другими значениями оттуда.

final int batchSize = 1000;
int count = 0;
for(String s : names ) {
   ps.setInt(1, nameListId); 
   ps.setString(2, s);
   ps.addBatch();

   if (++count % batchSize == 0) {
      ps.executeBatch();
      ps.clearBatch(); //not sure if this is necessary
   }
}
ps.executeBatch();   // flush the last few records.
5 голосов
/ 09 февраля 2010

Недостаточно памяти, потому что она хранит все транзакции в памяти и отправляет их в базу данных только при вызове executeBatch.

Если вам не нужно, чтобы он был атомарным и вы хотели получить лучшую производительность, вы можете сохранить счетчик и вызывать executeBatch каждые n количество записей.

...