【问题标题】:How do I update thousands of records into MySQL DB in milliseconds如何在几毫秒内将数千条记录更新到 MySQL 数据库中
【发布时间】:2018-11-21 16:45:25
【问题描述】:

我想在一秒钟内将大约 10K 条记录更新到 MySQL 数据库中。我编写了下面的代码,大约需要 6-8 秒才能将记录列表更新到数据库中。

public void updateResultList(List<?> list) {
            String user = "root";
            String pass = "root";
            String jdbcUrl = "jdbc:mysql://12.1.1.1/db_1?useSSL=false";
            String driver = "com.mysql.jdbc.Driver";
            PreparedStatement pstm = null;

            try {
                Class.forName(driver);
                Connection myConn = DriverManager.getConnection(jdbcUrl, user, pass);
                myConn.setAutoCommit(false);
                for(int i=0; i<list.size(); i++) {
                    Object[] row = (Object[]) list.get(i);
                    int candidateID = Integer.valueOf(String.valueOf(row[0]));
                    String result = String.valueOf(row[14]);
                    int score = Integer.valueOf(String.valueOf(row[19]));
                    String uploadState = (String) row[20];

                    String sql = "UPDATE personal_info SET result = ?, score = ?, uploadState = ? "
                                + " WHERE CandidateID = ?";

                    pstm = (PreparedStatement) myConn.prepareStatement(sql);
                    pstm.setString(1, result);
                    pstm.setInt(2, score);
                    pstm.setString(3, uploadState);
                    pstm.setInt(4, candidateID);
                    pstm.addBatch();
                    pstm.executeBatch();

                }
                myConn.commit();
                myConn.setAutoCommit(true);
                pstm.close();
                myConn.close();

            }
            catch (Exception exc) {
                exc.printStackTrace();
                try {
                    throw new ServletException(exc);
                } catch (ServletException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }   
       }

请让我知道您的意见,以优化此代码以提高性能。

【问题讨论】:

  • 尝试在for循环之后调用executeBatch()
  • 我曾尝试在 for 循环外调用它,但它只更新最后一条记录。
  • 提交后为什么要设置 autoCommit 为 true?
  • 因为我一开始设置为false,所以最后默认设置为true。
  • 您还需要将pstm = (PreparedStatement) myConn.prepareStatement(sql);移出循环...

标签: java mysql jdbc


【解决方案1】:

首先,你只需要初始化一次prepareStatement,你需要在for循环之前初始化它

其次,你应该避免为每个循环执行pstm.executeBatch();,这将花费更多的资源,你需要执行它指定的数量,例如100,500或更多,也不要在for循环之外执行它只有一次,因为它会消耗更多的内存资源

Class.forName(driver);
Connection myConn = DriverManager.getConnection(jdbcUrl, user, pass);
myConn.setAutoCommit(false);
String sql = "UPDATE personal_info SET result = ?, score = ?, uploadState = ? "
                + " WHERE CandidateID = ?";
pstm = (PreparedStatement) myConn.prepareStatement(sql);
for(int i=0; i<list.size(); i++) {
    Object[] row = (Object[]) list.get(i);
    int candidateID = Integer.valueOf(String.valueOf(row[0]));
    String result = String.valueOf(row[14]);
    int score = Integer.valueOf(String.valueOf(row[19]));
    String uploadState = (String) row[20];
    pstm.setString(1, result);
    pstm.setInt(2, score);
    pstm.setString(3, uploadState);
    pstm.setInt(4, candidateID);
    pstm.addBatch();
    if(i%500==0){//execute when it meet a specified amount
        pstm.executeBatch();
    }
}
pstm.executeBatch();
myConn.commit();
myConn.setAutoCommit(true);

【讨论】:

  • 感谢您的代码,这有助于减少一些时间。更新 2000 条记录仍然需要大约 5 秒的时间。
  • 500 可能不合适,你需要测试。一个更合适的计数
【解决方案2】:

您可以使用 rewriteBatchedStatements=true 将 INSERT 批处理到临时表中,然后使用单个 UPDATE 语句更新主表,而不是批量处理单个 UPDATE。在我的带有本地 MySQL 实例的机器上,以下代码大约需要 2.5 秒...

long t0 = System.nanoTime();
conn.setAutoCommit(false);

String sql = null;
sql = "UPDATE personal_info SET result=?, score=?, uploadState=? WHERE CandidateID=?";
PreparedStatement ps = conn.prepareStatement(sql);
String tag = "X";
for (int i = 1; i <= 10000; i++) {
    ps.setString(1, String.format("result_%s_%d", tag, i));
    ps.setInt(2, 200000 + i);
    ps.setString(3, String.format("state_%s_%d", tag, i));
    ps.setInt(4, i);
    ps.addBatch();
}
ps.executeBatch();
conn.commit();
System.out.printf("%d ms%n", (System.nanoTime() - t0) / 1000000);

...而这个版本大约需要 1.3 秒:

long t0 = System.nanoTime();
conn.setAutoCommit(false);

String sql = null;
Statement st = conn.createStatement();
st.execute("CREATE TEMPORARY TABLE tmp (CandidateID INT, result VARCHAR(255), score INT, uploadState VARCHAR(255))");
sql = "INSERT INTO tmp (result, score, uploadState, CandidateID) VALUES (?,?,?,?)";
PreparedStatement ps = conn.prepareStatement(sql);
String tag = "Y";
for (int i = 1; i <= 10000; i++) {
    ps.setString(1, String.format("result_%s_%d", tag, i));
    ps.setInt(2, 400000 + i);
    ps.setString(3, String.format("state_%s_%d", tag, i));
    ps.setInt(4, i);
    ps.addBatch();
}
ps.executeBatch();
sql = 
          "UPDATE personal_info pi INNER JOIN tmp ON tmp.CandidateID=pi.CandidateID "
        + "SET pi.result=tmp.result, pi.score=tmp.score, pi.uploadState=tmp.uploadState";
st.execute(sql);
conn.commit();
System.out.printf("%d ms%n", (System.nanoTime() - t0) / 1000000);

【讨论】:

  • 这是迄今为止最好的解决方案。上传 2000 条记录需要 1234 毫秒。感谢您的帮助。
【解决方案3】:

你的pstm.executeBatch() 应该在forloop 之后

参考How to insert List into database

【讨论】:

  • 这只是将最后一条记录更新到数据库中。
  • @JayeshB - 我认为你错了。再试一次。并在最后删除不必要的setAutoCommit(true)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-06-04
  • 1970-01-01
  • 1970-01-01
  • 2012-03-31
  • 1970-01-01
  • 1970-01-01
  • 2018-07-15
相关资源
最近更新 更多