【问题标题】:How to make MERGE serializable如何使 MERGE 可序列化
【发布时间】:2013-11-24 05:56:20
【问题描述】:

当每个会话对主键列id 使用不同的值(在下面的 sn-p 中显示为 ***)同时执行并发 MERGEs 时,如果我在 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 顺便说一句,在运行相同的实验时,从不使用等效的 MERGE 语句引发异常。甚至在同时处理同一行时也是如此。

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

顺便说一句,我尝试fix this using the strictest lock available 没有成功。因此,非常感谢有关如何使这个原子化的任何想法。看起来放松隔离级别可以让我摆脱异常,但如果同一行上有 2 个更新,则会有不一致的风险(否则为什么它首先会在可序列化模式下停止)。

【问题讨论】:

  • 您是否尝试过在单个事务中进行更新和插入,而不是使用合并? (合并可能是一个非常古怪的生物......)。
  • 您的意思是updateif rowcount=0 then insert?在可序列化模式下,我想我也必须手动锁定,对吧?
  • 你能解释一下为什么你坚持使用seralization吗?如果会话 A 和 B 大致同时执行,并且您需要每个会话对事务开始时存在的状态进行操作,那么序列化就足够了。这就是它的目的。
  • 序列化足以让异常发生。但是,我希望我的交易能够成功,同时保持一致的状态。在 serializable 模式 中时,我似乎必须使用 @BobJarvis 建议的笨拙循环来运行它,即使我从未在不同会话中同时在同一行上工作。顺便说一句,“序列化就足够了”你的意思是说“通过使用锁进行序列化,而不实际使用隔离模式 serializable”。这肯定是有道理的。

标签: oracle merge atomic


【解决方案1】:

您看到的异常是使用严格序列化的直接后果。如果您同时有多个事务处于活动状态,每个事务都以 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;

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

【讨论】:

  • 感谢您提供乐观并发循环的示例。我最终可能会满足于此。但是,它并不像when any one of [2 simultaneous transactions] commits the others will get an ORA-08177 那样简单。正如我所提到的,在处理不同的行时,它通常工作得很好。
  • 接受您的回答,因为它解决了“如何使合并可序列化”问题。想知道为什么不同行的更改会发生冲突。
【解决方案2】:

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

尽管the docs:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-12-21
    • 2019-11-05
    • 2011-04-15
    • 2020-07-18
    • 1970-01-01
    • 2013-01-20
    相关资源
    最近更新 更多