【问题标题】:Is bulk update faster than single update in db2?db2 中的批量更新比单次更新快吗?
【发布时间】:2014-02-27 12:37:26
【问题描述】:

我有一个包含 10 列的表,在该表中我有数千/数百万行。 在某些情况下,我想一次更新超过 10K 条记录。目前我的场景代码按顺序工作,

for i in (primary key ids for all records to be updated)
     executeupdate(i)

我的想法不是运行相同的查询 10K 次,而是将所有 id 添加到一个字符串中并运行一个更新查询,例如,

executeupdate(all ids) 

实际的数据库查询可以是这样的,

假设我有主键 ID,例如,

10001,10002,10003,10004,10005

所以在第一种情况下,我的查询会像

update tab1 set status="xyz" where Id="10001"
update tab1 set status="xyz" where Id="10002"
update tab1 set status="xyz" where Id="10003"
update tab1 set status="xyz" where Id="10004"
update tab1 set status="xyz" where Id="10005"

我的批量更新查询会是这样的,

update tab1 set status="xyz" where id in ("10001","10002","10003","10004","10005")

所以我的问题是,我是否会通过批量更新获得任何性能改进(执行时间) 还是每次记录索引扫描和更新的总查询执行时间相同?

注意:我使用 DB2 9.5 作为数据库

谢谢。

【问题讨论】:

  • DB2/z? DB2/LUW? DB2/i?该数据库在许多平台上运行。
  • DB2/LUW 是我正在使用的。
  • 如果您处理的是 range 的 ids(并且主要将数据更新为相同的值),您可以让它变得更简单; UPDATE tab1 SET status = "xyz" WHERE id >= '10001' AND id < '10006'。或者,如果该列表以前是从其他条件或查询中收集的。你是如何获得 id 列表的?

标签: sql database db2


【解决方案1】:

一般来说,“批量”更新会更快,无论数据库如何。当然,你可以测试两者的性能,然后反馈。

每次调用update 都需要大量开销,包括处理查询、在表/页面/行上设置锁。进行一次更新会合并此开销。

单个update 的缺点是总体上可能更快,它可能会锁定底层资源更长的时间。例如,每次更新可能需要 10 毫秒,其中 1,000 次更新的经过时间为 10 秒。但是,没有任何资源被锁定超过 10 毫秒。批量更新可能需要 5 秒,但资源会在这段时间内被锁定。

为了加快这些更新,请确保 id 已编入索引。

我应该注意。这是一般原则。我没有在 DB2 上专门测试单个与多个更新性能。

【讨论】:

  • 还有一点需要考虑:“批量”更新可能需要更多的日志空间(与每行后提交的单个行更新相比)。
  • @mustaccio 。 . .这是一个很好的观点。您认为这会对性能产生显着影响吗? (我的意思是,如果必须扩展日志文件,可能会有一些额外的开销,但话说回来,多次提交需要更多的日志修改。)
  • 通常你提交的频率越低,性能越好。在每次单行更新后提交将导致每次同步磁盘写入。主日志文件在数据库激活时预先分配,因此您不会产生添加新文件的开销,除非您的事务真的大到需要辅助日志。我的意思是确保总日志空间足以容纳长事务。
【解决方案2】:

您肯定会看到性能提升,因为您将减少往返次数。

但是,这种方法不能很好地扩展;一个语句中的数千个 ID 可能会有点棘手。此外,查询的大小也有限制(可能是 64k)。您可以考虑通过您的表格“分页”并更新 - 例如 - 每个更新语句 100 条记录。

【讨论】:

    【解决方案3】:

    一周前我带着同样的问题来到这里。然后我遇到了一种情况,我必须通过 JDBC 更新 mySQL 数据库中大约 3500 行的表。 我更新了同一个表两次:一次通过 For 循环,通过迭代对象集合,一次使用批量更新查询。以下是我的发现:

    • 当我通过迭代更新数据库中的数据时,大约需要 7.945 秒才能完全执行。
    • 当我想出一个相当巨大的(其中“巨大”表示 183 页长)更新查询并执行相同的操作时,完成更新过程大约需要 2.24 秒。

    显然,批量更新以巨大的优势获胜。

    为什么会有这种差异?

    要回答这个问题,让我们看看查询在 DBMS 中是如何实际执行的。

    与过程语言不同,您指示 DBMS 做什么,而不是如何做。 DBMS 然后执行以下操作。

    • 语法检查,或更通常称为“解析”。解析包括词法分析句法分析语义分析等步骤。
    • 一系列优化(虽然“优化”本身的定义可能因产品而异。至少这是我在网上冲浪时学到的。不过我对此了解不多。)。李>
    • 执行。

    现在,当您逐行更新数据库中的表时,您执行的每个查询都会经过解析、优化和执行。相反,如果您编写一个循环来创建一个相当长的查询,然后执行相同的查询,它只会被解析一次。使用批量更新代替迭代方法节省的时间几乎随更新的行数呈线性增长。

    在更新数据库中的数据时可能会派上用场的一些技巧

    • 在编写 Any 查询时使用索引列作为参考始终是一种很好的做法。
    • 尝试使用整数或数字而不是字符串来排序或搜索数据库中的数据。您的服务器比较两个数字比比较两个字符串要舒服得多。
    • 避免使用视图和“in”子句。它们使您的任务更容易,但会减慢您的数据库速度。改为使用联接。

    【讨论】:

      【解决方案4】:

      如果您使用的是 .NET(并且在其他语言(如 Java)中可能有类似的选项),您可以在名为 BeginChain 的 DB2Connection 类上使用一个选项,这将大大提高性能。

      基本上,当您激活了链选项时,您的 DB2 客户端会将所有命令保存在一个队列中。当您调用EndChain时,队列会立即发送到服务器,并一次性处理。

      文档说这应该比非链式UPDATE/INSERT/DELETEs 的性能要好得多(这是我们在我的商店中看到的),但是您可能需要一些差异要注意:

      • 单个语句不会引发异常。它们将全部集中在一个 DB2Exception 中,该异常将在 DB2Error 属性中包含多个错误。
      • 当链接处于活动状态时,ExecuteNonQuery 将返回 -1

      此外,通过使用带有参数标记的查询而不是单独的单个查询可以进一步提高性能(假设状态也可以改变,否则,您可能只使用文字):

      UPDATE tab1 
      SET status = @status
      WHERE id   = @id
      

      编辑评论:我不确定是在使用 Parameter Markers(它们只是查询中的值的占位符,有关更多详细信息,请参阅链接)或在链式的实际使用。如果是第二个,那么这里有一些示例代码(我没有验证它是否有效,所以使用风险自负:)):

      //Below is a function that returns an open DB2Connection
      //object. It can vary by shop, so put it whatever you do.
      using (var conn = (DB2Connection) GetConnection())
      {
          using (var trans = conn.BeginTransaction())
          {
              var sb = new StringBuilder();
              sb.AppendLine("UPDATE tab1 ");
              sb.AppendLine("   SET status = 'HISTORY' ");
              sb.AppendLine(" WHERE id = @id");
      
              trans.Connection.BeginChain();
      
              using (var cmd = trans.Connection.CreateCommand())
              {
                  cmd.CommandText = sb.ToString();
                  cmd.Transaction = trans;
      
                  foreach (var id in ids)
                  {
                      cmd.Parameters.Clear();
                      cmd.Parameters.Add("@id", id);
                      cmd.ExecuteNonQuery();
                  }    
              }
      
              trans.Connection.EndChain();         
              trans.Commit();
          }
      }
      

      【讨论】:

      • 感谢您的回答,但我没有得到最后一部分,请您详细解释一下。假设我想为所有 id 设置 status = 'HISTORY' 那么查询应该是什么?
      • 我添加了一些额外的信息,如果这能解决一些困惑,请告诉我!
      【解决方案5】:

      我想指出的另一个方面是提交间隔。如果单个更新语句更新了几 10 万行,事务日志也会相应增长,它可能会变慢。我已经看到在使用 ETL 工具(如 informatica)时总时间减少了,与基于条件的单个更新语句一次完成相比,它触发每条记录的更新语句集,然后进行提交。这对我来说是违反直觉的。

      【讨论】:

        猜你喜欢
        • 2010-11-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-05-25
        • 2012-11-14
        • 1970-01-01
        • 2018-07-30
        • 2015-07-09
        相关资源
        最近更新 更多