2010-06-07 2 views
43

Мне нужно вставить пару сотен миллионов записей в mysql db. Я участвую в выпуске 1 миллион за один раз. См. Мой код ниже. Кажется, он медленный. Есть ли способ его оптимизировать?Производительность вставки в 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

Ваш код немного испорчен (и укорочен преждевременно) – Uri

+0

BTW, какой драйвер вы используете? Общий JDBC или соединитель JDBC-Mysql? – Uri

+0

Я использую com.mysql.jdbc.Driver – user157195

ответ

8

Вы можете вставить несколько строк с одной вставкой заявления, делая несколько тысяч, в то время может значительно ускорить процесс, то есть, вместо того, чтобы делать, например, 3 вставки формы INSERT INTO tbl_name (a,b,c) VALUES(1,2,3);, вы делаете INSERT INTO tbl_name (a,b,c) VALUES(1,2,3),(1,2,3),(1,2,3); (Возможно, JDBC .addBatch() делает подобную оптимизацию сейчас - хотя mysql addBatch раньше была не оптимизирована и просто выдавала отдельные запросы в любом случае - я не знаю, все ли это случай с последними драйверами)

Если вам действительно нужна скорость, загрузите свои данные из файла с разделителями-запятыми LOAD DATA INFILE, мы получим ускорение в 7-8 раз, что делает десятки миллионов вставок.

+0

Загрузка данных infile может быть хорошей альтернативой, но мой входной файл требуется очистка, меня интересует только вставка некоторых строк, в которых второй токен соответствует строке (маркеры с разделителями пространства), является ли загрузочная информация достаточно гибкой, чтобы фильтровать строки? – user157195

+3

Я не думаю, что он может фильтровать, но вы можете сами очистить данные, написать новый файл с очищенными данными и загрузить этот файл. – nos

+0

Мои вставки в 10 раз быстрее! – user393274

3

Если:

  1. Это новая таблица, или сумма, подлежащая вставлено больше, то уже вставленных данных
  2. Есть индексы таблицы
  3. Вам не нужен другой доступ к стол во вставке

Тогда ALTER TABLE tbl_name DISABLE KEYS может значительно улучшить скорость ваших вставок. Когда вы закончите, запустите ALTER TABLE tbl_name ENABLE KEYS, чтобы начать создавать индексы, что может занять некоторое время, но не так долго, как делать это для каждой вставки.

1

Вы можете попробовать использовать объект DDBulkLoad.

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

У меня была аналогичная проблема производительности с MySQL и решить ее, установив useServerPrepStmts а также rewriteBatchedStatements свойства в связи URL.

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

Ницца! Я вижу 3-кратное улучшение – Kimble

+4

@ Kimble - так почему бы не принять этот ответ? Спасибо, приятель! Это работает как волшебство! –

+0

OMG! Добавление вышеуказанных параметров в мой URL-адрес подключения ускорило пакетные вставки почти 30 раз. Я не уверен, какие другие последствия имеют эти переменные. Но удивительно! Благодарю. – Keshav

39

Я хотел бы расширить ответ на вопрос Бертиля, поскольку я экспериментировал с параметрами URL-адреса соединения.

rewriteBatchedStatements=true является важным параметром. useServerPrepStmts по умолчанию уже является ложным, и даже изменение его на значение true не влияет на производительность пакетной вставки.

Теперь я думаю, что настало время написать, как rewriteBatchedStatements=true значительно улучшает производительность. Он делает это на rewriting of prepared statements for INSERT into multi-value inserts when executeBatch() (Source). Это означает, что вместо того, чтобы посылать следующие n операторы INSERT в MySQL сервер каждый раз, когда executeBatch() называется:

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

Было бы отправить один оператор INSERT:

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

Вы можете наблюдать его, переключив на ведение журнала mysql (по SET global general_log = 1), в которое будет входить в файл каждый оператор, отправленный на сервер mysql.

+0

Работает ли он на db2? – Vipin

+0

@Vipin Я понятия не имею. – 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

вместо того, чтобы сбрасывать, можно прокомментировать это, почему оно может или не может повысить производительность при выполнении нескольких партий между ними и не сразу. – benez

Смежные вопросы