2

MERGE当每个会话对主键列使用不同的值(在下面的片段中显示为 *** )时执行并发时id,如果我在 2 个终端会话中手动执行,一切都很好。

MERGE
 INTO x
   USING (SELECT *** as id FROM DUAL) MERGE_SRC
     ON (x.id = MERGE_SRC.id)
 WHEN MATCHED THEN UPDATE SET val = val + 1 WHERE id = ***
 WHEN NOT MATCHED THEN INSERT VALUES (***, 99);
COMMIT;

但是,使用 3 个或更多线程运行多线程负载测试时,我会相对较快地遇到ORA-08177 with locked table. 这是为什么?(为什么它是不确定的,因为它并不总是在事务重叠时发生?)

该表是使用创建的

create table x (id int primary key, val int);

SQL Server btw 从不使用等效的 MERGE 语句引发异常,运行相同的实验。当同时处理同一行时,情况也是如此。

是不是因为 MERGE 可能不是原子的,并且可序列化模式乐观地运行,所以比赛可能只显示有足够的​​争用?尽管如此,为什么即使不在同一行同时工作也会发生这种情况?

顺便说一句,我尝试使用可用的最严格的锁来解决这个问题是不成功的。因此,非常感谢有关如何使这个原子化的任何想法。看起来放松隔离级别会让我摆脱异常,但是如果在同一行上出现 2 个更新(否则为什么它首先会在可序列化模式下停止),可能会出现不一致的风险。

4

2 回答 2

4

您看到的异常是使用严格序列化的直接后果。如果您同时有多个事务处于活动状态,每个事务都以 SET TRANSACTION ISOLATION LEVEL SERIALIZABLE 开始,当其中任何一个提交时,其他的将得到 ORA-08177。这就是强制执行严格序列化的方式 - 如果另一个事务提交到可序列化会话所需的表中,数据库会在任何以 ISOLATION LEVEL SERIALIZABLE 开始的会话中抛出 ORA-08177。所以,基本上,如果你真的需要严格的序列化,你必须智能地处理 ORA-08177,如下所示:

DECLARE
  bSerializable_trans_complete  BOOLEAN := FALSE;
  excpSerializable              EXCEPTION;
  PRAGMA EXCEPTION_INIT(excpSerializable, -08177);
BEGIN
  <<SERIALIZABLE_LOOP>>
  WHILE NOT bSerializable_trans_complete
  LOOP
    BEGIN
      SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

      MERGE ...; -- or whatever

      COMMIT;

      bSerializable_trans_complete := TRUE;  -- allow SERIALIZABLE_LOOP to exit
    EXCEPTION
      WHEN excpSerializable THEN
        ROLLBACK;
        CONTINUE SERIALIZABLE_LOOP;
    END;
  END LOOP;  -- SERIALIZABLE_LOOP
END;

序列化不是魔术,也不是“免费”(“免费”的意思是“我作为开发人员不需要做任何事情来使其正常工作”)。它需要开发人员进行更多的计划和工作才能使其正常运行,而不是更少。分享和享受。

于 2013-11-12T12:52:24.987 回答
2

在 Oracle 中,该SERIALIZABLE模式以乐观的方式工作,而 SQL Server 则在该模式下进行悲观锁定。这意味着在后一种情况下,您甚至可以同时更改同一行而不会遇到异常。

尽管有文档

仅当其他事务对行所做的更改在可序列化事务开始时已经提交时, Oracle 数据库才允许可序列化事务修改行。当可序列化事务尝试更新或删除由可序列化事务开始后提交的不同事务更改的数据时,数据库会生成错误:

负载测试表明,当不同时在同一行上工作时,也可能会引发异常,尽管不能保证这一点,而在同一行上工作时,总是会导致ORA-08177.

于 2013-11-13T01:05:40.940 回答