2010-06-07 251 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