2010-02-09 10 views
16

He escrito un método insert() en el que estoy tratando de usar JDBC por lotes para la inserción de medio millón de registros en una base de datos MySQL:lotes JDBC Insertar 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); 
     } 
    } 

Pero cada vez que intento ejecutar este método, me sale el siguiente error:

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) 

Si sustituyo ps.addBatch() con ps.executeUpdate() y retire ps.executeBatch(), que funciona bien, aunque se tarda un poco de tiempo. Por favor, avíseme si sabe si usar Batch es apropiado en esta situación, y si lo es, ¿por qué da OurOfMemoryError?

Gracias

Respuesta

40

addBatch y executeBatch le dan el mecanismo para realizar inserciones por lotes, pero aún necesita hacer el algoritmo de procesamiento por lotes usted mismo.

Si simplemente apila cada instrucción en el mismo lote, como está haciendo, entonces se quedará sin memoria. Debe ejecutar/borrar el lote cada n registros. El valor de n depende de usted, JDBC no puede tomar esa decisión por usted. Cuanto mayor sea el tamaño del lote, más rápido irán las cosas, pero es demasiado grande y te morirá de memoria y las cosas se ralentizarán o fallarán. Depende de cuánta memoria tienes.

Comience con un tamaño de lote de 1000, por ejemplo, y experimente con diferentes valores desde allí.

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

Está fuera de la memoria, ya que tienen todas las transacciones en la memoria y sólo enviarlos a través de la base de datos cuando se llama executeBatch.

Si no lo necesita para ser atómica y le gustaría que el obtener un mejor rendimiento, se puede mantener un contador y llamar a todos los números de executeBatchn de registros.

+0

y cuál debería ser el valor de n? – craftsman

+3

El valor depende de usted, tiene que comparar su aplicación para obtener el mejor valor que desea para la compensación entre la memoria y el rendimiento. –