【问题标题】:In Oracle SQL update statement, does row update occur concurrently?在Oracle SQL 更新语句中,行更新是否同时发生?
【发布时间】:2012-03-22 02:29:37
【问题描述】:

在Oracle SQL update 语句中,假设更新会影响5 行,那么update 语句是同时更新还是顺序更新所有5 行?例如

UPDATE table1 
set column2 = 'completed' WHERE
index between 1 AND 5

在上述语句中,索引 1 到 5 是按顺序更新的,即 1、2、3、4 然后 5,还是同时发生(1-5 一次)。

我曾提到过Oracle documentation,但似乎没有提到任何内容。

【问题讨论】:

  • 逻辑上,它们同时发生。在实践中,他们没有,但你将无法发现差异。如果你做UPDATE Table1 SET Index = Index + 1 WHERE Index BETWEEN 1 AND 5,它会变得更有趣。

标签: database oracle concurrency sql-update


【解决方案1】:

执行 UPDATE 语句后,该语句的效果将对事务的其余部分(如果您提交,对其他事务)可见。 Oracle 将按照什么顺序进行实际操作,这是一个实现细节(类似地,SELECT 结果的顺序如何不能保证除非您指定 ORDER BY)。


在大多数情况下,此顺序对客户来说并不重要。一种情况可能是避免与正在更新重叠行集的另一个事务发生死锁。 UPDATE 将锁定正在更新的行直到事务结束,因此如果两个事务尝试锁定相同的行,但顺序不同,则可能会发生死锁。

避免死锁的标准方法是始终以明确定义的顺序锁定。不幸的是,UPDATE 没有 ORDER BY 子句,但您可以这样做:

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
SELECT ... WHERE condition ORDER BY ...  FOR UPDATE;
UPDATE ... WHERE condition;
COMMIT;

其中condition 对两个语句都是相同的。可序列化的隔离级别对于WHERE 始终在两个语句中看到相同的行集是必要的。

或者,在 PL/SQL 中你可以这样做:

DECLARE
    CURSOR CUR IS SELECT * FROM YOUR_TABLE WHERE condition ORDER BY ... FOR UPDATE;
BEGIN
    FOR LOCKED_ROW IN CUR LOOP
        UPDATE YOUR_TABLE SET ... WHERE CURRENT OF CUR;
    END LOOP;
END;
/

【讨论】:

    【解决方案2】:

    也有可能。

    在这种情况下,由于您只是更新 5 行,因此并行 DML 不太可能适用。假设UPDATE 不调用并行DML,尽管更新行的顺序是任意的,但行将按顺序更新。 INDEX 1 可能是第一个更新的,最后一个更新的,也可能是中间更新。这取决于查询计划。

    【讨论】:

      【解决方案3】:

      您提供的链接实际上涵盖了这一点。 Oracle 始终强制执行语句级读取一致性——这意味着对 table1 的任何查询都不会返回一些更新的记录,而另一些则不会。无论隔离级别如何,这将是全部或全部。

      【讨论】:

      • 谢谢。为了进一步澄清,假设索引 4 有一个读锁,更新语句是否会等待读锁被解除以更新所有 5 行?
      • @Ted:在 Oracle 中,读取器和写入器不会相互阻塞。更新数据时,旧数据会自动保存一段时间(UNDO)。这允许查询在某个时间点读取数据,而不管发生任何变化。
      • @jonearles 是的,但是写入器会互相阻塞(当他们写入相同的行时)。写入器将逐行锁定,这意味着如果所有事务的写入顺序不同,则可能会出现死锁。由于 UPDATE 没有 ORDER BY 子句,因此必须使用单独的SELECT ... ORDER BY ... FOR UPDATE 来保证锁定顺序。
      【解决方案4】:

      UPDATE、DELETE 和 INSERT 没有定义的顺序。从概念上讲,它们适用于一组并且一次完成。实际上,不要依赖您可能观察到的任何顺序 - 这是一个可以更改的实现细节,并且只会发生在现实世界妨碍理论的情况下。

      【讨论】:

      • 谢谢!我推断更新是基于集合完成的,即更新将在执行更新语句之前等待所有行可用?
      • @Ted:Oracle 会进行块级锁定,但更新开始时不必解锁所有行。
      【解决方案5】:

      所有记录将在一笔交易中更新为一条记录。 Oracle 不保证更新顺序中的任何顺序。

      您可以通过表中的 dbms_transaction.local_transaction_id 值更新任何字段以进行检查。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多