我可以在Oracle中进行原子MERGE吗?

时间:2010-11-19 16:05:00

标签: java oracle concurrency merge weblogic

我在一个WebLogic群集中运行了几个J2EE应用程序实例。

在某些时候,这些应用程序会执行MERGE以将记录插入或更新到后端Oracle数据库中。 MERGE检查是否存在具有指定主键的行。如果它在那里,请更新。如果没有,请插入。

现在假设两个应用实例想要插入或更新主键= 100的行。假设该行不存在。在合并的“检查”阶段,他们都看到行不在那里,所以他们都试图插入。然后我得到一个唯一的密钥约束违规。

我的问题是:Oracle中是否存在原子MERGE?我正在寻找与PL / SQL中的INSERT ... FOR UPDATE具有类似效果的东西,除了我只能从我的应用程序执行SQL。

编辑:我不清楚。我在使用MERGE语句时仍然出现此错误。问题是,只有“修改”部分是原子的,而不是整个合并。

4 个答案:

答案 0 :(得分:15)

这对MERGE来说不是问题。相反,问题在于您的应用程序。考虑这个存储过程:

create or replace procedure upsert_t23 
    ( p_id in t23.id%type
      , p_name in t23.name%type )
is
    cursor c is
        select null 
        from t23
        where id = p_id;
    dummy varchar2(1);
begin
    open c;
    fetch c into dummy;
    if c%notfound then
        insert into t23 
            values (p_id, p_name);
    else
        update t23
             set name = p_name
             where id = p_id;
    end if;
 end;

因此,这是T23上MERGE的PL / SQL等价物。如果两个会话同时调用它会发生什么?

SSN1>  exec upsert_t23(100, 'FOX IN SOCKS')

SSN2>  exec upsert_t23(100, 'MR KNOX')

SSN1首先到达那里,找不到匹配的记录并插入记录。 SSN2到达第二,但在SSN1提交之前,找不到记录,插入记录并挂起,因为SSN1在100的唯一索引节点上有一个锁定。当SSN1提交SSN2时将抛出DUP_VAL_ON_INDEX违规。

MERGE语句以完全相同的方式工作。两个会话都将检查on (t23.id = 100),找不到它并进入INSERT分支。第一个会议将成功,第二个会议将推出ORA-00001。

处理此问题的一种方法是引入悲观锁定。在UPSERT_T23程序开始时,我们锁定表格:

...
lock table t23 in row shared mode nowait;
open c;
...

现在,SSN1到达,抓住锁并继续前进。当SSN2到达时它无法获得锁定,因此它立即失败。这对于第二个用户来说是令人沮丧的,但至少他们没有挂起,而且他们知道其他人正在处理相同的记录。

INSERT没有语法等同于SELECT ... FOR UPDATE,因为没有什么可以选择的。所以MERGE也没有这样的语法。您需要做的是在发出MERGE的程序单元中包含LOCK TABLE语句。这是否可行取决于您使用的框架。

答案 1 :(得分:6)

第二个会话中的MERGE语句不能“看到”第一个会话在该会话提交之前所执行的插入。如果减少交易的大小,那么这种情况发生的可能性就会降低。

或者,您可以对数据进行排序或分区,以便将给定主键的所有记录都提供给同一会话。像“主键mod N”这样的简单函数应该均匀分配给N个会话。

顺便说一句,如果两个记录具有相同的主键,则第二个记录将覆盖第一个。听起来有点奇怪。

答案 2 :(得分:3)

是的,它被称为.... MERGE

编辑:唯一可以解决此问题的方法是插入,捕获dup_val_on_index异常并对其进行适当处理(更新或插入其他记录)。这可以通过PL / SQL轻松完成,但不能使用它。

您还在寻找解决方法。你能在Java中捕获dup_val_on_index并再次发出额外的UPDATE吗?

在伪代码中:

try {
  // MERGE
}
catch (dup_val_on_index) {
  // UPDATE
}

答案 3 :(得分:2)

我很惊讶MERGE会按照你描述的方式行事,但我没有充分利用它来说明它是否应该。

在任何情况下,您可能希望执行合并的事务将其隔离级别设置为SERIALIZABLE。我认为这可以解决您的问题。