2011-03-08 3 views
4

대상 총계를 유지하고 기존 값과 새 값의 차이를 반환하는 Oracle 데이터베이스에서 업데이트/삽입을 수행하는 PL/SQL 기능이 있습니다. 다중 스레드는 결코 실패하지와 단위 테스트에서 예상대로이 작동Oracle 및 PL/SQL을 사용한 삽입 또는 업데이트

FUNCTION calcTargetTotal(accountId varchar2, newTotal numeric) RETURN number is 
oldTotal numeric(20,6); 
difference numeric(20,6); 

begin 
    difference := 0; 
    begin 
     select value into oldTotal 
     from target_total 
     WHERE account_id = accountId 
     for update of value; 

     if (oldTotal != newTotal) then 
      update target_total 
      set value = newTotal 
      WHERE account_id = accountId 
      difference := newTotal - oldTotal; 
     end if; 
    exception 
     when NO_DATA_FOUND then 
     begin 
      difference := newTotal; 
      insert into target_total 
       (account_id, value) 
      values 
       (accountId, newTotal); 

     -- sometimes a race condition occurs and this stmt fails 
     -- in those cases try to update again 
     exception 
      when DUP_VAL_ON_INDEX then 
      begin 
       difference := 0; 
       select value into oldTotal 
       from target_total 
       WHERE account_id = accountId 
       for update of value; 

       if (oldTotal != newTotal) then 
        update target_total 
        set value = newTotal 
        WHERE account_id = accountId 
        difference := newTotal - oldTotal; 
       end if; 
      end; 
     end; 
    end; 
    return difference 
end calcTargetTotal; 

:
는 여기에 지금까지 가지고있는 코드입니다. 우리가 본 라이브 시스템에로드 할 때
그러나이 스택 추적이처럼 보이는 실패 :

ORA-01403: no data found 
ORA-00001: unique constraint() violated 
ORA-01403: no data found 

(그들은 문맥 의미가 있기 때문에 내가 제거한) 라인 번호 확인이 첫 번째 업데이트 데이터가 없어서 실패하고, 고유성으로 인해 삽입이 실패하며, 두 번째 업데이트가 불가능한 데이터없이 실패합니다.

다른 스레드에서 읽은 내용은 MERGE 문도 원자 적이지 않으며 비슷한 문제가 발생할 수 있습니다.

아무도 아이디어가 발생하지 않도록 방지하는 방법이 있습니까?

+0

하나의 열 (accountID)에 대한 고유 색인 만 있습니까? 또는 설명을 단순화하기 위해 표시하지 않는 두 번째 열이 있습니까? – redcayuga

+0

고유 제한 조건은 어떻게 정의됩니까? 고유 색인? 명시 적 고유 제한 조건이있는 고유하지 않은 색인? 명시 적 고유 제한 조건이 정의되면 연기가 가능합니까? – redcayuga

답변

1

오라클이 말한 것처럼 불가능한 상황은 아닙니다. 다른 프로세스가 삽입하려하지만 아직 커밋하지 않은 키를 삽입하면 설명 된 동작을 얻을 수 있습니다. 갱신은 삽입 된 레코드를 보지 않지만, 삽입 된 행이 아직 확약되지 않은 경우에도 고유 색인에 중복 값을 추가하려는 시도는 금지됩니다.

유일한 해결책은 커밋되지 않은 삽입이이 테이블에 걸려있는 시간을 최소화하거나 일종의 잠금 체계를 구현하거나 다른 트랜잭션이 완료되지 않을 때까지 기다리는 것입니다 .

1

DCookie에 동의하지 마십시오.

IF 세션 A는 값 "파란색"(고유하게 적용됨)을 삽입 한 다음 세션 B가 값 "파란색"을 삽입하면 세션 B는 세션 A의 잠금을 대기합니다. 세션 A가 커밋하면 세션 B 제약 조건 위반이 발생합니다. 세션 A가 롤백을 수행하면 세션 B가 계속 허용됩니다.

잠재적으로 세션 A가 행을 삽입하고 커밋하고 세션 B가 제약 조건 위반을 가져온 다음 세션 B가 레코드를 업데이트하기 전에 삭제할 행의 매우 작은 범위가 있습니다. 나는 그럴 가능성이 매우 낮다고 판단 할 것이다.

먼저 target_total 테이블에 단 하나의 고유 제한 조건이 있는지 살펴 보겠습니다. 그렇지 않은 경우 어떤 제약 조건이 위반을 일으키는 지 확실히 알고 싶습니다. 또한 고유 한 인덱스 및 제약 조건을 확인하십시오.

데이터 유형이 불일치하거나 간섭하는 트리거가 있는지 확인하십시오. 선택 일치에서 NUMBER (2,0)은 1.1 숫자 값과 같지 않을 수 있지만 삽입시 1.1은 잘려서 1.0으로 절단되어 잠재적으로 제약 조건 위반을 유발할 수 있습니다. 예를 들어, 트리거가 대문자 "BLUE"를 강요했다면, select는 "blue"에서 일치하지 않을 수도 있고, "BLUE"의 중복 키에서 삽입이 실패 할 수도 있으며, 이어지는 삽입 또한 " 푸른".

그런 다음 변수 이름 지정을 확인하십시오. INSERT .... VALUES (식별자)에서 식별자은 PL/SQL 변수 여야합니다.그러나 SELECT * FROM 테이블 WHERE 열 = 식별자이면 식별자은 PL/SQL 변수가 아닌 열 이름이 될 수 있습니다. 열 이름 또는 accountId의 함수가 있으면 같은 이름의 PL/SQL 변수보다 우선합니다. PL/SQL 변수 앞에 접두사를 두어 이름 공간 충돌이 발생하지 않도록하는 것이 좋습니다.

다른 생각은 다중 스레드를 실행 중이므로 스레드가 충돌 할 가능성이 있는지입니다. 스레드가 다른 세션의 잠금을 누를 가능성이있는 라이브 환경에서 이러한 현상이 발생할 수 있습니다. 이로 인해 테스트에서 잘리지 않는 이상한 방식으로 동기화 할 수 있습니다.