使用JDBC和MySQL优化大量插入

时间:2011-09-23 08:17:32

标签: java mysql jdbc insert

我必须执行大量插入(在本例中为27k),我想找到一个最佳的插件。现在这是我的代码。正如你所看到的那样,我正在使用预备语句和批处理,而且我每1000次执行一次(我也尝试使用较少的数字,例如10和100,但时间又是时间了)。从查询中省略的一件事是,如果问题有任何问题,则会有一个自动生成的ID:

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);
    }
}

有关可能更改的内容以加快流程的任何其他提示。我的意思是单个插入语句没有太多信息 - 我说所有4列

不超过50个字符

编辑:

好的,根据给出的建议,我重新构建了如下方法。加速是巨大的。您甚至可以尝试使用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 个答案:

答案 0 :(得分:3)

您还有其他解决方案。

  1. 使用mySQL Documentation中的LOAD DATE INFILE。
  2. 如果您想在Java中执行此操作,请仅使用一个按2顺序插入1000个值的Statement: “INSERT INTO mytable(col1,col2)VALUES(val1,val2),(val3,val4),......”
  3. 但我会推荐第一种解决方案。

答案 1 :(得分:1)

执行您要执行的操作的最快方法是直接从文件加载(http://dev.mysql.com/doc/refman/5.5/en/load-data.html)。

从文件加载存在一些问题 - 首先,文件需要服务器可读,而事实并非如此。错误处理可能很麻烦,如果文件中的数据不符合架构的预期,您最终可能会得到不一致或不完整的数据。

它还取决于实际的瓶颈是什么 - 如果你要插入的表有很多,你可能会更好地使用插入延迟的组合(http://dev.mysql.com/doc/refman /5.5/en/insert-delayed.html)。

关于加速插入的官方路线在这里:http://dev.mysql.com/doc/refman/5.5/en/insert-speed.html

答案 2 :(得分: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(">"))

请注意,我注意到您的数据是否会发生这种情况,或者说它会加速多少。