2010-02-09 9 views
16

Ho scritto un metodo insert() in cui sto cercando di utilizzare JDBC batch per l'inserimento di mezzo milione di record in un database MySQL:batch JDBC Inserire 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); 
     } 
    } 

Ma ogni volta che provo a fare funzionare questo metodo, ho ottenere il seguente errore:

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) 

Se sostituisco ps.addBatch() con ps.executeUpdate() e rimuovere ps.executeBatch(), funziona benissimo, anche se ci vuole un po 'di tempo. Per favore fatemi sapere se sapete se usare Batch è appropriato in questa situazione, e se lo è, allora perché dà OurOfMemoryError?

Grazie

risposta

40

addBatch e executeBatch forniscono il meccanismo per eseguire inserimenti batch, ma è comunque necessario eseguire da soli l'algoritmo di dosaggio.

Se si sommano semplicemente tutte le istruzioni nello stesso batch, come si sta facendo, la memoria si esaurirà. È necessario eseguire/cancellare il batch ogni record n. Il valore di n dipende da te, JDBC non può prendere quella decisione per te. Maggiore è la dimensione del batch, le cose più veloci andranno, ma troppo grandi e avrai fame di memoria e le cose rallenteranno o falliranno. Dipende da quanta memoria hai.

Iniziare con una dimensione batch di 1000, ad esempio, e sperimentare con valori diversi da lì.

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

E 'la memoria, perché tenere tutto la transazione in memoria e solo inviarlo sopra al database quando si chiama executeBatch.

Se non ne hai bisogno di essere atomica e vorrebbe che il ottenere prestazioni migliori, è possibile mantenere un contatore e chiamare executeBatch ogni numero n di record.

+0

e quale dovrebbe essere il valore di n? – craftsman

+3

Il valore spetta a te, devi confrontare la tua applicazione per ottenere il miglior valore per quello che vuoi per il trade off tra memoria e prestazioni. –