2011-09-23 67 views
2

我必須執行大量的插入操作(在本例中爲27k),我希望找到一個最佳的操作。現在這是我有的代碼。正如你所看到的,我正在使用預先準備好的語句和批處理,而且我正在執行每1000個(我也嘗試過使用較少的數字,例如10和100,但時間又長了)。這是從查詢忽略的一件事是,有一個自動生成的ID,如果它是任何事情的問題:使用JDBC和MySQL優化大量插入操作

private void parseIndividualReads(String file, DBAccessor db) { 
    BufferedReader reader; 
    try { 
     Connection con = db.getCon(); 
     PreparedStatement statement = null; 
     statement = con.prepareStatement("INSERT INTO `vgsan01_process_log`.`contigs_and_large_singletons` (`seq_id` ,`length` ,`ws_id` ,`num_of_reads`) VALUES (?, ?, ?, ?)"); 
     long count = 0; 
     reader = new BufferedReader(new FileReader(logDir + "/" + file)); 
     String line; 
     while ((line = reader.readLine()) != null) { 
      if(count != 0 && count % 1000 == 0) 
       statement.executeBatch(); 

      if (line.startsWith(">")) { 
       count++; 
       String res[] = parseHeader(line); 
       statement.setString(1, res[0]); 
       statement.setInt(2, Integer.parseInt(res[1])); 
       statement.setInt(3, id); 
       statement.setInt(4, -1); 
       statement.addBatch(); 
      } 
     } 

     statement.executeBatch(); 
    } catch (FileNotFoundException ex) { 
     Logger.getLogger(VelvetStats.class.getName()).log(Level.SEVERE, "Error opening file: " + file, ex); 
    } catch (IOException ex) { 
     Logger.getLogger(VelvetStats.class.getName()).log(Level.SEVERE, "Error reading from file: " + file, ex); 
    } catch (SQLException ex) { 
     Logger.getLogger(VelvetStats.class.getName()).log(Level.SEVERE, "Error inserting individual statistics " + file, ex); 
    } 
} 

任何其他提示說明哪些可能爲了加快這一進程而改變。我的意思是單個插入語句並沒有太多的信息 - 我說沒有超過50個字符的所有4列

編輯:

好以下給出我已經重構方法如下意見。加速是巨大的。您甚至可以嘗試使用可能產生更好結果的1000值進行遊戲:

private void parseIndividualReads(String file, DBAccessor db) { 
    BufferedReader reader; 
    PrintWriter writer; 
    try { 
     Connection con = db.getCon(); 
     con.setAutoCommit(false); 
     Statement st = con.createStatement(); 
     StringBuilder sb = new StringBuilder(10000); 

     reader = new BufferedReader(new FileReader(logDir + "/" + file)); 
     writer = new PrintWriter(new BufferedWriter(new FileWriter(logDir + "/velvet-temp-contigs", true)), true); 
     String line; 
     long count = 0; 
     while ((line = reader.readLine()) != null) { 

      if (count != 0 && count % 1000 == 0) { 
       sb.deleteCharAt(sb.length() - 1); 
       st.executeUpdate("INSERT INTO `vgsan01_process_log`.`contigs_and_large_singletons` (`seq_id` ,`length` ,`ws_id` ,`num_of_reads`) VALUES " + sb); 
       sb.delete(0, sb.capacity()); 
       count = 0; 
      } 
      //we basically build a giant VALUES(),(),()... string that we use for insert 
      if (line.startsWith(">")) { 
       count++; 
       String res[] = parseHeader(line); 
       sb.append("('" + res[0] + "','" + res[1] + "','" + id + "','" + "-1'" + "),"); 
      } 
     } 

     //insert all the remaining stuff 
     sb.deleteCharAt(sb.length() - 1); 
     st.executeUpdate("INSERT INTO `vgsan01_process_log`.`contigs_and_large_singletons` (`seq_id` ,`length` ,`ws_id` ,`num_of_reads`) VALUES " + sb); 
     con.commit(); 
    } catch (FileNotFoundException ex) { 
     Logger.getLogger(VelvetStats.class.getName()).log(Level.SEVERE, "Error opening file: " + file, ex); 
    } catch (IOException ex) { 
     Logger.getLogger(VelvetStats.class.getName()).log(Level.SEVERE, "Error reading from file: " + file, ex); 
    } catch (SQLException ex) { 
     Logger.getLogger(VelvetStats.class.getName()).log(Level.SEVERE, "Error working with mysql", ex); 
    } 
} 

回答

3

您有其他解決方案。

  1. 使用來自mySQL Documentation的LOAD DATE INFILE。
  2. 如果您想要在Java中執行此操作,請只使用一個Statement,它將按兩個順序插入1000個值: 「INSERT INTO mytable(col1,col2)VALUES(val1,val2),(val3,val4),... 「

但我會推薦第一個解決方案。

+0

除非數據庫和Web服務器在同一臺物理機器,否則不POS解決方案1。對於解決方案2,速度非常好,但是,您還需要照顧SQL注入。 – Dapeng

+0

1是不可行的,因爲執行插入操作的應用程序在單獨的機器上運行,並且修改上傳文件是不可行的。關於你的第二種選擇 - 這不就是準備好的聲明和批處理方法應該做什麼嗎? – LordDoskias

+0

我認爲這是不同的答案在解決方案2中速度要好得多。我要檢查一下。關於其他的東西,你可以設置connection.setAutoCommit(false);然後connection.commit();跑得更快。 – LaGrandMere

1

做你想做的最快的方法是直接從文件加載(http://dev.mysql.com/doc/refman/5.5/en/load-data.html)。

從文件加載有一些問題 - 首先,文件需要服務器可讀,而情況往往不是這樣。錯誤處理可能是一件痛苦的事,如果文件中的數據不符合模式的期望,最終可能會得到不一致或不完整的數據。

這也取決於實際的瓶頸是什麼 - 如果你插入的表有很多事情發生,你可能會更好地使用插入延遲(http://dev.mysql.com/doc/) refman/5.5/EN /插入-delayed.html)。

關於加快插入官方的說法是在這裏:http://dev.mysql.com/doc/refman/5.5/en/insert-speed.html

1

根據數據的結構,你有你的「執行批處理每1000次迭代」了潛在的錯誤邏輯。

如果以「>」開頭的行的頻率低,那麼你可以有地方會發生以下情況爲許多不必要executeBatch電話的一個實例(載:

line in data file  events in program 
----------------------------------------------------------- 
> some data   (count=999) 
> some more data  (count=1000) 
another line   (execute batch, count=1000) 
more unprocessed  (execute batch, count=1000) 
some more    (execute batch, count=1000) 

所以我會移動if(count != 0 && count % 1000 == 0)內該if (line.startsWith(">"))塊。

注意,我注意到知道這是否可以在數據發生或會是多少一加速而定。

+0

不錯的地方,我不小心碰到了這個bug,並且每次我在if語句中時都會讓count = 0來修復它。 – LordDoskias