2010-06-07 15 views
43

Ho bisogno di inserire un paio di centinaia di milioni di record nel mysql db. Sto inserendolo in batch 1 milione alla volta. Si prega di consultare il mio codice qui sotto. Sembra essere lento. C'è un modo per ottimizzarlo?JDBC prestazioni di inserimento batch

try { 
     // Disable auto-commit 
     connection.setAutoCommit(false); 

     // Create a prepared statement 
     String sql = "INSERT INTO mytable (xxx), VALUES(?)"; 
     PreparedStatement pstmt = connection.prepareStatement(sql); 

     Object[] vals=set.toArray(); 
     for (int i=0; i<vals.length; i++) { 
      pstmt.setString(1, vals[i].toString()); 
      pstmt.addBatch(); 
     } 

     // Execute the batch 
     int [] updateCounts = pstmt.executeBatch(); 
     System.out.append("inserted "+updateCounts.length); 
+0

Il codice è leggermente danneggiato (e troncato prematuramente) – Uri

+0

BTW, quale driver utilizzi? Un JDBC generale o il connettore JDBC-Mysql? – Uri

+0

Sto usando com.mysql.jdbc.Driver – user157195

risposta

8

È possibile inserire più righe con una sola istruzione di inserimento, facendo un paio di migliaia per volta può notevolmente accelerare le cose, vale a dire, invece di fare per esempio 3 inserti del modulo INSERT INTO tbl_name (a,b,c) VALUES(1,2,3);, si fa INSERT INTO tbl_name (a,b,c) VALUES(1,2,3),(1,2,3),(1,2,3); (Potrebbe essere JDBC .addBatch() fa l'ottimizzazione simile ora - sebbene il mysql addBatch fosse interamente non ottimizzato e semplicemente emettendo singole query in ogni caso - non so se questo è ancora il caso con i recenti driver)

Se davvero bisogno di velocità, caricare i dati da un file separato da virgole con LOAD DATA INFILE, otteniamo circa 7-8 volte SpeedUp farlo vs facendo decine di milioni di inserti.

+0

caricare dati in infile potrebbe essere una buona alternativa, ma il mio file di input ha bisogno di una pulizia, mi interessa solo inserire determinate righe in cui il secondo token corrisponde a una stringa (token delimitati da spazi), il caricamento dei dati è sufficientemente flessibile per filtrare le righe? – user157195

+3

Non penso che possa filtrare, ma puoi pulire tu stesso i dati, scrivere un nuovo file con i dati puliti e caricare quel file. – nos

+0

Ora i miei inserti sono 10 volte più veloci! – user393274

3

Se:

  1. E 'da inserire una nuova tabella, o la quantità è maggiore, allora i dati già inseriti
  2. ci sono gli indici della tabella
  3. Non è necessario altro l'accesso al tabella durante l'inserimento

Quindi ALTER TABLE tbl_name DISABLE KEYS può notevolmente migliorare la velocità dei vostri inserti. Quando hai finito, esegui ALTER TABLE tbl_name ENABLE KEYS per iniziare a creare gli indici, che può richiedere un po 'di tempo, ma non quasi quanto farlo per ogni inserto.

1

Si può provare a utilizzare l'oggetto DDBulkLoad.

// Get a DDBulkLoad object 
DDBulkLoad bulkLoad = DDBulkLoadFactory.getInstance(connection); 
bulkLoad.setTableName(“mytable”); 
bulkLoad.load(“data.csv”); 
126

Ho avuto un problema di prestazioni simile con mysql e risolto impostando i useServerPrepStmts ei rewriteBatchedStatements proprietà nel URL collegamento.

Connection c = DriverManager.getConnection("jdbc:mysql://host:3306/db?useServerPrepStmts=false&rewriteBatchedStatements=true", "username", "password"); 
+0

Bello! Sto vedendo un improvviso 3x – Kimble

+4

@Kimble - quindi perché non accettare questa risposta? Grazie, amico! Funziona come per magia! –

+0

OMG! Aggiungendo i parametri sopra indicati al mio URL di connessione, ho accelerato gli inserimenti batch di quasi 30x. Non sono sicuro di quali altre implicazioni abbiano queste variabili. Ma è incredibile! Grazie. – Keshav

39

Mi piacerebbe espandere la risposta di Bertil, poiché ho sperimentato i parametri dell'URL di connessione.

rewriteBatchedStatements=true è il parametro importante. useServerPrepStmts è già falso per impostazione predefinita e persino la modifica a true non fa molta differenza in termini di prestazioni di inserimento batch.

Ora penso sia il momento di scrivere come rewriteBatchedStatements=true migliora le prestazioni in modo così drammatico. Lo fa da rewriting of prepared statements for INSERT into multi-value inserts when executeBatch() (Source). Ciò significa che invece di inviare i seguenti n istruzioni INSERT al server MySQL ogni volta executeBatch() si chiama:

INSERT INTO X VALUES (A1,B1,C1) 
INSERT INTO X VALUES (A2,B2,C2) 
... 
INSERT INTO X VALUES (An,Bn,Cn) 

avrebbe inviato una singola istruzione INSERT:

INSERT INTO X VALUES (A1,B1,C1),(A2,B2,C2),...,(An,Bn,Cn) 

Si può osservare che commutando su la registrazione mysql (per SET global general_log = 1) che registrava in un file ogni istruzione inviata al server mysql.

+0

Funziona per db2? – Vipin

+0

@Vipin non ne ho idea. – Eran

0
try { 
     // Disable auto-commit 
     connection.setAutoCommit(false); 
     int maxInsertBatch = 10000;  
     // Create a prepared statement 
     String sql = "INSERT INTO mytable (xxx), VALUES(?)"; 
     PreparedStatement pstmt = connection.prepareStatement(sql); 

     Object[] vals=set.toArray(); 
     int count = 1; 
     for (int i=0; i<vals.length; i++) { 
      pstmt.setString(1, vals[i].toString()); 
      pstmt.addBatch(); 
      if(count%maxInsertBatch == 0){ 
       pstmt.executeBatch(); 
      } 
      count++; 
     } 

     // Execute the batch 
     pstmt.executeBatch(); 
     System.out.append("inserted "+count); 
+0

invece di downvoting ci potrebbe essere un commento su questo, perché può o non può migliorare le prestazioni quando si eseguono diversi batch in mezzo e non tutti in una volta ... – benez

Problemi correlati