오라클 동기화 매커니즘
- 오라클은 거대한 동기화(Synchronization) 머신으로서 수많은 프로세스가 동일 리소스를 사용할 수 있다. 리소스를 보호할 정밀한 {*}{+}동기화 매커니즘{+}{*}이 없다면 리소스의 일관성을 유지하기 어려우므로 오라클은 {*}{+}래치(Latch)와 락(Lock)+{*}이라는 두 개의 동기화 메커니즘을 이용해 리소스를 보호한다.
LATCH?
\- 가벼운 LOCK
\- ORACLE이 자동으로 사용
\- SGA의 Resource들의 동시 Access를 제어하기 위해서 사용(각각의 Resource에 해당하는 Latch를 획득해야만 그 Resource를 사용가능)
\- 작업의 부하를 최소화 하기 위해 래치 획득의 순서를 보장하지 않음
\- 9i이전에는 latch와 관련된 parameter가 수정 가능 하였으나 9i 이상은 hidden parameter로서 수정하지 않아도 됨
NAME VALUE DEFLT TYPE DESCRIPTION
------------------------- ----- --------- ---------- ------------------------------
_db_block_lru_latches 32 TRUE number number of lru latches
EX) LRU latch : LRU List를 통해서 Database Buffer Cache에 올려 놓기 위해 Server Process(A)가 Free Buffer를 LRU list의 LRU end부터 찾는다
만약 Free를 찾으면 일단 "Mark"해 놓고 Server Process는 이 자리로 Physical Read한 Block을 올려 놓을려고 한다.
이때, 다른 Process(B)가 Free Buffer를 찾을려고 LRU list를 검색하고, 같은 자리의 Free Buffer를 확인한 이후 이걸 사용 할수 있다면 작업이 비정상 적으로 처리 될 수 있으므로,
이런 현상을 막기 위해서 LRU latch를 먼저 잡은 Process가 그 Free Buffer를 이용하고, LATCH를 해제한 이후에 다른 사람이 이 Latch를 획득하여 자료를 변경하는 방식으로 처리한다.
- 부모 (Parent) 래치 : 여러개의 자식 래치를 거느리는 래치
- 독립 (Solitary) 래치 : 전체 인스턴스에 단 하나만 존재하는 래치
select latch#, level#,name from v$latch_parent
where latch#=215
LATCH# LEVEL# NAME
---------- ---------- --------------------------------------------------
215 5 library cache
- 자식 (Child) 래치 : 부모 래치에 속한 래치
select latch#, child#, level# from v$latch_children
where latch#=215
LATCH# CHILD# LEVEL#
---------- ---------- ----------
215 1 5
215 2 5
215 3 5
215 4 5
215 5 5
215 6 5
215 7 5
215 8 5
215 9 5
215 10 5
215 11 5
-->Library Cache Latch는 CPU값 보다 큰 소수 중에 가장 작은 값 만큼 Child Latch가 존재
특정 메모리 영역들은 복수개의 Chil Latch를 사용하는데 Child Latch 사용여부는 Memory 영역의 크기보다는 메모리의 구조와 알고리즘을 통해 결정된다.
Shared Pool은 매우 큰 메모리 영역이지만 구조상 하나의 shared pool 래치를 통해서만 메모리 할당이 가능하였지만, 9i부터는 Shared Pool을 여러개로 분할해서 사용가능하며, 이 경우에는 복수개의 shared pool래치를 사용 할 수 있다.
- 단순히 Latch가 사용가능할때 "0", 사용중일때는 "1"로 처리하는 것은 동일하지만 세부적으로 구현방식은 OS에 따라 다름
- CPU가 한개인 SYSTEM인 경우는 필요한 LATCH를 다른 PROCESS가 이미 점유하고 있다면, 이 LATCH를 필요로 하는 다른 PROCESS는 CPU를 해제하고 SLEEP
- CPU가 여러개인 경우는, LATCH를 필요로 하는 PROCESS는 일정 횟수 동안 SPIN하면서 다시 LATCH를 요구하기 위해서 "SPIN"이라는 단계를 거침
얼마만큼 SPIN할 것인지, 몇번 SPIN을 하는 동안에도 LATCH를 얻지 못할 경우 SLEEP 단계로 넘어갈지는 Platform에 따라서 다름
- h4. Latch level
- 래치 획득 과정에서의 데드락을 원척적으로 방지 하기 위해 모든 래치에 레벨을 부여
- 레벨은 0~13까지 14가지의 값이 존재
- 버전마다 다를 수 있으며, Child Latch는 항상 Parent Latch와 동일한 레벨값을 갖음
- 한 개 이상의 래치를 보유한 상태에서 또 다른 래치를 획득하고자 하는 프로세스는 반드시 가장 최근에 획득한 래치보다 높은 레벨의 래치만을 획득해야함
- 어떤 이유로 인해서 현재 보유한 래치보다 낮거나 같은 레벨의 래치를 획득하려면 No-wait 모드로 획득하게 함으로써 데드락을 방지
{tip}만약 1레벨의 래치를 보유한 프로세스는 다음 작업을 위해서 반드시 2레벨이상의 래치만을 요구해야 함.
만일 0레벨의 래치를 필요하면 데드락을 피하기 위해 No-wait 모드로 획득을 시도하고, 이 시도가 실패하면 래치를 해지한 후 다시 0,1의 순서로 획득을 시도 {tip}
- h4. Latch Mode
- 기본적으로 Exclusive 모드를 사용
- 한 순간에 하나의 프로세스 만이 래치를 보유
- cache buffers chains latch는 읽기 작업인 경우에는 Shared 모드를 사용하기도 함
- h4. Willing-to-wait 모드의 래치 획득
- Spin에 의한 Latch 획득
\**## Process A래치 획득 시도 \-> 해당 래치를 점유한 Process가 존재하지 않으므로 래치 획득- Process B 래치 획득 시도 \-> Process A가 래치를 점유하고 있으므로 래치 획득 실패
- Process B는 \_SPIN_COUNT의 수 만큼 스핀을 수행(스핀을 수행한 후 다시 래치 획득 시도. 기본값 : 2000) \-> spin으로 인한 CPU 사용률 증가
- 만일 여러 번의 스핀을 반복한 후에도 래치를 획득하지 못할 경우, Process B는 Sleep 상태로 전환->SPIN 행위 자체가 CPU를 사용하기 대문에, 래치 경합에 의해서 많은 수의 프로세스가 동시에 스핀을 수행할 경우 높은 cpu 사용율을 보일 수 있음 {tip}
- Sleep하지 않고 스핀을 수행하는 이유
- 아주 빠른 시간안에 다른 프로세스가 래치를 해지할 것이라고 기대하기 때문
- 슬립 상태로 빠지게 되면 OS 차원에서 Context Switching이 발생하게 되는데 이 비용이 약간의 CPU를 소모하면서 스핀을 수행하는 것보다 훨씬 비싸기 때문
- Sleep 상태에서 깨어나는 방법은?
- Time-Out이 발생하면 (Latch들은 아주 빠른 시간에 해제된다고 가정 할 경우 사용. 대부분이 이 방식)
Time-Out은 래치 획득에 실패하는 프로세스는 1/100초 단위로 1, 1, 2, 2, 4, 4, 8, 8...의 순으로 대기하며 최대 2초까지 증가 가능한 방식으로 빠른 시간 안에 래치 획득을 재시도하는 과정에서 지나친 스핀으로 인해 CPU를 과다하게 사용하는 것을 방지 - Latch Wait list에 자신을 등록해 놓고 다른 프로세스가 깨워주기를 기다리는 것(Shared pool래치나 Library cache 래치와 같은 일부 래치들은 오랜시간동안 사용되므로 이 방법을 사용. 밑의 Posting 방법에서 다시 설명){tip}
- Process B는 일정시간이 지나면 깨어나서 다시 래치 획득 시도
- Posting에 의한 Latch 획득
\**## Process A 래치 획득 시도 \-> 해당 래치를 점유한 프로세스가 존재하지 않으므로 래치 획득- Process B 래치 획득 시도 \-> Process A가 래치를 점유하고 있으므로 래치 획득 실패
- Process B를 대기목록(wait list)에 등록
- Process A가 자원을 해제할 때 대기목록으로 알려 줌
- 대기목록에서 대기하던 프로세스 중 하나가 래치를 획득
(순서보장을 하지 않음. 대기 목록에서 깨어난 프로세스가 래치를 획득하려고 하는 그 찰나의 순간에 신규로 래치를 획득하고자 하는 다른 프로세스가 래치를 획득 할 수 있기 때문)
\--> Latch wait list도 SGA에 존재하는 Resource이므로 또 다른 래치에 의해 보호 받아야 하므로 Oracle 9i 까지는 래치 대기목록을 보호하기 위해 latch wait list 래치를 사용하지만, oracle 10g 부터는 존재 하지 않음.
- h4. No-wait모드의 래치 획득
- 현재 다른 래치들을 보유하고 있는 프로세스가 현재 보유한 래치중 가장 최근에 획득한 래치보다 더 낮거나 같은 레벨의 래치를 획득하고자 할 때 No-Wait모드로 획득
- 만일 no-wait 모드의 래치획득에 실패하면 획득하고자 하는 래치보다 높은 레벨의 래치는 모두 해제하고 올바른 순서로 다시 시도
{tip}현재 (1,2,4) 레벨의 래치를 보유한 프로세스가 (3) 레벨의 래치를 획득하고자 하면 우선 No-Wait모드로 획득을 시도.만일 성공하면 (1,2,4,3) 레벨의 래치를 보유함.
No-Wait 모드의 시도에서 래치 획득에 실패하면 3보다 낮은 (1, 2) 레벨의 래치만을 남기고 다시 (3,4)의 순서로 재시도. 이 경우는 (1,2,3,4) 레벨의 래치를 보유함.
\--> 재시도 과정에서 DeadkLock발생을 피하기 위해 요청 순서를 재조정 {tip}
- h4. 래치 Cleanup
- 래치 획득 후 SGA 영역을 변경하고자 하는 프로세스는 변경 전에 래치 복구 영역(latch recovery area)에 자신의 목적(공유 메모리를 어떻게 변경할 것인지)에 대한 정보를 기록
- 프로세스가 래치를 보유한 채로 종료되면 PMON은 각 래치마다 구현되어 있는 Cleanup Function을 호출하여 래치 복구 영역에 기록된 정보를 이용하여 SGA 영역을 원래 상태로 복구 하고, PMON은 래치를 해지
- 래치를 획득하기 위해 대기하는 프로세스는 네 번째 재시도에서도 실패하게 되면, PMON에게 래치를 보유한 프로세스가 살아있는지 체크 요청
- 래치를 보유한 프로세스가 죽은 것으로 밝혀지면 Cleanup 과정을 수행하고, 살아 있다면 래치에 실제 경합이 발생하고 있다는 것을 의미
- V$LATCH_MISSES : Latch Misses가 발생한 오라클 커널 코드의 어떤 영역에서 발생했는지의 정보
- V$LATCHHOLDER : Latch Holder 정보
- V$LATCH_PARENT : 부모 Latch 통계
- V$LATCH_CHILDREN : 자식 Latch 통계
- V$LATCH : 래치의 종류별 통계 값
GETS : Willing-to-wait 모드에서 슬립하기 전의 래치 "요청" 회수
MISSES : Willing-to-wait 모드에서의 슬립하기 전의 래치 획득 실패 회수
SPIN_GETS : Willing-to-wait 모드에서 슬립하기 전의 스핀단게에서의 래치 획득 성공 회수
SLEEPS : Willing-to-wait 모드에서의 슬립 회수
IMMEDIATE_GETS : No-wait 모드에서 래치 획득 성공 회수
IMMEDIATE_MISSES : No-wait 모드에서 래치 획득 실패 회수
SLEEP1~SLEEP4 : 1~3회의 슬립회수와 4회 이상의 실립회수. 오라클 10g R2 부터는 사용되지 않으며 V$EVENT_HISTOGRAM뷰로 대체되었다.
WAITERS_WOKEN : latch wait posting을 사용할 경우 세션이 깨어난 회수. 오라클 10gR2 부터는 사용되지 않는다. latch wait list 래치가 없어진 것과 같은 맥락으로 latch wait posting 알고리즘이 완전히 바뀌었음을 의미한다.
WAIT_TIME : 래치를 획득하기 위해 대기한 시간(microsecond 단위)
\--> 가령 1024개의 cache buffers chains 래치 중 특정 번호의 Child 래치에 대해 GETS 요청이 높다면 해당 래치가 관장하는 해시 체인(Hash Chain)에 핫블록(Hot block)이 있을 수 있음을 의미한다.
Willing-to-wait 모드일 경우에는 {*}{+}MISSES/GETS값이{+}{*}, No-wait 모드일 경우에는 {*}{+}IMMEDIATE_MISSES / (IMMEDIATE_GETS + IMMEDIATE_MISSES)+{*} 값이 1% 이상이면 래치 경합으로 판단
WAIT_TIME을 이용해 대기시간이 CPU 시간에 비해 어느 정도 높은 값을 보일 경우 래치 경합이 발생 {info:title=Mutex}10gR2부터 Shared Cursor Operation에 대해 기본적으로 Mutex를 사용하게 되었다.
- Mutex(MUTual EXclusion)란?
- Critical section을 가진 Thread들의 running time이 서로 겹치지 않게, 각각 단독으로 실행되게 하는 기술
Critical section : 프로그램 상에서 동시에 실행될 경우 문제를 일으킬 수 있는 부분.
어느 Thread에서 Critical section을 실행하고 있으면 다른 Thread들을 그 Critical Section에 접근할 수 없고 앞의 Thread가 Critical Section을 벗어나기를 기다려야 한다.
- Mutex의 특징
\-다수의 프로세스가 동일한 리소스를 공유할 수 있게 해줌
\-시스템으로 부터 프로그램이 요청한 리소스를 위한 mutex를 하나 생성하고 시스템에 고유 id를 부여
\-만약 mutex가 이미 다른 쓰레드에 의해 lock이 걸려 있다면 이 쓰레드는 시스템의 대기 queue로 가서 대기
\-latch와 달리 mutex는 시스템이 관장하며 latch spin을 수행하지 않기 때문에 가벼움
\-문제 발생시 Oracle해결범위를 넘어서며 mutex의 경우는 복구가 불가능
- Shared Cursor Operation 이란?
library cache latches, library cache pin latches, library cache pins
http://performeister.tistory.com/tag/oracle%20library%20cache%20pinhttp://exem.tistory.com/34
LOCK이란?
\- Latch보다 무거운 동기화 객체
\- Database와 관련된 Object를 보호하는 동기화 객체
\- Database Level로 작동
- User Type Lock
Enqueye : TM, TX, UL.... - System Type lock
Enqueue : CF, CI, HW, ST...
Row cache lock
Library cache lock, Library cache pin
Buffer Lock
{info:title=Enqueue Lock & 일반 Lock}
- Enqueu Lock이란?
Enqueue구조로 관리되는 락으로 Enqueue 락은 <ResourceType-ID1-ID2>를 리소스로 사용한다.
가령 TM Lock은 하나의 테이블을 보호 하는데 이 테이블을 유일하게 구분하는 리소스 구분자로 <TM-table's Object id-0>(여기서는 Object id가 111)의 값을 사용한다.
하나의 인스턴스에 수많은 Enqueue Lock이 존재 할 수 있기 때문에 Hash Chain 구조를 사용한다.
즉, <ResourceType-ID1-ID2>의 리소스 구분자에 해시함수를 적용해서 버킷(Bucket)을 할당하고, 하나의 버킷은 해시 체인 형태로 여러개의 리소스 구조체를 거느린다.
각 리소스 구조체는 리소스 자체에 대한 정보와 이 리소스에 대한 락을 보유한 프로세스 목록, 대기하고 있는 프로세스 목록, 락 모드를 변경한 프로세스 목록 을 거느린다.
- 일반 Lock이란?
Enqueue Lock과 동일한 목적을 가지면서도 다른 방식으로 구현되는 방식으로 row cache lock이 대표적이다.
row cache lock은 Enqueue 영역에서 통합 관리하지 않고 개별 Row Cache 메모리 영역 자체에 보유 프로세스 목록(Owner List)과 대기 프로세스 목록(Waiter List)를 관리 하는 방식이다.
이 방식으로 작동되는 락은 row cache lock, library cache clok, library cache pin, buffer lock등이 존재
http://wiki.ex-em.com/index.php/Enqueue
- \*# Process A가 락획득 시도 \-> 해당 락을 점유한 프로세스가 존재 하지 않으면 락 획득
- 해당 Object 사용
- Process B가 획득 시도 \-> Process A가 락을 점유하고 있으므로 락 획득 대기
(현재 Lock 보유한 A가 사용이 끝나면 Lock을 해제하고 enqueue 리소스의 대기목록에서 다음 프로세스를 개워준다.
만약 3초가 지난 후에도 Lock을 해제하고 깨워주지 않으면 스스로 깨어난다.
깨어난 프로세스는 DeadLock(이후 설명)이 발생했는지의 여부를 확인한 후 다시 대기 상태로 돌아 간다)
- h3. Lock의 종류
- DML LOCK Type
- Row Level Lock(TX)
- 수정 되는 Row에 걸림
- Exclusive Lock
- Table Level Lock™
- 수정 되는 Table에 걸림
- 서로 다른 Transaction이 서로 다른 row를 수정가능하므로 Shared Lock
Transaction1 | Transaction2 |
---|
SQL> UPDATE emp
SET salary = salary * 1.1
1rows updated | SQL> DROP TABLE emp;
ora-00054 |
SQL> UPDATE emp
SET salary = salary * 1.1
WHERE empno=7788; | SQL> UPDATE emp
SET salary = salary 8 1.1
WHERE empno=7788;
\--> DB Hang |
=> Transaction1의 Row에는 TX Lock이 생성되고, emp 테이블에는 TM Lock이 생성
- DDL LOCK or dictionary locks
- Exclusive DDL Lock
- 독점적으로 Definition 정보를 가짐(Table을 Drop, Alter 하는 경우 어떠한 작업도 수행될 수 없으므로 자동으로 걸림)
- Shared DDL Lock
- 해당 Definition을 공유해서 사용 (만약, EMP에 Procedure를 생성 중, EMP Table이 변경 불가능 하거나, 다른 Procedure를 생성하는 것이 불가능 하면 안돼므로 이 Lock이 자동으로 걸리고, 해당 Procedure 문이 생성완료 되면 Lock이 해제된다.)
- Breakable Parse Locks
- 엄밀히 말하면, Lock이 아니라 Pointer의 개념으로 Library Cache에 Parse 정보를 이용할 수 없다는 Invalidation 됐다는 의미의 Lock
- Table Lock Mode
- Row Excclusive(RX): INSERT, UPDATE, DELETE
- Table Lock이 RX
- 같은 서로 다른 ROW에 대해서 변경가능하나, 같은 ROW에는 변경 불가능 하고, 수동으로 Exclusive Lock을 수동으로 걸지 못하도록 방지
- Row Share(RS): SELECT...FOR UPDATE
- 해당 테이블에 대해서 다른 Transaction이 Exclusive Lock을 수동으로 걸지 못하도록 방지
Transaction1 | Transaction2 |
---|
SQL>select id, salary
from emp
where id=24877
for update; | SQL>lock table employee
in exclusive mode ;
Transaction 2 wait. |
SQL> LOCK TABLE employee IN share MODE;
- 더이상의 DML이 불가
- 9i이상에서는 자동으로 걸리는 경우가 드물었음
{info:title=8i 자동 Shared level Lock}
위와 같이 FK관계로 있을 경우, Dept의 불필요한 부서라고 생각 되는 30번 부서를 delete(delete dept where deptno=30;-) 한 이후
commit or rollback을 고민 하는 중 다른 Transaction에서 30 번 부서의 사원이 insert 될 수도 있었으므로, 8i 까지는
child table 전체에 대해서 share lock이 자동으로 걸리는 Mechanism 이었음. 이유로 emp Table은 DML이 불가 하였음
(이 이유로, 8i까지 DBA들은 FK에 반드시 index를 걸어 Table에 (S) Lock이 걸리지 않는 방법을 수행하였음)
\*9i 동장 Mechanism
EMP의 FK에 Index가 존재 하지 않아도 어떠한 lock도 걸지 않음. dept의 30번 부서 delete작업은 완료 되지 않았다고 하더라고 (S) Mode Lock을 걸지 않음emp Table에 30번 부서를 insert할려고 할 때, emp 테이블에 share lock을 검. 이왕 같은 Reference Integrity시에만 자동으로 수행
- SRX(Share Row Exclusive)
- Shared Lock과 비슷함. DML을 허용 하지 않고, Share Mode로 관련된 lock만 허용
- Reference Integrity시에만 걸림
{tip:title=그럼 S와 SRX의 차이점은?}FK 생성시 on delete cascade option을 지정한 경우에는 Shared Mode다 조금 더 강한 Lock 이 걸림 {tip}
- Exclusive
- 자동으로 수행되지 않음. 수동으로 걸림
- 다른 모든 작업을 막고, 독점적으로 select를 수행할 때 작업
<Lock Mode>
모드 | 설명 |
---|
0 | None |
1 | Null(n) |
2 | Row-S (SS) |
3 | Row-X(SX) |
4 | Share(S) |
5 | S/Row-X (SSX) |
6 | Exclusive (X) |
<Lock 모드의 호환성>
| N | RS | RX | S | SRX | X |
---|
N | O | O | O | O | O | O |
---|
RS | O | O | O | O | O | X |
---|
RX | O | O | O | X | X | X |
---|
S | O | O | X | O | X | X |
---|
SRX | O | O | X | X | X | X |
---|
X | O | X | X | X | X | X | {info}
* h3. DML lock 정보 저장 |
---|
- \*\- 각 변경된 Row에 대한 Transaction 정보를 Block Header의 Transaction Slot에 저장.
- 각 Row에는 Row Header가존재 하고, 이 Row헤더에, 이 Row에 Row chaining또는 Row Migrtion 정보와 몇번 TX Slot에 저장하는 Lock Byte와
이 Row가 몇개의 Column에 대한 값을 가지고 있는지, 길이 Value, 길이 Value 이런식으로 저장이 되어 있다. - Lock Byte는 Transaction이 Row를 수정하는데, 이 Transaction이 몇번 Slot에 저장되어 있는지를 저장하는 것이다.
Transaction 1 | | Transaction 2 |
---|
UPDATE employee
SET salary = salary * 1.1
WHERE empno = 1000; | 9:00 | UPDATE employee
SET manager = 1342
WHERE empno=2000; |
UPDATE employee
SET manager=1234
WHERE empno=2000; | 9:15 | UPDATE employee
SET salary = salary * 1.1
WHERE empno = 1000; |
ORA-00060: deadlock detected while waiting for resource | | |
- \*\- DB에서 자동으로 해당 Transaction을 Rollback;
- Application Code를 수정해 주어야 함
- SID : 락을 보유중이거나 요청 중인 세션의 아이다. LMODE > 0 이면 락을 보유중인 세션이고, REQUEST 0> 이면 락을 요청 중인 세션이다.
- TYPE : Enqueue락의 리소스 타입 (예: TM, TX, UL...)
- ID1 : 리소스 아이디1
- ID2 : 리소스 아이디2
- LMODE : 락을 보유하고 있는 경우의 모드(1~6)
- REQUEST : 락을 요청중인 경우의 모드(1~6)
- CTIME : 현재의 락 모드가 허용된 이후의 시간(SECOND). 즉, 락을 보유하거나 요청한 이후부터의 시간
- BLOCK : 현재의 락이 다른 락을 블로킹하고 있는지의 여부. 1= 다른 락을 블로킹중, 0 = 다른 락을 블로킹 하지 않음
SQL>select sid, type, id1, id2, lmode, request, ctime, block
2 from v$lock
3 ;
SID TYPE ID1 ID2 LMODE REQUEST CTIME BLOCK
---------- ------ ---------- ---------- ---------- ---------- ---------- ----------
1077 TM 13734 0 3 0 45 0
1077 TX 327682 645 6 0 45 1
1078 TM 13734 0 3 0 39 0
1078 TX 327682 645 0 6 39 0
1077번 세션은 <TX-327682-645> 리소스에 대해 Exclusive 모드 (lmode=6)로 락을 획득한지 45초가 지났으며 다른 락을 블로킹(block=1)하고 있다.
1078번 세션은 같은 리소스인 <TX-327682-645>에 대해 Exclusive 모드 (request=6)락을 요청한지 39초가 지났다.
즉, 경합이 발생하고 있다.
TM모드일 경우 ID1은 Object id 이고, TX 모드일 경우의 ID1은 어느 rollback segment에 저장이 되어 있는지에 대한 정보이다.
\\
** V$LOCKED_OBJECTS : 현재 시스팀의 모든 트랜잭션에 의해 획득중인 TM락에 대한 정보를 제공
출처 : Advanced OWI in Oracle 10g, Practical OWI in Oracle 10g