【问题标题】:Are explicit commits okay when autocommit is on?启用自动提交时,显式提交可以吗?
【发布时间】:2013-04-02 20:15:28
【问题描述】:

我已经搜索了一段时间,但找不到这个。我正在使用 Oracle 并且有一个类似于以下的 For 循环:

BEGIN
  FOR YEARIDs IN (SELECT DISTINCT YEARID From MyTable)
  LOOP
    UPDATE (
              SELECT    ......
            )
    SET     MyFlag = 1;
    COMMIT;  -- Added
  END LOOP;
END;

AutoCommit 已打开,但在整个 FOR 循环完成之前似乎不会发生提交。因此,我在上面的代码中添加了 Commit 语句。这会导致任何意外结果,还是违反任何最佳实践? (即,当 AutoCommit 打开时,我不应该明确调用提交吗?)

谢谢, 斯科特

编辑:糟糕...我正在使用 Oracle 11g 和 Oracle SQL Developer 作为客户端。

编辑:到目前为止,感谢您的回复。在查询运行的时间点,正在生成和调整数据。不应有其他连接尝试访问数据。至于为什么我经常提交,在开发过程中,我对数据的子集运行查询并且查询运行得很好。该表包含大约 1400 万条记录,我正在测试大约 100k 条记录。该查询相当复杂,针对该子集运行大约 5 分钟。当我开始对整个表运行它时,查询运行了 14 多个小时并且无法更新任何记录。我的理论是持有这么多撤消信息可能会消耗开发服务器上的所有可用资源。如果我经常提交,那么撤消信息可以被释放和重用。是的,它很慢。但是如果查询真的会完成,即使需要一整夜,也可以将其移至测试服务器。 (并且性能调整可以在以后进行。)这个截止日期早就过去了。 (错过最后期限后,我被请来帮忙。我的专业领域不在 Oracle。)

【问题讨论】:

  • 不应该提交在循环之外吗? (在 END LOOP 之后)。
  • 自动提交将在语句完成后进行,在这种情况下,这是整个匿名块;不在for 循环之后。在这种情况下是学术性的,因为无论如何在循环之后都会注意到,但仍然......你为什么要如此频繁地提交?
  • 阅读这篇文章,了解 Oracle 自动提交(或缺少自动提交)的细微差别。 asktom.oracle.com/pls/apex/…。正如其他人所指出的,在每次迭代后提交是一个坏主意,但如果您有一个覆盖这些问题的用例,请在您的问题中分享。
  • 到目前为止,我编辑了原始帖子以解决 cmets。
  • 另外,在您更新后,possibly related question on DBA,其中包括到 AskTom 的链接,例如 this one。如果它用完了撤消,它会告诉你,所以其他事情正在发生;也许更新不是问题,而是您正在更新的视图很慢,也许您有触发器,也许是存档器问题,谁知道呢?看起来你需要做更多的分析,从解释计划开始,看看到底发生了什么。

标签: sql oracle


【解决方案1】:

在循环中提交通常是个坏主意(允许任何工具自动提交也是如此)。

在循环内提​​交会使编写可重新启动的代码变得更加困难。如果在 3 次迭代后遇到错误会发生什么?您现在已经成功提交了 2 个UPDATE 语句的结果。据推测,您需要找出更新了哪些行并编写代码来反转更新,或者您必须添加代码以避免尝试更新这两个成功的 yearid 值的数据。这当然是可能的。但这涉及编写一堆代码来跟踪您的进度,并且通常会使您的代码更加复杂。

在循环内提​​交会使您的代码变慢。提交通常是一项相当昂贵的操作。因此,循环执行它通常是一个坏主意。如果您只有几十个循环迭代,那么问题就不大了。但是,如果您有数百或数千次迭代,您很容易最终将大部分时间都花在提交上。

在循环内提​​交会大大增加导致 ORA-01555 错误的风险。您对MyTable 的查询需要读取一致的数据视图。但是,如果您在循环内提​​交,您就是在告诉 Oracle 您的会话不再需要旧的 UNDO 数据。如果 Oracle 碰巧清除了循环后续迭代所需的 UNDO 数据,则会出现错误。然后你又要处理不可重新启动的代码,你已经成功地经历了 N 次迭代,但你不知道哪些年份已经被处理或哪些需要被处理。

在循环内提​​交会产生数据一致性问题。例如,如果某个其他会话正在运行报告,这些报告很容易看到部分更新的数据,这通常意味着数据将不一致。如果 3 年的数据发生了变化,而其他年份没有发生变化,则很难理解报告的意义,并且人员(或流程)很容易做出错误的决定。

在循环内提​​交也会降低代码的可重用性。如果您的代码包含提交(或回滚,而不是您在块内建立的保存点),则它不能被任何其他不希望其事务提交的代码调用。这会导致人们尝试在没有事务控制的情况下重新实现您的逻辑,或者错误地违反事务完整性,这不可避免地会导致他们构建引入数据一致性问题的应用程序。

【讨论】:

  • 删除撤消信息实际上是我考虑这种方法的原因。 (请参阅我的第二次编辑,在原始帖子中。)我知道这不是好的形式。但我越来越绝望了。针对完整数据集的每个测试都失败了,并且需要数小时才能运行。我当然愿意接受其他建议。 (感谢您的回复。)
  • @DeadZone - 与上述 Alex Poole 的评论相呼应,如果您用完了 UNDO,甲骨文会告诉您。除了磁盘空间之外,保留旧的UNDO 不需要任何费用。所以这似乎不是一个好方法。它肯定不会提高性能。如果您遇到性能问题,您可能需要创建一个新问题,向我们展示查询和查询计划以及您拥有的任何其他信息(例如等待事件)。
【解决方案2】:

DBMS_PARALLEL_EXECUTE 是很棒的功能之一,我们一直在使用具有 mn 行且没有任何中断的大型表。

这可确保高可用性、可重新启动性并利用并行处理。

看到这个 https://oracle-base.com/articles/11g/dbms_parallel_execute_11gR2

我认为在定期间隔(比如 1000 - 10000 行,具体取决于行大小)之后提交数据没有任何问题。事实上,对于大数据迁移/回扫,建议分块并提交以避免'ORA-01555 Snapshot Too Old'。

这也可以防止在基表上锁定更长的时间。

正如其他人所说,这里不需要“自动提交”...

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-07-01
    • 1970-01-01
    • 2012-07-24
    • 1970-01-01
    • 2011-06-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多