2010-06-07 15 views
43

Potrzebuję wstawić kilkaset milionów rekordów do bazy danych mysql db. Jestem wsadowy wkładając go milion za jednym razem. Zobacz mój kod poniżej. Wydaje się być powolny. Czy istnieje sposób na jej optymalizację?Wydajność wsadu wsadowego JDBC

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

Twój kod został nieco uszkodzony (a obcięte przedwcześnie) – Uri

+0

BTW, którego kierowca używasz? Ogólny JDBC lub złącze JDBC-Mysql? – Uri

+0

Używam com.mysql.jdbc.Driver – user157195

Odpowiedz

8

można wstawić wiele wierszy z jednej instrukcji insert, robiąc kilka tysięcy naraz może znacznie przyspieszyć, czyli zamiast robić np 3 wstawki w postaci INSERT INTO tbl_name (a,b,c) VALUES(1,2,3);, robisz INSERT INTO tbl_name (a,b,c) VALUES(1,2,3),(1,2,3),(1,2,3); (To może być JDBC .addBatch() robi teraz podobną optymalizację - chociaż mysql addBatch był niezobowiązująco niezoptymalizowany i po prostu wydawał indywidualne zapytania - nie wiem czy to nadal w przypadku najnowszych sterowników)

Jeśli naprawdę potrzebujesz prędkości, załaduj dane z pliku rozdzielanego przecinkami za pomocą LOAD DATA INFILE, uzyskujemy około 7-8-krotne przyspieszenie w porównaniu z dziesiątkami milionów wstawek.

+0

ładowanie danych może być dobrą alternatywą, ale mój plik wejściowy potrzebuje oczyszczenia, interesuje mnie tylko wstawianie pewnych wierszy, w których drugi token pasuje do ciągu znaków (tokeny rozdzielane spacjami), czy plik danych jest wystarczająco elastyczny, by filtrować wiersze? – user157195

+3

Nie sądzę, może filtrować, ale możesz sam wyczyścić dane, napisz nowy plik z wyczyszczonymi danymi i załaduj ten plik. – nos

+0

Moje wstawki są teraz 10 razy szybsze! – user393274

3

Jeśli:

  1. To nowy stół, czy kwota włożona jest większa wtedy już wstawionych danych
  2. Tam są indeksy na stole
  3. Nie trzeba innego dostępu do tabela podczas wstawiania

Następnie można znacznie poprawić prędkość wkładek. Kiedy skończysz, uruchom ALTER TABLE tbl_name ENABLE KEYS, aby zacząć budować indeksy, co może trochę potrwać, ale nie tak długo, jak robi to dla każdej wstawki.

1

Możesz spróbować użyć obiektu DDBulkLoad.

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

miałem podobny problem wydajności z mysql i rozwiązać go poprzez ustawienie useServerPrepStmts a rewriteBatchedStatements właściwości w adresie URL połączenia.

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

Nice! Widzę 3-krotny wzrost wartości – Kimble

+4

@Kimble - więc dlaczego nie przyjąć tej odpowiedzi? Dzięki, kolego! Działa to jak magia! –

+0

OMG! Dodanie powyższych parametrów do mojego adresu URL połączenia przyspieszyło wsad o prawie 30x. Nie jestem pewien, jakie są inne implikacje tych zmiennych. Ale to niesamowite! Dzięki. – Keshav

39

Chciałbym rozwinąć odpowiedź Bertila, ponieważ eksperymentowałem z parametrami adresu URL połączenia.

rewriteBatchedStatements=true to ważny parametr. useServerPrepStmts jest już domyślnie fałszywe, a nawet zmiana na true nie robi dużej różnicy pod względem wydajności wsadowego wsadu.

Teraz myślę, że nadszedł czas, aby napisać, jak rewriteBatchedStatements=true znacznie poprawia wydajność. Czyni to przez rewriting of prepared statements for INSERT into multi-value inserts when executeBatch() (Source). Oznacza to, że zamiast wysyłać następujące n INSERT do serwera mysql za każdym razem executeBatch() nazywa się:

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

Byłoby wysłać pojedynczą INSERT:

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

można obserwować go przez przełączenie na logi mysql (przez SET global general_log = 1), które logowałyby do pliku każdą instrukcję wysłaną do serwera mysql.

+0

Czy działa dla db2? – Vipin

+0

@Vipin Nie mam pojęcia. – 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

zamiast downwotowania może być komentarz na ten temat, dlaczego może on lub nie może poprawić wydajność podczas wykonywania kilku partii pomiędzy i nie wszystkie na raz ... – benez

Powiązane problemy