2010-06-07 130 views
43

我需要將幾百萬條記錄插入到mysql數據庫中。我一次批量插入100萬。請參閱下面的代碼。它似乎很慢。有什麼辦法可以優化它嗎?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 2010-06-07 21:19:49

+0

順便說一句,你正在使用哪個驅動程序?一般的JDBC或JDBC-Mysql連接器? – Uri 2010-06-07 21:20:15

+0

我正在使用com.mysql.jdbc.Driver – user157195 2010-06-07 21:26:25

回答

8

您可以插入多行與一個INSERT語句,一次做幾千可以大大加快速度,這是不是如做, 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 2010-06-07 22:07:54

+3

我不認爲它可以過濾,但您可以自己清理數據,使用已清理的數據編寫新文件並加載該文件。 – nos 2010-06-09 23:27:45

+0

我的插入速度快了10倍! – user393274 2013-08-09 16:18:39

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和通過在連接URL設置useServerPrepStmtsrewriteBatchedStatements特性解決了這個問題。

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

不錯!我看到了3倍的改進 – Kimble 2012-12-10 11:50:59

+4

@Kimble - 爲什麼不接受這個答案? 謝謝,夥計!這工作像魔術一樣! OMG! – 2013-04-04 08:56:01

+0

添加上面的參數到我的連接URL加快了批量插入接近30倍。我不確定這些變量有什麼其他的影響。但它是驚人的!謝謝。 – Keshav 2013-04-20 05:26:48

39

我想展開Bertil的回答,因爲我一直在試驗連接URL參數。

rewriteBatchedStatements=true是重要參數。 useServerPrepStmts默認情況下已經爲false,即使將其更改爲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 2016-05-11 13:23:55

+0

@Vipin我不知道。 – Eran 2016-05-11 13:33:10

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

而不是downvoting可能會對此發表評論,爲什麼它可以或不可以提高性能時執行多個批次,而不是一次全部... – benez 2017-05-17 14:28:28