Un moyen efficace d’effectuer des INSERTS par lots avec JDBC

Dans mon application, je dois faire beaucoup d’INSERTS. C’est une application Java et j’utilise JDBC pour exécuter les requêtes. La firebase database étant Oracle. J’ai toutefois activé le traitement par lot, ce qui évite les latences réseau pour exécuter des requêtes. Mais les requêtes s’exécutent en série en tant que INSERT distincts:

insert into some_table (col1, col2) values (val1, val2) insert into some_table (col1, col2) values (val3, val4) insert into some_table (col1, col2) values (val5, val6) 

Je me demandais si la forme suivante d’INSERT pourrait être plus efficace:

 insert into some_table (col1, col2) values (val1, val2), (val3, val4), (val5, val6) 

c’est-à-dire en regroupant plusieurs INSERT en une seule.

D’autres astuces pour créer des INSERT par lots plus rapidement?

Ceci est un mélange des deux réponses précédentes:

  PreparedStatement ps = c.prepareStatement("INSERT INTO employees VALUES (?, ?)"); ps.setSsortingng(1, "John"); ps.setSsortingng(2,"Doe"); ps.addBatch(); ps.clearParameters(); ps.setSsortingng(1, "Dave"); ps.setSsortingng(2,"Smith"); ps.addBatch(); ps.clearParameters(); int[] results = ps.executeBatch(); 

Bien que la question demande l’ insertion efficace dans Oracle en utilisant JDBC , je joue actuellement avec DB2 (sur le mainframe IBM), l’insertion conceptuelle serait similaire, donc il pourrait être utile de voir mes statistiques entre

  • insérer un enregistrement à la fois

  • insérer un lot d’enregistrements (très efficace)

Voici les mésortingques

1) Insérer un enregistrement à la fois

 public void writeWithComstackQuery(int records) { PreparedStatement statement; try { Connection connection = getDatabaseConnection(); connection.setAutoCommit(true); Ssortingng comstackdQuery = "INSERT INTO TESTDB.EMPLOYEE(EMPNO, EMPNM, DEPT, RANK, USERNAME)" + " VALUES" + "(?, ?, ?, ?, ?)"; statement = connection.prepareStatement(comstackdQuery); long start = System.currentTimeMillis(); for(int index = 1; index < records; index++) { statement.setInt(1, index); statement.setString(2, "emp number-"+index); statement.setInt(3, index); statement.setInt(4, index); statement.setString(5, "username"); long startInternal = System.currentTimeMillis(); statement.executeUpdate(); System.out.println("each transaction time taken = " + (System.currentTimeMillis() - startInternal) + " ms"); } long end = System.currentTimeMillis(); System.out.println("total time taken = " + (end - start) + " ms"); System.out.println("avg total time taken = " + (end - start)/ records + " ms"); statement.close(); connection.close(); } catch (SQLException ex) { System.err.println("SQLException information"); while (ex != null) { System.err.println("Error msg: " + ex.getMessage()); ex = ex.getNextException(); } } } 

Les mésortingques pour 100 transactions:

 each transaction time taken = 123 ms each transaction time taken = 53 ms each transaction time taken = 48 ms each transaction time taken = 48 ms each transaction time taken = 49 ms each transaction time taken = 49 ms ... .. . each transaction time taken = 49 ms each transaction time taken = 49 ms total time taken = 4935 ms avg total time taken = 49 ms 

La première transaction prend environ 120-150ms ce qui correspond à l'parsing de la requête , puis à l'exécution. Les transactions suivantes ne prennent que 120-150ms environ. (Ce qui est encore élevé, mais ma firebase database est sur un serveur différent (j'ai besoin de dépanner le réseau))

2) Avec insertion dans un lot (efficace) - réalisé par preparedStatement.executeBatch()

 public int[] writeInABatchWithComstackdQuery(int records) { PreparedStatement preparedStatement; try { Connection connection = getDatabaseConnection(); connection.setAutoCommit(true); Ssortingng comstackdQuery = "INSERT INTO TESTDB.EMPLOYEE(EMPNO, EMPNM, DEPT, RANK, USERNAME)" + " VALUES" + "(?, ?, ?, ?, ?)"; preparedStatement = connection.prepareStatement(comstackdQuery); for(int index = 1; index <= records; index++) { preparedStatement.setInt(1, index); preparedStatement.setString(2, "empo number-"+index); preparedStatement.setInt(3, index+100); preparedStatement.setInt(4, index+200); preparedStatement.setString(5, "usernames"); preparedStatement.addBatch(); } long start = System.currentTimeMillis(); int[] inserted = preparedStatement.executeBatch(); long end = System.currentTimeMillis(); System.out.println("total time taken to insert the batch = " + (end - start) + " ms"); System.out.println("total time taken = " + (end - start)/records + " s"); preparedStatement.close(); connection.close(); return inserted; } catch (SQLException ex) { System.err.println("SQLException information"); while (ex != null) { System.err.println("Error msg: " + ex.getMessage()); ex = ex.getNextException(); } throw new RuntimeException("Error"); } } 

Les mésortingques pour un lot de 100 transactions sont

 total time taken to insert the batch = 127 ms 

et pour 1000 transactions

 total time taken to insert the batch = 341 ms 

Ainsi, faire 100 transactions en ~5000ms (avec un trxn à la fois) est réduit à ~150ms (avec un lot de 100 enregistrements).

REMARQUE - Ignore mon réseau qui est très lent, mais les valeurs des mesures seraient relatives.

La Statement vous donne l’option suivante:

 Statement stmt = con.createStatement(); stmt.addBatch("INSERT INTO employees VALUES (1000, 'Joe Jones')"); stmt.addBatch("INSERT INTO departments VALUES (260, 'Shoe')"); stmt.addBatch("INSERT INTO emp_dept VALUES (1000, 260)"); // submit a batch of update commands for execution int[] updateCounts = stmt.executeBatch(); 

Il est évident que vous devrez effectuer des tests, mais sur JDBC, l’insertion de plusieurs insertions sera beaucoup plus rapide si vous utilisez un PreparedStatement plutôt qu’un Statement.

Que diriez-vous d’utiliser l’instruction INSERT ALL?

 INSERT ALL INTO table_name VALUES () INTO table_name VALUES () ... SELECT Statement; 

Je me souviens que la dernière instruction select est obligatoire pour que cette requête réussisse. Ne te souviens pas pourquoi. Vous pouvez également envisager d’utiliser PreparedStatement . beaucoup d’avantages!

Farid

Vous pouvez utiliser addBatch et executeBatch pour l’insertion par lots dans Java Voir l’exemple: Insérer un lot en Java

Utiliser PreparedStatements sera BEAUCOUP plus lent que les instructions si vous avez des itérations faibles. Pour tirer un avantage des performances en utilisant un PrepareStatement sur une instruction, vous devez l’utiliser dans une boucle où les itérations sont au moins 50 ou plus.

Insert de lot à l’aide de l’instruction

 int a= 100; try { for (int i = 0; i < 10; i++) { String insert = "insert into usermaster" + "(" + "userid" + ")" + "values(" + "'" + a + "'" + ");"; statement.addBatch(insert); System.out.println(insert); a++; } dbConnection.commit(); } catch (SQLException e) { System.out.println(" Insert Failed"); System.out.println(e.getMessage()); } finally { if (statement != null) { statement.close(); } if (dbConnection != null) { dbConnection.close(); } }