programing

오라클에서 아토믹 MERGE를 수행할 수 있습니까?

javamemo 2023. 6. 29. 19:41
반응형

오라클에서 아토믹 MERGE를 수행할 수 있습니까?

단일 WebLogic 클러스터에서 실행되는 J2EE 앱의 몇 가지 인스턴스가 있습니다.

어느 시점에서 이러한 앱은 병합을 수행하여 백엔드 오라클 데이터베이스에 레코드를 삽입하거나 업데이트합니다.MERGE는 지정된 기본 키를 가진 행이 있는지 확인합니다.있으면 업데이트합니다.그렇지 않으면 삽입합니다.

이제 두 개의 앱 인스턴스가 기본 키가 = 100인 행을 삽입하거나 업데이트하려고 한다고 가정합니다.행이 존재하지 않는다고 가정합니다.병합의 "확인" 단계에서 두 행 모두 해당 행이 없음을 확인하여 두 행 모두 삽입을 시도합니다.그러면 고유한 키 제약 조건 위반이 발생합니다.

제 질문은 다음과 같습니다.오라클에 원자적 MERGE가 있습니까?와 비슷한 효과가 있는 것을 찾고 있습니다.INSERT ... FOR UPDATEPL/SQL에서 SQL만 실행할 수 있습니다.

편집: 저는 확실하지 않았습니다.이 오류가 발생하는 동안 MERGE 문을 사용하고 있습니다.중요한 것은, "수정" 부분만이 원자적이며 전체 병합은 아니라는 것입니다.

이것은 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가 도착하면 잠금을 받을 수 없으므로 즉시 실패합니다.두 번째 사용자에게는 실망스럽지만 적어도 그들은 교수형을 당하지 않으며, 다른 사람이 같은 레코드를 작업하고 있다는 것을 알고 있습니다.

SELECT...와 동일한 INSERT 구문이 없습니다.선택할 항목이 없기 때문에 업데이트를 위해.따라서 MERGE에는 이러한 구문이 없습니다.당신이 해야 할 일은 MERGE를 발행하는 프로그램 유닛에 LOCK TABLE 문을 포함시키는 것입니다.이것이 가능한지 여부는 사용 중인 프레임워크에 따라 다릅니다.

두 번째 세션의 MERGE 문은 해당 세션이 커밋될 때까지 첫 번째 세션이 수행한 삽입을 "확인"할 수 없습니다.트랜잭션의 크기를 줄이면 이러한 현상이 발생할 확률이 줄어듭니다.

또는 주어진 기본 키의 모든 레코드가 동일한 세션에 제공되도록 데이터를 정렬하거나 분할할 수 있습니다.기본 키 mod N과 같은 간단한 기능은 N개의 세션에 균등하게 분배되어야 합니다.

btw, 두 레코드가 동일한 기본 키를 가지고 있으면 두 번째 레코드가 첫 번째 레코드를 덮어씁니다.좀 이상하게 들리네요.

네, 그리고 그것은... MERGE라고 불립니다.

편집: 이 물을 단단히 잠그는 유일한 방법은 삽입하고, dup_val_on_index 예외를 포착하여 적절하게 처리하는 것입니다(업데이트하거나 다른 레코드를 삽입할 수도 있습니다).이것은 PL/SQL로 쉽게 할 수 있지만, 당신은 그것을 사용할 수 없습니다.

또한 해결 방법을 찾고 있습니다.Java에서 dup_val_on_index를 캡처하고 추가 UPDATE를 다시 발행할 수 있습니까?

유사 코드:

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

MERGE가 당신이 설명한 방식으로 행동한다는 것은 놀랍지만, 해야 하는지 말아야 하는지에 대해 말할 만큼 충분히 사용하지 않았습니다.

어떤 경우에도 병합을 실행하려는 트랜잭션의 분리 수준을 SERIABLE로 설정할 수 있습니다.그것이 당신의 문제를 해결할 수 있다고 생각합니다.

언급URL : https://stackoverflow.com/questions/4226830/can-i-do-an-atomic-merge-in-oracle

반응형