Oracle - 락에 대하여

Posted 05 17, 2012 14:06, Filed under: DataBase/Oracle


# 락은 무엇인가?

락(lock)은 공유 자원에 대한 동시 액세스를 조절하기 위한 메커니즘이다.
'데이터베이스 로우' 대신 '공유 자원'이라는 용어를 사용했음에 주목하라.
오라클은 로우 수준에서 테이블 데이터에 락을 걸기도 하지만, 다양한 자원에 대한 동시 액세스를 제공하기 위해 다른 여러 수준에서 락을 사용한다.
예를 들면 저장 프로시저를 실행하는 동안에는 다른 사용자가 그 프로시저를 실행할 권한이 있다 하더라도 프로시저 자체에 락이 걸린다.
데이터베이스에서 락은 이런 공유 자원에 대한 동시 액세스를 허용하기 위해 사용되며, 그와 함께 데이터 무결성과 일관성을 제공한다.

단일 사용자 데이터베이스에서 락은 꼭 필요하지는 않다.
정보를 변경하는 사용자가 단 한명이기 때문이다.
그러나 다수 사용자가 데이터나 데이터 구조를 액세스하고 변경하는 동안에는 동일한 정보를 동시에 변경할 우려가 있으므로 제대로 된 메커니즘을 갖는 것이 매우 중요하다.
이것이 락킹이 중요한 이유다.
......

오라클을 통해 다름 사항들을 배울 것이다.
    * 트랜잭션은 데이터베이스에 관한 모든 것이며, '좋은 것'이다.

    * 적절한 순간이 올 때까지 커밋을 미루어야 한다.
       시스템의 부하를 피하기 위해서 급하게 커밋을 해서는 안된다.
       대용량 트랜잭션을 오랜 시간 수행한다고 해서 시스템에 부하를 주지는 않기 때문이다.
       커밋을 해야 할 때 커밋을 하는 것으로 미리 해서는 안 된다.
       트랜잭션은 단지 비즈니스 로직을 실행할 만큼의 크기면 된다

    * 필요하다면 데이터에 대한 락을 보유하고 있어야 한다.
       락은 사용할 툴이지 피해야 할 것이 아니며, 부족한 자원도 아니다.
       이와 정반대로, 필요한 만큼만 데이터에 락을 보유하고 있어야 한다.
       락은 부족하지 않겠지만 다른 세션이 정보를 수정하는 것을 방해할 수 있다.
   
    * 오라클에서는 로우 수준의 락으로 인한 오버헤드는 없다.
       1개의 로우 락을 가지든 1,000,000개의 로우 락을 가지든 락을 거는 데 사용되는 자원의 수는 똑같다.
       1개의 로우보다는 1,000,000개의 로우를 변경하는 것이 당연히 작업량이 더 많겠지만 로우 1,000,000개에 락을 거는 데 필요한 자원의 수는 로우 1개일 때와 같다.
       고정 상수이기 때문이다.
   
    * '시스템에 더 낫다'는 이류로 결코 락을 에스컬레이션(예:로우 락 대신 테이블을 사용하는 것)해서는 안 된다.
       오라클 시스템 측면에서 락 상승이 더 나을 것도 없다(자원을 덜 쓰거나 하지 않기 때문이다).
       가령 배치 프로세스처럼 테이블 락을 사용할 때가 있다.
       테이블 전체를 수정하는 동안 다른 세션이 로우에 락을 걸지 못하도록 하기 위해서다.
       그런 시스템 측면에서 보면, 로우 락을 할당하는 대신에 테이블 락을 사용한다고 해서 일이 더 쉬워지는 것은 아니다.
       이런 경우, 배치 프로그램에서 필요한 모든 자원에 대한 액세스를 획득하는 것을 보장하기 위해 테이블 락을 사용하는 것이다.

    * 동시성과 일관성은 동시에 달성되어 빠르면서도 정확한 결과를 얻을 수 있다.
       데이터를 쓰는 자와 읽는 자는 서로 블로킹하지 않는다.
       이 점이 오라클과 다른 관계형 데이터베이스 간에 존재하는 근본적 차이점 중 하나다.


락킹 이슈

오라클이 사용하는 다양한 종류의 락을 논의하기 전에 도움이 될 만한 몇 가지 락킹 이슈를 살펴보자.
대부분의 락킹 문제는 오라클의 락킹 매커니즘을 제대로 사용하지 않은(또는 아예 사용조차 하지 않은) 잘못 설계된 애플리케이션에서 발생한다.

Lost Update
lost update는 고질적인 데이터베이스 문제며, 실제로 모든 다중 사용자 컴퓨터 환경에서 발생한다.
간단하게 말하면, lost update는 아래와 같은 단계를 거쳐 순서대로 발생한다.

    1. Session1의 트랜잭션이 로컬 메모리에서 한 로우 데이터를 검색(쿼리)하고, 그것을 최종 사용자 User1에게 보여준다.

    2. Session2에서 다른 트랜잭션이 같은 로우를 검색하고, 다른 최종 사용자 User2에게 그 데이터를 보여준다.

    3. User1은 애플리케이션을 사용해서 해당 로우를 변경한 후 데이터베이스를 수정하고 커밋한다. Session1 트랜잭션은 이제 완료되었다.

    4. User2 또한 해당 로우를 변경하고 데이터베이스를 수정하고 커밋한다. Session2의 트랜잭션도 이제 완료되었다.

이 과정을 'lost update'라고 한다.
왜냐하면 3단계에서 변경한 내용을 잃어버리기 때문이다.

비관적 락킹
비관적 락킹(pessimistic locking)은 상요자가 화면에 있는 값을 변경하기 바로 전에 실행되는 방법이다.
예를 들어 사용자가 화면에서 버튼을 클릭하여 특정한 로우를 선택하고 수정할 의사를 보이면, 그 즉시 로우에 락을 건다.
그 로우 락은 애플리케이션이 데이터베이스에 있는 로우를 변경하고 커밋할 때까지 계속된다.

그러나 비관적 락킹은 커넥션이 유지되는 환경에서만 유용하다.
다시 말해, 애플리케이션이 데이터베이스에 커넥션을 계속 유지할 수 있고, 적어도 트랜잭션이 살아있는 동안에는 해당 커넥션을 사용하는 사용자가 오직 한 명뿐인 환경을 의미한다.
......
애플리케이션은 바인드 변수에 화명 데이터를 제공하고, 이번에는 다른 세션이 수정하는 것을 막기 위해 로우에 락을 걸고 동일한 로우를 다시 쿼리한다.
이 접근 방식을 비과적 락킹(pessimistic locking)이라 한다.
락을 걸지 않으면 로우가 변하지 않은 채 남아 있을까 의심하기 때문에(비관적이므로) 수정하기 전에 로우에 락을 건다.

낙관적 락킹
낙관적 락킹(optimistic locking)은 수정하기 바로 전까지 모든 락킹을 미루는 방법으로, 락을 획득하지 않고 화면 정보를 변경할 것이다.
다른 사용자가 데이터를 변경하지 않을 거라 낙관적으로 생각하므로, 수정 작업을 제대로 했는지 알 수 있는 마지막 순간까지 기다린다.

이 락킹 방법은 모든 환경(클라이언트/서버 환경 또는 n-tier 환경)에서 동작하지만, 수정이 실패할 확률을 증가시킨다.
다시 말해, 사용자가 로우를 수정했는데 데이터가 변경되었다는 것을 알면 사용자는 다시 수정해야 한다.
......
낙관적인 동시성 제어를 구현하는 방법은 많다.
그 중에서 애플리케이션이 자체적으로 로우의 이전 이미지(before images)를 저장하는 방법을 논의했다.
다음 절에서는 다른 두 가지 방법을 알아볼 것이다.

    * 로우의 '버전'을 말해주는 특정 컬럼을 사용하기, 이 특정 컬럼은 데이터베이스 트리거(trigger)나 애플리케이션 코드에 의해 유지된다.

    * 원래의 데이터를 이용하여 계산되는 체크섬(checksum) 또는 해시값을 사용하기

# 버전 컬럼을 이용한 낙관적 락킹
이것은 lost update로부터 보호하고 싶은 각 데이터베이스 테이블에 한 개의 컬럼을 추가하여 간단하게 구현하는 방법이다.
이 컬럼은 일반적으로 NUMBER 타입 또는 DATE/TIMESTAMP 타입이고, 전반적으로 테이블의 로우 트리거를 통해 유지된다.
이 트리거는 로우가 변경될 때마다 NUMBER 컬럼을 증가시키거나 DATE/TIMESTAMP 컬럼을 수정할 책임이 있다.

-Note---------------------------------------------------------------------
필자는 로우 트리거를 통해 버전 컬럼이 유지된다고 말했지만 로우 트리거가 버전 컬럼을 유지하기 위한 최고의 방법이거나 올바른 방법이라고는 생각하지 않는다.
트리거는 정말 필요한 경우를 제외하고는 사용을 자제해야 한다고 생각하기 때문에(이 경우처럼) 개인적으로 UPDATE 문 자체적으로 버전 컬럼을 유지하는 방법을 선호한다.
필자가 트리거를 피하려는 근거를 알고 싶다면 『Oracle Manazine』에 실린 'Trouble With Triggers' 기사를 참고하라.
(http://www.oracle.com/technology/oramag/oracle/08-sep/o58asktom.html)
http://www.oracle.com/technetwork/issue-archive/2008/08-sep/o58asktom-101055.html
---------------------------------------------------------------------------

낙과적인 동시성 제어를 위한 애플리케이션은 모든 컬럼의 이전 이미지 대신에 추가된 버전 컬럼값만 저장하면 된다.
애플리케이션이 update를 요구하면, 데이터베이스에서 이 컬럼값이 초기에 읽었던 값과 일치하는지 확인만 하면 된다.
이 값들이 서로 같으면 로우는 변경되지 않았음을 의미한다.

SCOTT.DEPT 테이블의 복제 테이블을 이용해서 낙관적 락킹을 어떻게 구현하는지 보자.
일단은 다음의 DDL(Data Definition Language)을 사용하여 테이블을 생성한다.
SUNSHINY@ora11g > create table dept
  2  ( deptno     number(2),
  3    dname      varchar2(14),
  4    loc        varchar2(13),
  5    last_mod   timestamp with time zone
  6               default systimestamp
  7               not null,
  8    constraint dept_pk primary key(deptno)
  9  )
 10  /

Table created.

-- 그런 다음, 이 테이블에 SCOTT.DEPT 데이터를 삽입한다.
SUNSHINY@ora11g > insert into dept( deptno, dname, loc )
  2  select deptno, dname, loc
  3    from scott.dept;

4 rows created.

SUNSHINY@ora11g > commit;

Commit complete.

SUNSHINY@ora11g > 


위 코드는 TIMESTAMP WITH TIME ZONE 데이터타입(오라클 9i부터 가능한)의 LAST_MOD 커럼을 추가하여 DEPT 테이블을 생성했다.
LAST_MOD 컬럼은 항상 값 이 존재해야 하므로 NOT NULL로 정의하고 디폴트 값은 현재 시스템 시간이다.

TIMESTAMP 데이터타입은 오라클에서 가장 정밀도가 높은 데이터타입이며, 일반적으로 마이크로세컨드(1초의 백만 분의 일) 단위까지 표현할 수 있다.
사용자가 생각하는 정도의 시간을 필요로 하는 애플리케이션에서는 이 정밀도 수준은 과분하다.
왜냐하면 데이터베이스에서 로우를 검색하고, 사람이 그 로우를 본 후에 그것을 변경하고, 데이터베이스로 다시 update를 보내느 일련의 과정이 1초 안에 모두 일어난다는 것은 비현실적이다.
또한 두 사람이 1초 안에 동일한 로우를 읽고 변경할 확률도 정말 매우 낮다.

다음으로 LAST_MOD 컬럼값을 유지하는 방법이 필요하다.
두 가지 선택이 있을 수 있는데, 하나는 애플리케이션이 로우를 수정할 때 LAST_MOD 컬럼값을 SYSTIMESTAMP로 설정하는 것이고, 또 하나는 트리거/저장 프로시저로 값을 유지하는 것이다.
애플리케이션이 LAST_MOD 컬럼을 유지하는 것이 트리거에 기반을 둔 접근보다는 확실히 더 효율적이다.
그 이유는 트리거는 오라클이 이미 만들어 놓은 부분 외에 추가적인 처리 과정이 필요하기 때문이다.
어쨌든 모든 애플리케이션에서 해당 테이블을 변경하는 로직은 LAST_MOD 컬럼을 일관되게 유지해야 한다는 것을 의미한다.
그래서 각 애플리케이션이 이 컬럼을 유지할 책임이 있다면, LAST_MOD 컬럼이 변경되었는지 확인하고 이 컬럼을 현재 SYSTIMESTAMP으로 설정해야 한다.
예를 들어 애플리케이션이 DEPTNO=10인 로우를 쿼리해보자.
SUNSHINY@ora11g > variable deptno   number
SUNSHINY@ora11g > variable dname    varchar2(14)
SUNSHINY@ora11g > variable loc      varchar2(13)
SUNSHINY@ora11g > variable last_mod varchar2(50)
SUNSHINY@ora11g > 
SUNSHINY@ora11g > begin
  2      :deptno := 10;
  3      select dname, loc, to_char( last_mod, 'DD-MON-YYYY HH.MI.SSXFF AM TZR' )
  4        into :dname,:loc,:last_mod
  5        from dept
  6       where deptno = :deptno;
  7  end;
  8  /

PL/SQL procedure successfully completed.

SUNSHINY@ora11g > select :deptno dno, :dname dname, :loc loc, :last_mod lm
          from dual; 

  DNO        DNAME              LOC             LM
----------------------------------------------------------------------------
   10       ACCOUNTING     NEW YORK     17-MAY-2012 01.36.19.780872 PM +09:00

조회한 결과를 변경하기 위해 다음의 update 문을 사용할 것이다.
마지막 라인은 타임스탬프가 변경되지 않았는지 확인하기 위한 체크를 수행하고, SELECT 문으로부터 저장된 문자열을 TIMESTAMP WITH TIME ZONE 데이터타입으로 변환하기 위해 오라클 함수 TO_TIMESTAMP_TZ(tz는 timezone의 약어)를 이용한다.
부가적으로 UPDATE 문의 라인 3은 LAST_MOD 컬럼을 현재 시각으로 수정한다.
SUNSHINY@ora11g > update dept
  2     set dname = initcap(:dname),
  3         last_mod = systimestamp
  4   where deptno = :deptno
  5     and last_mod = to_timestamp_tz(:last_mod, 'DD-MON-YYYY HH.MI.SSXFF AM TZR' );

1 row updated.

보다시피 한 개의 로우가 수정되었다. 기본키(deptno)로 로우를 수정했고, LAST_MOD 컬럼을 처음 일고 수정을 하는 사이에 다른 세션에 의해 LAST_MOD 컬럼이 변경되었는지 확인했다.
새로운 LAST_MOD 값을 조회하지 않고 동일한 로직을 이용해서 다시 똑같은 로우를 수정하려고 하면 다음의 결과를 보게 될 것이다.
SUNSHINY@ora11g > update dept
  2     set dname = upper(:dname),
  3         last_mod = systimestamp
  4   where deptno = :deptno
  5     and last_mod = to_timestamp_tz(:last_mod, 'DD-MON-YYYY HH.MI.SSXFF AM TZR' );

0 rows updated.


이번에는 LAST_MOD에 관한 조건이 만족되지 않았기 때문에 '0 rows updated' 결과를 리턴했다.
DEPTNO가 10일 로우는 존재하지만, 수정하는 시점의 LAST_MOD 컬럼값이 로우를 쿼리한 시점의 타임스탬프 값과 더 이상 일치하지 않는다.
애플리케이션은 데이터베이스에서 update 전에 데이터가 변경되었다는 것을 알게 되고, 결국은 해당 로우가 update되지 않았다는 사실에 근거해 이제 무엇을 해야 하는지 파악해야 한다.


# 낙과적 락킹인가, 비관적 락킹인가?
그렇다면 어느 방법이 최선의 선택인가?
경험상 오라클에서는 비관적 락킹이 가장 잘 작동하며(다른 데이터베이스에서는 그렇지 않을 가능성 있음), 낙관적 락킹에 비해 많은 이점을 갖고 있지만 클라이언트/서버 커넥션처럼 데이터베이스에 대한 상태 기반의 커넥션을 요구한다.
여러 커넥션에 걸쳐서 락을 유지할 수 없기 때문이다.
오늘날에는 많은 경우에 있어서 비관적 락킹을 실현하기 어렵다.
과거에 수십, 수백의 사용자를 가진 클라이언트/서버 애플리케이션에서는 비관적 락킹은 필자가 선택할 수 있는 유일한 방법이었다.
그러나 오늘날 대부분의 애플리케이션에서는 낙관적 동시성 제어를 권한다.
트랜잭션이 살아 있는 동안 한 개의 커넥션을 독점하는 것은 치러야 할 대가가 너무 크다.

필자는 어느 방법을 더 선호할까?
타임스탬프 컬럼을 이용한 버전 컬럼 방식을 주로 사용한다.
이 방법은 update할 때마다 타임스탬프가 증가흐므로 장기적인 면에서 부가적인 update 정보를 준다.
더 나아가 CPU 측면에서 보면 해시나 체크섬보다 경제적이고, LONG, LONG RAW, CLOB, BLOB 처럼 규모가 큰 컬럼을 처리할 때 해시나 체크섬에서는 잠재적으로 일어날지 모를 문제가 발생하지 않는다.



출처 : 전문가를 위한 오라클 데이터베이스 아키텍처 - 토마스 카이트


※ 위 내용은, 여러 자료를 참고하거나 제가 주관적으로 정리한 것입니다.
   잘못된 정보나 보완이 필요한 부분을, 댓글 또는 메일로 보내주시면 많은 도움이 되겠습니다.
05 17, 2012 14:06 05 17, 2012 14:06


Trackback URL : http://develop.sunshiny.co.kr/trackback/758

Leave a comment

« Previous : 1 : ... 227 : 228 : 229 : 230 : 231 : 232 : 233 : 234 : 235 : ... 648 : Next »

Recent Posts

  1. HDFS - Python Encoding 오류 처리
  2. HP - Vertica ROS Container 관련 오류...
  3. HDFS - Hive 실행시 System Time 오류
  4. HP - Vertica 사용자 쿼리 이력 테이블...
  5. Client에서 HDFS 환경의 데이터 처리시...

Recent Comments

  1. Right away I am ready to do my bre... 골목게임 11 17,
  2. Terrific article! That is the type... 선릉야구장 11 16,
  3. Yes! Finally someone writes about /. / 11 16,
  4. Круто, круто! Некот... карточные игры на... 11 13,
  5. 안녕하세요^^ 배그핵

Recent Trackbacks

  1. master djs bozeman master djs bozeman %M
  2. wedding dj bozeman mt wedding dj bozeman mt 17 11
  3. joes dj bozeman joes dj bozeman 17 11
  4. Mysql - mysql 설치후 Character set... 멀고 가까움이 다르기 때문 %M

Calendar

«   11 2019   »
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30

Bookmarks

  1. 위키피디아
  2. MysqlKorea
  3. 오라클 클럽
  4. API - Java
  5. Apache Hadoop API
  6. Apache Software Foundation
  7. HDFS 생태계 솔루션
  8. DNSBL - Spam Database Lookup
  9. Ready System
  10. Solaris Freeware
  11. Linux-Site
  12. 윈디하나의 솔라나라

Site Stats

TOTAL 2755910 HIT
TODAY 152 HIT
YESTERDAY 638 HIT