2010-02-09 4 views
16

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

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?

Благодаря

ответ

40

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

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

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

+0

и что должно быть значением n? – craftsman

+3

Значение зависит от вас, вам нужно сравнить ваше приложение, чтобы получить лучшее значение для того, что вы хотите для обмена информацией между памятью и производительностью. –