아래 blog 내용 처럼 v$lock을 조회할때 그리 시간이 많이 걸리는 경험은 없었던것 같지만..
v$lock을 조회하면 내부 fixed table join시 'MERGE JOIN CARTESIAN'이 사용되며 여기에 비용이 많이 든다는 군요.
혹시 v$lock query 결과가 늦으시면 ordered hint를 써보심이...


SQL statement for V$LOCK!!! 
select s.inst_id,l.laddr,l.kaddr,s.ksusenum,r.ksqrsidt,r.ksqrsid1, r.ksqrsid2,l.lmode,l.request,l.ctime,decode(l.lmode,0,0,l.block) from v$_lock l, x$ksuse s, x$ksqrs r where l.saddr=s.addr and concat(USERENV('Instance'),l.raddr)=concat(r.inst_id,r.addr) and s.inst_id = USERENV('Instance'); 



library cache lock/pin은 library cache내의 object에 대한 매우 빠른 시간내에 처리되는 lock 메카니즘입니다.

따라서 일반적인 운영상황에서는 library cache lock/pin 등의 contention을 dump 등으로 정보를 수집하기는 엄청어렵죠.
뭐 운영중 DDL을 수행하면 library cache lock/pin을 유발할 순 있겠지만..

다음의 시나리오는 2006년 가을호 오라클 매거진에 있는 hang을 유발하는 시나리오 입니다.
(보고 싶은신 분들은 www.oracle.com에 가면 아마~ 있을 겁니다.)
오라클 매거진에서는 system state dump를 설명하고 있지만, 
요 시나리오를 이용해서 library cache dump를 떠보겠습니다. 

욱짜님의 오라클 블로그에 보면 "library cache pin self deadlock 만들기" 방법도 있습니다..

* 준비 과정

create table employee (empno number,ename varchar2(200), deptno number);
insert into employee values (1,'Alice',10);
commit;
create or replace procedure deleteEmployee(eno number)
is
begin
delete employee where empno = eno;
end;
/

* 시나리오
T1 (SID 110) : delete employee where empno = 1;
T2 (SID 92)  : exec deleteEmployee(1);
T3 (SID 67)  : alter procedure deleteEmployee compile;
T4 (SID 36)  : exec deleteEmployee(2);

alter session set events 'immediate trace name LIBRARY_CACHE level 11';


위와 같이 수행하게 되면 SID 92, 7, 36은 sql prompt가 떨어지지 않고 기다리게 됩니다.
각각 어떤 resource를 기다리고 있을까요?

* wait event
       SID EVENT                                  P1 P1RAW                    P2 P2RAW                    P3
---------- ------------------------------ ---------- ---------------- ---------- ---------------- ----------
        36 library cache lock             4.4044E+12 000004017D44D868 4.4040E+12 0000040163A70D88        201
        67 library cache pin              4.4044E+12 000004017D44D868 4.4040E+12 0000040163D23718        301
        92 enq: TX - row lock contention  1415053318 0000000054580006     131076 0000000000020004      31074
       110 SQL*Net message from client    1650815232 0000000062657100          1 0000000000000001          0

* lock info

ADDR             KADDR                   SID TY        ID1        ID2      LMODE    REQUEST      CTIME      BLOCK
---------------- ---------------- ---------- -- ---------- ---------- ---------- ---------- ---------- ----------
000004017E512348 000004017E512368         92 TX     131076      31074          0          6        144          0
000004017BBA0DC0 000004017BBA0DE8        110 TM     107693          0          3          0        150          0
000004017BBA0EC0 000004017BBA0EE8         92 TM     107693          0          3          0        144          0
000004017BC45998 000004017BC459D0        110 TX     131076      31074          6          0        150          1

wait event를 보면

SID 110이 "delete employee where empno = 1"을 수행해서 employee table에 대한 TM lock과 empno가 1인 row에 대한 TX lock을 획득하고 있습니다. 물론 commit을 하지 않아 그 상태가 계속 유지가 되죠.

SID 92는 "deleteEmployee(1)"을 수행해 empno가 1인 row를 지우려 하지만 이미 해당 row에 대한 lock을 SID 110이 갖고 있어 이를 기다리게 됩니다. 이 상태에서 해당 procedure는 현재 수행 중이기 때문에 library cache pin을 갖고 있게 됩니다.

SID 67은 deleteEmployee procedure를 수정하려 하기 때문에 관련 library cache lock과 pin이 필요하게 됩니다. 그러나 library cache pin은 현재 SID 92에서 갖고 있기 때문에 이를 기다려야 합니다.

SID 36은 "deleteEmployee(2)"를 수행하려 합니다. 그러나 deleteEmployee procedure를 수행하기 위해서는 library cache lock에 대한sahred lock을 할당 받아야 합니다.

우선 wait event에 있는 library cache handle 먼저 찾아 보겠습니다. (library cache lock/pin event는 p1 값이 library cache handle을, p2 값이 각각 library cache lock/pin address를 가리킵니다.)

아래 그림을 보시면 SCOTT.DELETEEMPLOYEE library cache object의 lock과 pin의 holder, waiter 정보를 확인할 수 있습니다. 그리고 DEPENDENCIES 항목을 보면 library cache object의 관련 object들도 list-up 된 것을 확인 할 수 있습니다.



library cache dump 등을 장애때 직접 수행해서 관련 정보를 분석할 일은 별로 없긴 하지만,
이를 통해 조금 더 오라클 내부 구조나 hang/lock 현상 등을 좀더 잘 이해 할 수 있는 과정이 될 수 있지 않을까 생각합니다.




일반적인 HOT block에 따른 현상이 널리 알려진게 'cache buffer chains' 등의 buffer chain에 대한 latch 입니다. 

다음의 SQL은 cache buffers chains children latch 중 가장 sleep count가 높은 children latch의 ADDR에 어떤 block들이 달려 있는지 확인해
그 block 중 가장 동시 접근이 많은 block을 찾는 SQL 입니다. 

select * from (
select HLADDR,TS#,FILE#,DBABLK,TCH , CHILD#,GETS,MISSES,SLEEPS
from x$bh x, 
    (select * from (
     select CHILD#  ,ADDR,GETS,MISSES,SLEEPS  from v$latch_children where name = 'cache buffers chains' order by 5 desc
                   ) 
     where rownum < 2 ) y
where x.hladdr = y.addr
order by tch desc
) where rownum < 2
/





뻔한 script 지만, 나름 쓸만한 shell script 입니다.
shell로 저장한 후 뒤에 sql script와 interval을 주만 무한 루프..
active session 모니터링용으로는 뭐 GUI tool이 부럽지 않죠.. ㅋ

<사용방법>
rpt <sql script name> <interval(sec)>

<내용>
if [ $# -eq 1 ]
then
 arg2=2
elif [ $# -eq 2 ]
then
 arg2=$2
else
     echo "Usage : rpt sql term";
     exit;
fi

arg1=$1
while true
do
sqlplus -s '/ as sysdba' <<EOF
set linesize 200
set pause off
set pagesize 120
@$arg1
EOF
sleep $arg2
done




AWR을 보다 보니 낯선 enqueue 2개가 자주 보여 요거 좀 정리합니다.

RO-Multiple Object Reuse (fast object reuse)

RO enqueue는 "multi object reuse" enqueue로 알려져 있습니다. 이 Enqueue는 foreground process와 background process 간의 sync 하는데 사용되는 enqueue 입니다. Background는 주로 DBWR나 CKPT를 말합니다.

특별히 object drop이나 table truncate 할 때 많이 사용됩니다.

oracle database내에서 truncate나 drop이 발생하면 다음과 같은 내부 작업들이 수행됩니다.

1. foreground process는 먼저 "RO" enqueue를 "execlusive" mode로 요청합니다.
2. 다음은 instance에 작업을 요청하는 cross instance call이 발생되어, CI enqueue가 할당됩니다.
3. 각 instance의 CKPT는 CI call의 요청에 따라 DBWR에서 dirty buffer를 write하게하고 관련 buffer를
   invalidate 합니다.
4. DBWR가 write 작업을 끝내면 foreground process는 "RO" enqueue를 release 합니다.

사실상 이 enqueue 작업은 truncate/drop operation을 순차적으로 수행되기 때문에 자주 drop/truncate가 발생하면 "RO" enqueue contention이 발생할 수 있겠죠..

KO-Multiple Object Checkpoint (fast object checkpoint)

얼마전에 PDML 관련해 posting 한 내용 중 다음과 같은 글을 쓴적 있습니다.

"direct-path read가 발생하면 변경되었거나 disk에 반영되지 않은 데이터를 buffer cache에서 disk로 강제로 flush 한다. 그후 data를 direct path I/O로 읽는다."
Parallel Query Execution

oracle 10g R2이전엔 direct-path read가 발생하면 관련 segment가 저장된 tablespace를 대상으로 flush를 하였습니다. 그러나 oracle 10g R2이후엔 관련 object만 disk로 flush하게 바뀌었습니다.
tablespace 단위로 flush 할 경우 해당 tablespace에 많은 데이터가 저장되어 있거나 한 tablespace에 데이터를 몰아 넣었다면 엄청 불필요한 dirty buffer write가 발생할 수 있겠죠.

KO enqueue는 10g R2 부터 바뀐 이러한 동작에 발생하는 enqueue lock type 입니다. 즉 object에 대한 checkpoint 시 대기할 때 발생하는 enqueue lock입니다.


다음은 일반적인 Enqueue 성능을 확인하는 script 들 입니다.
Enqueue는 v$system_event  v$session_wait  v$enqueue_stat을 통해 각종 statistic 정보를 확인할 수 있습니다.

전체 database system 내의 wait event에 대한 wait 관련 정보

select EVENT,TOTAL_WAITS,TOTAL_TIMEOUTS,TIME_WAITED,AVERAGE_WAIT,WAIT_CLASS
from v$system_event
where wait_class not in ('Idle')
order by 4 desc
/

전체 enqueue lock에 대한 사용 정보
select * from v$enqueue_stat order by 7 desc
/


이 포스트는 다음의 문서를 참고했습니다.

http://ww.orafaq.com/usenet/comp.databases.oracle.server/2006/09/23/1586.htm
Note 286363.1 Truncate Takes A Long Time -- Waits on RO enqueue




 
oracle 운영 중 DB hang 이나 slowdown(거의 hang) 현상이 발생하면, 사실 거의 할 수 있는 일이 없다. monitoring이나 trace 등의 데이터는 resource 부족으로 수행이 잘 안되며, 운영 부서에서 빨리 문제 해결해 달라고 엄청 push 하기 마련이다. 대부분 resource 문제는 database restart하면 풀린다. 물론 recovery 등의 작업은 다시 올리면 또 돌긴하지만 이런 작업은 심각한 hang이나 slowdown 현상을 대개 보이지는 않는다.

암튼 조치 후에 문제 분석을 위해서는 반드시 system state dump를 수행해야 한다. 요게 없으면 문제에 대한 기본적인 분석이 어려워 어떤때는 현상 파악 자체도 잘 안되는 경우가 있다. (10g에서는 active session history나 AWR 등의 데이터로 인해 좀 나아졌다)

system state dump는 몇분 주기로 여러번 수행해야 특정 resource의 변동사항을 유추해 낼 수 있다.
일반적으로 system state dump는 두가지의 방법으로 수행된다.

1.  alter session set events 'immediate trace name SYSTEMSTATE level 10';

2.  $ sqlplus (svrmgrl)
      connect internal
      oradebug setospid <process ID>
      oradebug unlimit
      oradebug dump systemstate 10

드물지만 어떤 경우엔 database로의 접근 자체가 안되는 경우가 발생한다(max process 등..). 이럴 경우 당연히 alter session이나 oradebug를 이용해 system dump 수행 자체가 안된다.

이러한 상황에서 system dump state dump를 수행할 수 있는 방법이 있는데, OS의 debug tool을 이용해 직접 dump를 수행하는 function call을 수행하는 방법과 10g에서 소개된 sqlplus의 -prelim option을 사용하는 방법이 그것이다.

OS debuger를 이용해 function call을 하는 경우 조심할 점은 attach 한 process가 terminated 될 수 있으므로 가급적 oracle background process에 대한 수행은 안하는 게 좋다.

dbx -a PID (where PID = any oracle shadow process)
dbx() print ksudss(10)
...return value printed here
dbx() detach

(saki) % dbx -a 28348
Waiting to attach to process 28348 ...
Successfully attached to oracle.
warning: Directory containing oracle could not be determined.
Apply 'use' command to initialize source path.

Type 'help' for help.
reading symbolic information ...
stopped in read at 0xd016fdf0
0xd016fdf0 (read+0x114) 80410014        lwz   r2,0x14(r1)
(dbx) print ksudss(10)
2
(dbx) detach

oracle 10g를 사용하는 경우 사용할 수 있는 option이 하나 있는데, sqlplus의 prelim option이다.
이 방법은 기존의 oradebug를 사용하는 방법과 동일하며 단지 sqlplus 수행시 option으로 지정하면 된다.
 
prelim option에 관한 내용은 10g, 11g sqlplus manual에도 나와 있지 않으므로.. 생략 !!

sqlplus -prelim / as sysdba

export ORACLE_SID=PROD                                 ## Replace PROD with the SID you want to trace
sqlplus -prelim / as sysdba
oradebug setmypid
oradebug unlimit;
oradebug dump systemstate 10

참고한 자료들 입니다.

Note 121779.1 - Taking Systemstate Dumps when You cannot Connect to Oracle
http://oraclue.com/2008/09/25/sqlplus-preliminary-connection/
http://yaping123.wordpress.com/2008/08/30/sqlplus-using-prelim-connection/

2009년 8월 26일 추가.
sqlplus의 relim option 관련해서 참고할 만한 블로그 입니다.

oradeblog : SQL*Plus 'prelim' connection


2009년 10월 09일 추가
-relim option은 정상적인 세션이 아니기 때문에 systemstate dump 명령이 수행이 되더라도 dump가 끝까지 떨어졌는지 확인이 필요합니다. trace file을 여셔서 맨 끝에 "end of system state dump(?)"라고 되어 있는지 확인해 보세요.





Enqueue는 오라클에서 사용되는 locking 메커니즘이다.
Enqueue는 동시에 여러 프로세스가 기존의 자원에 대해서 다른 정도(degree)로 공유할 수 있는 방법을 제공한다. 

* enqueue 종류

column enqueue format a150
select EQ_TYPE,EQ_NAME||'('||REQ_REASON||') : '||REQ_DESCRIPTION enqueue  from  V$ENQUEUE_STATISTICS;

EQ ENQUEUE
--------------------------------------------------------------------------------------------------------------------------------------------------------
TA Instance Undo(contention) :Serializes operations on undo segments and undo tablespaces
TX Transaction(contention) :Lock held by a transaction to allow other transactions to wait for it
TX Transaction(row lock contention) :Lock held on a particular row by a transaction to prevent other transactions from modifying it
TX Transaction(allocate ITL entry) :Allocating an ITL entry in order to begin a transaction
TX Transaction(index contention) :Lock held on an index during a split to prevent other operations on it
TW Cross-Instance Transaction(contention) :Lock held by one instance to wait for transactions on all instances to finish
US Undo Segment(contention) :Lock held to perform DDL on the undo segment
SU SaveUndo Segment(contention) :Serializes access to SaveUndo Segment
TT Tablespace(contention) :Serializes DDL operations on tablespaces
IM Kti blr lock(contention for blr) :Serializes block recovery for IMU txn
TD KTF map table enqueue(KTF dump entries) :KTF dumping time/scn mappings in SMON_SCN_TIME table
                                                               :
위의 내용처럼 TX lock의 경우 발생할 수 있는 경우가 4가지가 있다.
transaction contention, row lock contention, ITL allocation, index contention.
한개의 enqueue가 한개의 원인일 꺼란 생각은 금물 !!

Enqueue의 가장 대표적인 예가 테이블에 대한 Lock(TM)이라 할 수 있겠다.
즉 하나의 테이블에 대해서 두개의 프로세스가 share 모드나 share update 모드로 Lock을 잡을 수 있다.

Enqueue는 O/S의 locking 메커니즘을 이용하여 사용자가 요구한 lock의 모드에 관한 정보를 갖고  있고 O/S lock 관리자는 Lock에 걸린 자원를 계속해서 추적한다. 만약 어떤 프로세스가  요구한 Lock 모드가 현재 허용될 수 없다면 O/S는 Lock을 요구하는 프로세스를 wait queue에 넣게 된다.

* Lock monitoring

SQL> select * from v$lock where type not in ('MR');

ADDR             KADDR                   SID TY        ID1        ID2      LMODE    REQUEST      CTIME      BLOCK
---------------- ---------------- ---------- -- ---------- ---------- ---------- ---------- ---------- ----------
0000040B410D1DA0 0000040B410D1DC0        879 TS          3          1          3          0      98069          0
0000040B410D1B20 0000040B410D1B40        880 RS         25          1          2          0      98071          0
0000040B410D19E0 0000040B410D1A00        880 CF          0          0          2          0      98074          0
0000040B410D1940 0000040B410D1960        880 XR          4          0          1          0      98074          0
0000040B410D1C60 0000040B410D1C80        881 RT          1          0          6          0      98071          0



Latches 와 Enqueues의 차이는 latch는 wait하기위한 queue의  순서가 없는 반면 enqueue에는 queue의 순서가 있다는 것이다. Latch waiter는 wake up 하기위한 timer를 이용하거나, retry 또는 spin (multiprocessors 환경)을 이용한다. 모든 waiter가 동시에 retry(scheduller dependent)하기 때문에 누구든지 latch를 얻을 수 있고, 어느 경우에는 처음 시도한 프로세스가 가장 나중에 latch를 획득(get) 할 수도 있다.

ENQUEUE_RESOURCES 는 lock manager 에 의해 lock 되는 자원(resource)의 개수를 의미한다. 

이의 초기값은 SESSIONS 파라미터에 의하며, 적절히 부여하기 위해 DML_LOCKS+20 보다 크게 주어야 한다.
예를 들어 3-4 개의 세션(session) 인 경우 default 값은 20 이다.
또 4-10 세션(session) 인 경우 default 값은 ((SESSIONS - 3) * 5) + 20,  
10개 이상의 세션(session)에서는 ((SESSIONS - 10) * 2) + 55 이다.
만일 ENQUEUE_RESOURCES 를 DML_LOCKS + 20 보다 크게 한 경우 그 값이 이용된다.

만일 테이블이 많은 경우 이 값은 증가한다.
이는 한 lock 당 부여되는 것이 아니고, 자원을 사용하는 세션(session)의 개수나 cursor 개수에 관계없이 각 자원(resource) 마다 부여되기 때문이다.

* resource 사용현황

SQL> select * from v$resource_limit;

RESOURCE_NAME                  CURRENT_UTILIZATION MAX_UTILIZATION INITIAL_ALLOCATION   LIMIT_VALUE
------------------------------ ------------------- --------------- -------------------- --------------------
processes                                      180             477        800                  800
sessions                                       173             470        885                  885
enqueue_locks                                   60              81      10750                10750
enqueue_resources                               60              90       4112            UNLIMITED
ges_procs                                        0               0          0                    0





사용자 process가 SGA내의 database buffer cache나 dirty buffer를 포함하는 LRU chain을 scan하기 위한 latch이다.

+ cache buffer handles - buffer cache내의 buffer header에는 다음의 두 가지 list를 포함하고 있다.
 1. user list: doubly linked list로 연결되어 있는 "handle"을 포함한다. 여기에서 handle은 해당 buffer를 사용하고 있는 oracle process를 가리키는 정보를 담고 있다.
 2. waiter list: 이 list도 doubly linked 되어 있는 "handle"의 list를 포함하고 있는데, 여기에서의 handle은 해당 buffer를 사용하기 위해 기다리고 있는 oracle process에 대한 정보를 담고 있다.
cache buffer handle latch를 확보한 뒤, process에 buffer handle을 할당하고 나서 latch를 푼다.

+ cache buffers chains -  foreground process가 buffer를 변경하기 전에 잡아야 하는latch로 복수 사용자에 의해 동시에 변경되는 것을 막아준다. 하나의 latch에 대해서 여러 개의buffer가 DBA를 이용하여 hash되어진다.
    latch contention이 심한 경우, 특정한 particular hash list가 크게 증가하였거나, 하나의 block에 대해서 CR copy가 여러 개 존재하는 경우이다. 다음과 같은 query를 이용하여 그러한 경우인지를 확인한다.
    select dbarfil "File #", dbablk "Block #",count(*)
    from x$bh
    group by dbarfil, dbablk
    having count(*) > 1 ;

+ cache buffers lru chain - LRU list를 보호하기 위한 latch이다. buffer를 이 list에 옮기려면 일단 이 latch를 잡아야 한다. LRU latch이 개수는 {_}db_block_lru_latches로 결정된다. 이 값은 기본적으로 Oracle8.0이전은 CPU_COUNT/2, Oracle8i의 경우 CPU_COUNT, Oracle9i의 경우 CPU_COUNT*4 이다. ( Buffer Cache의 크기에 따라 변경될 수 있다)




Latch는 SGA의 공유 데이터구조를 보호하기 위해서 사용되는 기법으로 빨리 획득되고 풀리는 lock의 일종이다. 일반적으로, Latch는 한 순간에 하나 이상의 프로세스가 동시에 같은 실행코드(code)를 수행하는 것을 방지하는데 사용된다. 그렇게 함으로서 SGA의 공유 데이터 구조를 보호하게 된다.
Latch에는 Wait latch (“willing-to-wait”)와 Nowait latch (immediate) 2종류가 있다. Latch의 모든 항목은 99%이상의 Hit Ratio를 유지해야 한다. 그렇지 않은 경우 Latch Contention이 발생되고 있다고 판단 할 수 있다.

+ Wait latches (“willing-to-wait”)
 - Gets -  Latch획득에 성공한 횟수.
 - Misses -  최초 Latch의 획득을 실패한 횟수.
 - Sleeps  - 최초 Latch의 획득을 실패한 이후로 Latch획득을 시도한 횟수.
 - Latch get hit ratio = (gets - misses) / gets, 이 수치는 1에 가까워야 한다.

+ Nowait latches (immediate)
 - immediate gets - immediate call에 의한 Latch획득에 성공한 횟수.
 - Immediate misses - immediate call에 의한 최초 Latch의 획득을 실패한 횟수.
 - Nowait latches와 관련된 Sleeps은 없다.
 - Nowait hit ratio = (nowait_gets - nowait_misses) / nowait_gets, 이 수치는 또한 1에 가까워야 한다.



'Oracle Database' 카테고리의 다른 글

[tip] 마지막 partition key value 값을 maxvalue로 바꾸기  (0) 2009.04.08
Buffer Cache Latch & Buffer Cache LRU Latch  (0) 2009.04.07
Database Writer (DBWR)  (0) 2009.04.07
Log Writer (LGWR)  (0) 2009.04.07
EXADATA  (0) 2009.03.20

모든 사용자 프로세스는 먼저 Redo Log Buffer가 생성 되어야만 Redo record Block을 변경할 수 있다.  즉, 첫 redo allocation latch를 할당 받고 난 다음 redo copy latch를 요구한다. 사용자 프로세스가 redo log buffer를 할당 받기 위해선 ‘redo allocation latch’ 를 먼저 할당 받고 ‘redo copy latch’를 획득 하여야 한다. ‘redo allocation latch’는 하나의 instance에 단지 하나만이 존재 하며, 'redo copy latch'는 Default로 CPU*2로 설정되어 있다. 다중 사용자 환경에서 이 redo allocation latch에 대한 경합을 줄이는 것(즉 사용자 프로세스당 redo allocation latch의 사용 시간을 최대한으로 줄이는 것)이 성능 향상에 도움이 된다.

l    Redo entry의 기록절차
1.    redo allocation latch획득으로 Log Buffer Position 확보
2.    redo copy latch획득
3.    redo log buffer allocation
4.    release redo allocation latch
5.    redo entry를 redo log buffer로 복제
6.    release redo copy latch



'Oracle Database' 카테고리의 다른 글

Latch  (0) 2009.04.07
Database Writer (DBWR)  (0) 2009.04.07
EXADATA  (0) 2009.03.20
Automatic Optimizer Statistics Collection  (0) 2009.03.20
Oracle 11g New Feature : OLTP table comperssion  (0) 2009.03.09

+ Recent posts