by-nc-sa     개발자, DBA가 함께 만들어가는 구루비 지식창고!

비관적 vs. 낙관적 동시성 제어




비관적 vs. 낙관적 동시성 제어

학습 목표
  1. 비관적 동시성 제어(Pessimistic Concurrency Control)와 낙관적 동시성 제어(Optimistic Concurrency Control)의 의미 이해
  2. 비관적, 낙관적 동시성 제어의 구현 사례 이해 및 동시성 저하 예방 방안 이해
    • 많은 경우에 있어서 트랜젝션의 동시성 제어를 개발자가 직접 구현해야 한다
    • DBMS가 제공하는 트랜젝션 고립화 수준 변경 기능을 사용할 수 없는 경우가 많음 : n-Tier 구조 등
  3. ORA_ROWSCN 활용 사례의 이해

(1) 비관적 동시성 제어

사용자들이 같은 데이터를 동시에 수정할 것이다
  • 한 사용자가 데이터를 읽는 시점에 Lock을 걸고 조회/갱신이 완료될 때까지 유지
  • 동시성 저해 우려 존재
예시)
  • 우수고객 대상으로 적립포인트를 추가시켜 주는 이벤트
  • 다양한 실적정보, 산출공식에 의한 적립포인트 계산중 다른 트랜젝션이 고객 레코드 변경시 문제 발생
  • select for update를 사용하여 Lock을 걸어서 데이터가 잘못 갱신되는 문제 방지
    select 적립포인트, 방문횟수, 최근방문일시, 구매실적
      from 고객
     where 고객번호 = :cust_num
       for update nowait ;
    
    -- 새로운 적립포인트 계산
    
    update 고객
       set 적립포인트 = :적립포인트
     where 고객번호 = :cust_num
    
  • wait 또는 nowait 옵션을 사용하여 동시성 저해 방지
    for update nowait --> 대기없이 Exception(ORA-00054)을 던짐
    for update wait 3 --> 3초 대기 후 Exception(ORA-30006)을 던짐
    

(2) 낙관적 동시성 제어

사용자들이 같은 데이터를 동시에 수정하지 않을 것이다
  • 데이터를 읽을때 Lock을 설정하지 않음
  • 데이터 수정 시점에 앞서 읽은 데이터의 변경여부 반드시 체크해야 함
    온라인 쇼핑몰 주문처리 트랜젝션 구현 사례
    insert into 주문
    select :상품코드, :고객ID, :주문일시, :상점번호, ...
      from 상품
     where 상품코드 = :상품코드
       and 가격 = :가격 ; -- 주문을 시작한 시점 가격
    
    if sql%rowcount = 0 then
      alert('상품가격이 변경되었습니다.');
    end if;
    
  • Lock이 유지되는 시간이 매우 짧아져 동시성을 높이는데 유리
  • '동시성 제어 없는 낙관적 프로그래밍' 우려
예시1)
  • 고객번호를 제외한 4개의 컬럼을 참조한 경우
  • select문에서 읽은 컬럼들이 많은 경우 update문에 조건절을 일일이 기술하는 번거로움 존재
    select 적립포인트, 방문횟수, 최근방문일시, 구매실적
      into :a, :b, :c, :d
      from 고객
     where 고객번호 = :cust_num;
    
    -- 새로운 적립포인트 계산
    
    update 고객
       set 적립포인트 = :적립포인트
     where 고객번호 = :cust_num
       and 적립포인트 = :a
       and 방문횟수 = :b
       and 최근방문일시 = :c
       and 구매실적 = :d ;
    
    if sql%rowcount = 0 then
      alert('다른 사용자에 의해 변경되었습니다.');
    end if;
    
예시2)
  • update 대상 테이블에 최종변경일시를 관리하여 조건절에 사용시 간단하게 레코드 갱신여부 확인
  • for update 사용으로 동시성이 저하되는 것을 예방
    select 적립포인트, 방문횟수, 최근방문일시, 구매실적, 변경일시
      into :a, :b, :c, :d, :mod_dt
      from 고객
     where 고객번호 = :cust_num;
    
    -- 새로운 적립포인트 계산
    
    select 고객번호
      from 고객
     where 고객번호 = :cust_num
       and 변경일시 = :mod_dt
       for update nowait ; -- 다른 트랜젝션에 의해 설정된 Lock 때문에 동시성이 저하되는 것을 예방
    
    update 고객
       set 적립포인트 = :적립포인트,
           변경일시 = SYSDATE
     where 고객번호 = :cust_num
       and 변경일시 = :mod_dt ; --> 최종 변경일시가 앞서 읽은 값과 같은지 비교
    
    if sql%rowcount = 0 then
      alert('다른 사용자에 의해 변경되었습니다.');
    end if;
    
예시3) ora_rowscn 활용
  • 오라클 10g부터 제공하는 Pseudo 컬럼 ora_rowscn을 활용한다면 Timestamp를 오라클이 직접 관리해 주므로 쉽고 완벽하게 동시성 제어 가능
    select 적립포인트, 방문횟수, 최근방문일시, 구매실적, ora_rowscn
      into :a, :b, :c, :d, :rowscn
      from 고객
     where 고객번호 = :cust_num;
    
    -- 새로운 적립포인트 계산
    
    update 고객
       set 적립포인트 = :적립포인트,
           변경일시 = SYSDATE
     where 고객번호 = :cust_num
       and ora_rowscn = :rowscn ;
    
    if sql%rowcount = 0 then
      alert('다른 사용자에 의해 변경되었습니다.');
    end if;
    
  • ora_rowscn 이라는 Pseudo 컬럼을 이용하면 특정 레코드가 변경된 후 커밋된 시점을 추적할 수 있음
  • 따로 변경일시 컬럼을 만들지 않고도 동시성 제어 가능
  • ora_rowscn을 사용하기 위해서 테이블 생성시 ROWDEPENDENCIES 옵션을 사용해야 함
    create table t
    ROWDEPENDENCIES
    nologging
    as
    select * from scott.emp;
    
    • 기본값은 NOROWDEPENDENCIES인데, 이때는 블록단위로 ora_rowscn이 변경된다.
  • 주의할 점
    • ora_rowscn이 변경일시 컬럼을 대체할 수는 없음(동시성 제어 용도로만 사용)
    • ora_rowscn은 영구히 저장되는 값이지만 이를 시간정보로 변환하는 데 사용되는 매핑정보 테이블(sys.smon_scn_time)의 보관주기는 5일이므로 5일 넘게 지난 ora_rowscn 정보의 Timestamp 값 (SCN_TO_TIMESTAMP(ORA_ROWSCN))을 구하려 하면 에러(ORA-08181) 발생

문서정보

Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.