DFS lock handle

EXEM Knowledge Base

(Dfs lock handle에서 넘어옴)
Jump to: navigation, 찾기

목차

[편집] Basic Info

DFS lock handle은 요청한 Global locklock handle을 기다리고 있는 대기이벤트이다. 이 lock handleglobal lock과 동일하다. lock handle을 획득하면 global lock을 소유한 상태로 lock conversion이나 lock release와 같은 여러 작업을 수행할 수 있다. global lock은 DLM에 의해 관리된다.

DFS는 Distributed File System의 약자로, 오라클의 제품 역사(History)와 관련 있는 용어로 생각된다. 오라클 10g의 RAC 기능이 완성되기 전까지 오라클은 상당한 기간 동안 분산 데이터베이스를 구현해왔으며, 최초의 분산 데이터베이스는 분산 파일 시스템에 기반하고 있었던 것으로 알려져 있다. 이러한 이유로, 아직까지 일부 global lock 경합에서 여전히 DFS 라는 용어를 사용하고 있다.

대부분의 lock에 대한 글로벌 경합은 싱글 인스턴스 환경에서와 같은 대기이벤트 명으로 관찰된다. 가령 글로벌 TM lock 경합은 enq: TM – contention 이벤트로 관찰되며, 글로벌 SQ lock 경합은 enq: SQ – contention 이벤트로 관찰된다. 이들과는 달리 DFS lock handle 대기이벤트로 관찰되는 대표적인 global lock 경합에는 SV lockCI lock 경합이 있다.

[편집] Parameter & Wait Time

[편집] Wait Parameters

DFS lock handle 대기이벤트의 파라미터 정보는 enqueue 대기이벤트와 동일하다.

  • P1 = lock 유형과 요청(requested)모드를 나타낸다. lock 유형은 TX, TM, SQ, SV, CI 등과 같은 lock 유형을 의미한다. 모드는 1(N) ~ 6(X)의 lock 모드를 의미한다. 이 값은 ASCII 형식으로 인코딩되어 있으므로, 다음의 SQL을 사용하여 enqueue명과 요청모드를 확인할 수 있다.
SELECT chr(to_char(bitand(p1,-16777216))/16777215)||
	chr(to_char(bitand(p1, 16711680))/65535) "Lock",
	to_char( bitand(p1, 65535) )    "Mode"
FROM v$session_wait
WHERE event = 'DFS lock handle'

  (32 - bit오라클의 수행 결과)
    SID EVENT                                    P1     P1RAW Lock     Mode
------- --------------------------------- ---------- -------- ---- --------
     24 enqueue                           1415053318 54580006 TX          6

  (64 - bit오라클의 수행 결과)
    SID EVENT                          P1             P1RAW Lock     Mode
------- ---------------------- ---------- ----------------- ---- --------
    132 enqueue                1415053318  0000000054580006 TX          6

64-bit의 경우 앞의 0을 무시하고 마지막 4byte, (32bit는 4byte로 이루어져 있음)에서 상위 2byte는 lock 유형이다. 54580006의 경우 상위 2byte는 0x5458HEX이다. 54는 10진수로 84이고 58은 88이므로 lock 유형은 아래와 같이 알아낼 수 있다.

Select chr(84) || chr(88) from dual;
CH
--
TX

lock모드는 나머지 2byte에 인코딩 되어 있지만 쉽게 알 수 있다. 위의 예에서lock 모드는 0x0006HEX이므로 6이다. Lock 모드는 다음과 같다

Mode ValueDescription
1 Null Mode
2 Sub-Shared
3 Sub-Exclusive
4 Shared
5 Share/Sub-Exclusive
6 Exclusive
  • P2 = ID1. V$LOCK.ID1 컬럼과 같은 의미이다.
  • P3 = ID2. V$LOCK.ID2컬럼과 같은 의미이다.

[편집] Wait Time

세션이 DLM으로 부터 lock handle을 소유할 때까지 loop를 돌며 대기한다. 각 loop 마다 0.5초씩 대기한다

[편집] Check Point & Solution

[편집] Sequence의 Cache Size를 늘려라.

DFS lock handle 이벤트는 OPSRAC 환경에서 버퍼 캐시 동기화를 제외한 row cachelibrary cache의 동기화를 위해 lock을 획득하는 과정에서 대기하는 이벤트이다. 여러 노드간에 시퀀스의 순서를 보장하려면 글로벌하게 lock을 획득해야하고 이 과정에서 DFS lock handle 대기가 발생하게 되는 것이다. SV lock을 획득하는 과정에서 발생하는 DFS lock handle 대기이벤트의 P1, P2 값은 enq: SQ - contention 대기이벤트와 동일하다.(P1=mode+namespace, P2=object#) 따라서 P1 값으로부터 SV lock인지의 여부를 확인할 수 있고, P2 값을 통해 어떤 시퀀스에 대해 대기가 발생하는지 확인할 수 있다. SV lock 경합 문제가 발생하는 경우의 해결책은 SQ lock의 경우와 동일하다. 캐시 사이즈를 적절히 키워주는 것이 유일한 해결책이다

[편집] RAC의 경우 Sequence에 CACHE+NOORDER속성을 부여하라.

RAC와 같은 멀티 노드 환경에서는 시퀀스의 캐시 사이즈가 성능에 미치는 영향은 싱글 노드 환경에서 보다 훨씬 크다. 따라서 가능하면 CACHE + NOORDER 속성을 부여하고 충분한 크기의 캐시 크기를 부여하는 것이 바람직하다. 만일 순서를 보장하는 것이 반드시 필요하다면 CACHE + ORDER 속성을 부여한다. 하지만 이 경우 순서를 보장하기 위해 인스턴스간에 데이터 교환이 끊임없이 발생한다. 이로 인해 NOORODER 속성을 부여한 경우보다 성능면에서 불리하다

[편집] Event Tip

[편집] Sequence속성에 따른 대기 이벤트

한가지 주의할 것은 CACHE 속성을 부여하지 않은 경우에는 ORDER 속성 사용여부나 RAC 환경 여부와 무관하게 항상 row cache lock 이벤트를 대기한다는 것이다. Row cache lock은 글로벌하게 사용가능한 lock이며, 싱글 인스턴스 환경이나 멀티 인스턴스 환경에서 동일하게 사용된다. 시퀀스 생성시 부여한 속성에 따른 대기이벤트를 정리하면 다음과 같다.

  • row cache lock : Sequnece.nextval을 호출하는 과정에서 딕셔너리 정보를 물리적으로 변경하는 경우에 획득한다. NOCACHE 속성을 부여한 시퀀스에서 사용된다.
  • SQ lock : 메모리에 캐시되어 있는 범위안에서 Sequence.nextval을 호출하는 동안 획득한다. CACHE 속성을 부여한 시퀀스에서 사용된다.
  • SV lock : RAC에서 노드간에 순서가 보장된 상태로 Sequence.nextval 을 호출하는 동안 획득한다. CACHE + ORDER 속성을 부여한 시퀀스에서 사용된다.

[편집] row cache lock 경합

캐시(Cache) 속성을 부여하지 않은 시퀀스를 동시에 많은 프로세스가 사용하는 경우 row cache lock 이벤트 대기가 광범위하게 발생할 수 있다. 캐시를 사용하지 않은 시퀀스에 대해 nextval을 호출하면 매번 Dictionary정보가 변경되어야 하기 때문에 row cache lock을 SSX 모드로 획득해야 한다. SSX모드간에는 상호 호환성이 없기 때문에 이 과정에서 경합이 발생하게 된다. Row cache 덤프와 V$ROWCACHE_PARENT 뷰로부터 정확한 객체 정보를 확인하는 방법을 통해 row cache lock의 정보를 알아보자.

SQL> create sequence seq_seq nocache;  -- NOCACHE 속성의 시퀀스를 생성한다.
-- 시퀀스의 Object ID를 얻고, 이를 16진수로 변환한다.  16진수로 변환된 값을 이용해야만 row cache 덤프 내에서 시퀀스에 대한 정보를 참조할 수 있다.

SQL> select object_id from DBA_OBJECTS where object_name = 'SEQ_SEQ';
 OBJECT_ID
 ----------
    107886

SQL> select to_hex(107886) from dual;
TO_HEX(107886)
------------------------------------------
1A56E       <-- 이 값을 이용해 row cache 덤프를 찾아본다.

-- 다음 명령을 사용해서 row cache를 덤프 한다.
SQL> alter session set events 'immediate trace name row_cache level 12'
-- Row Cache 덤프 파일의 내용은 다음과 같다.
------------(덤프 파일 시작)----------------------------------------------
....
BUCKET 104:
  row cache parent object: address=201D46CC cid=13(dc_sequences)
  hash=ba7abee7 typ=9 transaction=00000000 flags=00000002
  own=201D4740[201D4740,201D4740] wat=201D4748[201D4748,201D4748] mode=N
  status=VALID/-/-/-/-/-/-/-/-
  data=
  0001a56e 00020004 000f0002 00020001 000002c1 00000000 00000000 00000000 
  02c10000 00000000 00000000 00000000 00000000 64640ace 64646464 64646464 
  00646464 00800000 00000000 00000000 00000000 00000000 000002c1 00000000 
  00000000 00000000 2d2d0000 2d2d2d2d 2d2d2d2d 2d2d2d2d 2d2d2d2d 2d2d2d2d 
  2d2d2d2d 2d2d2d2d 2d2d2d2d 2d2d2d2d 

----------(덤프 파일 끝)--------------------------------------------------

위의 덤프 파일에서 의미있는 정보는 다음과 같다

이제, nextval을 대량으로 호출하면서 row cache lock이 어떻게 획득되는지 관찰해보자. 아래와 같이 10000번 동안 seq_seq.nextval을 호출한다.

row cache lock을 획득하고 해제하는 시간은 매우 짧으므로 많은 회수를 시도해야 관찰이 가능하다.

SQL> declare
SQL>   v_seq number;
SQL> begin
SQL>   for idx in 1 .. 10000 loop
SQL>     select seq_seq.nextval into v_seq from dual;
SQL>   end loop;
SQL> end;
SQL> /

위의 PL/SQL이 수행되는 동안 덤프를 통해 얻은 address=201D46CC 값을 이용해 V$ROWCACHE_PARENT 뷰 정보를 조회하면, 아래 결과와 같이 SSX(Shared Sub-Exclusive) 모드로 row cache lock을 획득함을 알 수 있다.

SQL> select * from v$rowcache_parent where address='201D46CC';

INDX                          : 2237
HASH                          : 103
ADDRESS                    : 201D46CC
CACHE#                      : 13
CACHE_NAME              : dc_sequences
EXISTENT                    : Y
LOCK_MODE                : 5  <-- Shared Sub-Exclusive Mode
LOCK_REQUEST           : 0
TXN                            : 21EEE5BC

Shared Sub-Exclusive 모드의 의미는 객체 전체에 대해서는 Shared 모드로, 객체의 일부분에 대해서는 Exclusive 모드로 락을 획득하는 것이다. Seqneuce.nextval 호출에 의해 시퀀스 딕셔너리 정보가 변경되는 경우, 시퀀스 자체를 변경시키는 것은 아니고 시퀀스의 "다음값"만을 변경시키는 것이므로 SSX 모드로 row cache lock을 획득하는 것이다.

시퀀스를 제외하고는 row cache의 정보를 이처럼 자주 변경하는 일은 거의 없다. 따라서 row cache lock 대기가 나타날 경우 시퀀스에 NOCACHE 속성이 부여되어 있지는 않은지 확인해봐야 한다. OPS 환경에서 시퀀스의 순서를 완벽하게 보장하기 위해 NOCACHE 속성으로 시퀀스를 생성한 경우에 row cache lock 대기현상이 나타나는 경우가 많다. RAC 환경에서는 CACHE 속성을 사용하면서, 동시에 노드 간에 시퀀스의 순서를 완벽하게 보장하는 것이 가능하다.

[편집] SQ Lock 경합

CACHE 속성이 부여된 시퀀스에 대해 nextval을 호출하는 동안, SQ lock을 SSX 모드로 획득해야 한다. 동시에 많은 세션이 SQ lock을 획득하기 위해 경쟁하는 과정에서 경합이 발생하면 enq: SQ - contention 이벤트를 대기하게 된다. enq: SQ - contention 이벤트의 P2 값은 시퀀스의 오브젝트 아이디이다. 따라서 P2의 값을 이용해 DBA_OBJECTS 뷰와 조인하면 어떤 시퀀스에 대해 대기현상이 발생하는지 알 수 있다.

시퀀스 생성시 부여한 캐시의 크기가 작은 경우에 enq: SQ - contention 대기가 증가하는 경향이 있다. 캐시의 크기가 작은 경우에는 메모리에 미리 캐시된 값이 빠른 속도로 소진되며, 캐시값이 소진된 경우, 딕셔너리 정보를 물리적으로 변경하고 다시 캐시하는 작업을 해야한다. 그 동안 SQ lock을 계속해서 획득해야 하기 때문에 enq: SQ - contention 이벤트 대기 시간이 그만큼 증가하는 것이다. SQ lock 경합에 의한 성능 문제는 CACHE 속성을 크게 해주는 것으로 문제를 해결할 수 있다. 불행하게도 시퀀스 생성시 캐시 크기의 기본값이 20으로 작게 설정되어 있다. 따라서 사용량이 많을 것으로 예상되는 시퀀스를 생성할 때에는 CACHE 값을 1000 이상으로 크게 잡아주는 것이 좋다.

간혹 한꺼번에 많은 세션이 동시에 생성될 때 enq: SQ - contention 이벤트 대기가 발생하는 경우가 있다. 그 이유는 V$SESSION.AUDSID(Auditing session id) 컬럼값이 시퀀스를 이용해 생성되는데서 비롯된다. 오라클은 새로운 세션이 생성되면 SYS.AUDSES$ 라는 이름의 시퀀스의 nextval을 이용해 AUDSID 값을 생성한다. SYS.AUDSES$ 시퀀스의 캐시 크기는 기본값인 20으로 설정되어 있다. 한꺼번에 많은 세션이 동시에 접속하는 경우에는 SYS.AUDSES$ 시퀀스의 캐시 크기를 10000 정도로 크게 늘려줌으로써 enq: SQ - contention 대기문제를 해결할 수 있다.

RAC에서는 시퀀스 생성시 CACHE 속성을 부여한 상태에서 ORDER 속성을 부여하지 않으면 각 노드가 다른 범위의 시퀀스값을 메모리에 캐시한다. 예를 들어 2개의 노드로 이루어진 RAC 환경에서 CACHE 100 속성으로 시퀀스를 생성하는 경우 1번 노드는 1~100 번을 사용하고, 2번 노드는 101~200번을 사용하게 된다. 만일 양 노드간에 모두 순차적으로 증가하게끔 시퀀스를 사용하려면 반드시 아래와 같이 ORDER 속성을 부여해야 한다

SQL> create sequence ordered_sequence cache 100 order;

[편집] SV Lock 경합

SV lock은 Sequence Value lock의 약자로 RAC에서 ORDER 속성의 시퀀스 값을 보호하기 위해 사용되는 lock이다.

CACHE + NOORDER 속성의 시퀀스가 성능 면에서는 가장 유리하다. 하지만, NOORDER 속성의 시퀀스는 RAC 시스템에서 노드 간의 순서를 보장할 수 없다는 단점이 있다. 가령 캐시 크기가 100이고 NOORDER 속성이 부여된 시퀀스를 2 노드로 이루어진 RAC 시스템에서 사용하면 노드 1번에는 1~100 번, 노드 2번에는 101~200 번의 시퀀스 캐시를 사용하게 된다. 따라서 클라이언트가 RAC 시스템에 접속해서 해당 시퀀스를 사용하게 되면 시퀀스 값이 “1, 2, 101, 3, 102, 103”과 같이 양쪽 노드의 캐시 값이 혼재되어 추출된다. 대부분의 상황에서는 이러한 현상이 문제가 되지 않지만, 만일 시퀀스 값이 반드시 순차적으로 추출되어야 한다는 제한 조건이 필요하다면 ORDER 속성을 부여함으로써 이런 현상을 해소할 수 있다. ORDER 속성의 시퀀스에서는 RAC의 모든 노드가 동일한 캐시 값을 보유한다. 즉 노드 1번도 1~100 번, 노드 2번도 1~100번의 시퀀스 캐시를 사용하며, 두 노드는 SV lock을 이용해서 글로벌 시퀀스 동기화를 수행한다.

ORDER 속성을 사용할 경우 시퀀스의 값을 추출할 때마다 SV lock에 대한 글로벌 동기화 작업이 발생하므로 NOORDER 속성을 사용할 때보다 성능 면에서는 상당히 불리하다. 하지만 NOCACHE 속성을 부여할 때보다는 훨씬 성능 면에서는 유리하다. 따라서 RAC 시스템에서는 가능하면 CACHE + NOORDER 속성을 사용하고, 순서 보장이 필요한 경우에 한해 ORDER 속성을 부여하는 것이 좋다. NOCACHE 속성은 가능한 한 사용하지 않아야 한다.

시퀀스 키 값이 누락(Miss) 없이 항상 순차적으로 증가해야 한다는 전제 조건 때문에 NOCACHE 속성의 시퀀스를 사용하는 경우가 종종 있다. 이것은 우선, 성능 면에서 대단히 불행한 선택이라고 할 수 있다. 특히 RAC와 같은 분산 데이터베이스 환경에서는 NOCACHE 속성의 시퀀스가 미치는 악영향은 치명적일 수 있다. 명심할 것은 시퀀스 값의 누락은 절대 피할 수 없다는 것이다. NOCACHE 속성을 부여하더라도 시퀀스 값의 누락은 항상 발생한다. 가령 50만건의 데이터의 키 값을 시퀀스를 이용해서 생성하다가 파일 공간 부족으로 롤백이 발생했다면, 이 50만개의 시퀀스 키 값은 누락된 상태가 된다. 이런 이유로 NOCACHE 속성의 시퀀스는 절대 사용하지 않는 것을 원칙으로 할 것을 권장한다.

CACHE + ORDER 속성이 부여된 시퀀스인 경우에 오라클은 SQ lock이 아닌 SV lock을 사용해서 동기화를 수행한다. 즉, Order속성이 부여된 Sequence에 대해서 nextval을 호출하면 SSX 모드로 SV lock을 획득해야 한다. SV lock을 획득하는 과정에서 경합이 발생할 경우에는 row cache lock 이벤트나 enq: SQ - contention 이벤트와는 전혀 다른 DFS lock handle 이라는 이름의 이벤트를 대기하게 된다. 이런 이유로, V$EVENT_NAME 뷰에는 “enq: SV – contention”과 같은 이름의 이벤트가 존재하지 않는다.

[편집] Analysis Case