【问题标题】:Updating multiple rows of single table更新单表的多行
【发布时间】:2013-12-03 15:07:27
【问题描述】:

我需要更新超过 60k 行的表的每一行。 目前我正在这样做:

public void updateRank(Map<Integer, Double> map) {
    Iterator<Map.Entry<Integer, Double>> it = map.entrySet().iterator();
    while (it.hasNext()) {
        Map.Entry<Integer, Double> pairs = (Map.Entry<Integer, Double>) it.next();
        String query = "update profile set rank = " + pairs.getValue()
                + " where profileId = " + pairs.getKey();
        DBUtil.update(query);
        it.remove();
    }
}

仅此方法大约需要 20 多分钟才能完成,我认为每行 (60k) 的数据库都是我认为的原因。(尽管我使用 dbcp 进行连接池,最大活动连接数为 50)

如果我能够通过单个数据库命中来更新行,那就太好了。那可能吗 ?怎么样?

或者有什么其他的方法来改善时间?

【问题讨论】:

标签: java mysql


【解决方案1】:

如果每一行都应该得到一个不同的值,而该值不能从数据库中的现有数据中派生出来,那么您就无法优化整体复杂性。所以不要期望太多的奇迹。

也就是说,您应该开始使用准备好的语句和批处理:

public void updateRank(Map<Integer,Double> map){
    Iterator<Entry<Integer, Double>> it = map.entrySet().iterator();
    String query = "";
    int i = 0;

    Connection connection = getConnection(); // get the DB connection from somewhere
    PreparedStatement stmt = connection.prepareStatement("update profile set rank = ? where profileId = ?");

    while (it.hasNext()) {
        Map.Entry<Integer,Double> pairs = (Map.Entry<Integer,Double>)it.next();
        stmt.setInt(1, pairs.getValue());
        stmt.setDouble(2, pairs.getKey());
        stmt.addBatch(); // this will just collect the data values
        it.remove();
    }       
    stmt.executeBatch(); // this will actually execute the updates all in one
}

这是做什么的:

  1. prepared statement 导致 SQL 解析器只解析 SQL 一次
  2. 批处理最大限度地减少了客户端-服务器-往返,因此不是每次更新都有一个
  3. 客户端和服务器之间的通信被最小化,因为 SQL 只传输一次,并且数据被收集并作为一个数据包(或至少更少的数据包)发送

另外:

  • 请检查数据库列profileId 是否正在使用索引,以便查找相应行的速度足够快
  • 您可以检查您的连接是否设置为自动提交。如果是这样,请尝试禁用自动提交并在更新所有行后显式提交事务。这样,单个更新操作也可以更快。

【讨论】:

    【解决方案2】:

    现在您独立执行每个查询,这会导致巨大的连接开销(即使使用连接池)。而是使用批处理机制一起执行多个查询。

    使用 JDBC(DBCP 显然正在使用)和准备好的语句,您可以使用 addBatch()executeBatch() 轻松完成此操作。我最近不得不自己做这件事,大约 1000 个查询的批量大小是最快的。尽管在您的情况下这可能完全不同。

    参考文献

    【讨论】:

      【解决方案3】:

      您可以连接您的查询(用 ; 分隔它们)并仅发送 100 个查询的批次:

      public void updateRank(Map<Integer, Double> map) {
          Iterator<Map.Entry<Integer, Double>> it = map.entrySet().iterator();
          String queries = "";
          int i = 0;
          while (it.hasNext()) {
              Map.Entry<Integer, Double> pairs =
                      (Map.Entry<Integer, Double>) it.next();
              queries += "update profile set rank = " + pairs.getValue()
                      + " where profileId = " + pairs.getKey() + ";";
              it.remove();
              if (i++ % 100 == 99) {
                  DBUtil.update(queries);
                  queries = "";
              }
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-09-26
        • 1970-01-01
        • 1970-01-01
        • 2020-11-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多