preparestatement preparedstatement los libreria insertar funcionamiento ejemplos desde datos consultas con java resultset bulkinsert prepared-statement

preparedstatement - Inserción masiva en Java usando declaraciones preparadas actualización por lotes



preparedstatement java insert (4)

Estoy tratando de llenar un conjunto de resultados en Java con aproximadamente 50,000 filas de 10 columnas y luego insertarlas en otra tabla usando el método batchExecute de PreparedStatement .

Para acelerar el proceso, investigué un poco y descubrí que, al leer los datos en el resultado, establecer que fetchSize juega un papel importante.

Tener un fetchSize muy bajo puede resultar en demasiados viajes al servidor y un fetchSize muy alto puede bloquear los recursos de la red, por lo que experimenté un poco y configuré un tamaño óptimo que se adapte a mi infraestructura.

Estoy leyendo este conjunto de resultados y creando instrucciones de inserción para insertar en otra tabla de una base de datos diferente.

Algo como esto (solo una muestra, no código real):

for (i=0 ; i<=50000 ; i++) { statement.setString(1, "[email protected]"); statement.setLong(2, 1); statement.addBatch(); } statement.executeBatch();

  • ¿Intentará el método executeBatch enviar todos los datos a la vez?
  • ¿Hay una manera de definir el tamaño del lote?
  • ¿Hay alguna mejor manera de acelerar el proceso de inserción masiva?

Al actualizar de forma masiva (50,000 filas 10 cols), ¿es mejor usar un ResultSet o PreparedStaement actualizable con ejecución por lotes?


El lote se realizará en "todo a la vez", eso es lo que le pediste que hiciera.

50,000 parece un poco grande para intentar en una llamada. Lo dividiría en trozos más pequeños de 1,000, así:

final int BATCH_SIZE = 1000; for (int i = 0; i < DATA_SIZE; i++) { statement.setString(1, "[email protected]"); statement.setLong(2, 1); statement.addBatch(); if (i % BATCH_SIZE == BATCH_SIZE - 1) statement.executeBatch(); } if (DATA_SIZE % BATCH_SIZE != 0) statement.executeBatch();

50,000 filas no deberían tomar más de unos pocos segundos.


La actualización masiva no registrada no le dará el rendimiento mejorado que desea en la forma en que lo hace. Ver this


Si solo se insertan datos de una / más tablas en la base de datos en esta tabla y no hay intervención (alteraciones en el conjunto de resultados) , entonces llame a statement.executeUpdate(SQL) para realizar la declaración INSERT-SELECT , esto es más rápido ya que no hay gastos generales. No hay datos que salgan de la base de datos y toda la operación está en la base de datos no en la aplicación.


Voy a abordar sus preguntas a su vez.

  • ¿Intentará el método executeBatch enviar todos los datos a la vez?

Esto puede variar con cada controlador JDBC, pero los pocos que he estudiado se repetirán en cada entrada de lote y enviarán los argumentos junto con el identificador preparado cada vez a la base de datos para su ejecución. Es decir, en su ejemplo anterior, habría 50,000 ejecuciones de la declaración preparada con 50,000 pares de argumentos, pero estos 50,000 pasos se pueden realizar en un "bucle interno" de nivel inferior, que es donde entra el ahorro de tiempo. una analogía más bien estirada, es como abandonar el "modo de usuario" al "modo kernel" y ejecutar todo el bucle de ejecución allí. Usted ahorra el costo de bucear dentro y fuera de ese modo de nivel inferior para cada entrada de lote.

  • ¿Hay alguna manera de definir el tamaño del lote?

Lo ha definido implícitamente aquí al insertar 50,000 conjuntos de argumentos antes de ejecutar el lote a través de la Statement#executeBatch() . Un tamaño de lote de uno es igual de válido.

  • ¿Hay alguna mejor manera de acelerar el proceso de inserción masiva?

Considere abrir una transacción explícitamente antes de la inserción por lotes y cometerla después. No permita que la base de datos o el controlador JDBC impongan un límite de transacción alrededor de cada paso de inserción en el lote. Puede controlar la capa JDBC con el método Connection#setAutoCommit(boolean) . Primero, quite la conexión del modo de confirmación automática , luego complete los lotes, inicie una transacción, ejecute el lote, luego confirme la transacción a través de la Connection#commit() .

Este consejo supone que sus inserciones no competirán con escritores concurrentes y que estos límites de transacción le darán valores suficientemente consistentes leídos de las tablas de origen para su uso en las inserciones. Si ese no es el caso, favorece la corrección sobre la velocidad.

  • ¿Es mejor usar un ResultSet o PreparedStatement actualizable con ejecución por lotes?

Nada supera las pruebas con el controlador JDBC de su elección, pero espero que la última: PreparedStatement and Statement#executeBatch() ganará aquí. El identificador de la declaración puede tener una lista o matriz asociada de "argumentos por lotes", siendo cada entrada el conjunto de argumentos proporcionado entre las llamadas a la Statement#executeBatch() y la Statement#addBatch() (o la Statement#clearBatch() ). La lista aumentará con cada llamada a addBatch() , y no se executeBatch() hasta que llame a executeBatch() . Por lo tanto, la instancia de Statement realmente está actuando como un buffer de argumento; está intercambiando memoria por conveniencia (usando la instancia de Statement en lugar de su propio conjunto de argumentos externo).

Una vez más, debe considerar estas respuestas generales y especulativas siempre que no estemos discutiendo un controlador JDBC específico . Cada controlador varía en sofisticación, y cada uno variará en las optimizaciones que persigue.