IT

[Oracle] 오라클 Lock

정미나 2018. 4. 30. 16:23

DB에 대해 쥐뿔도 아는게 없던 시절에도 Lock이라는 단어는 많이 들어봤었다.

애플리케이션에서 뭔가가 수정이 안되거나 저장이 안될 때면 늘 누군가 외쳤었으니깐.


"락 걸린 것 같아!"


대학 시절 deadlock에 대해 배우면서 아래와 같은 그림도 뻔질나게 봤었다.

근데 막상 "Lock의 종류나 원리에 대해 설명해봐라." 라고 하면 말문이 막히기 일쑤다.

흠.. 대학교에선 들입다 외워서 시험보느라 바빴고

회사에선 Lock을 1초라도 빨리 푸는데만 바빴다고 구차하게 변명해본다.

그럼 이제부터 Lock에 대해 본격적으로 파헤쳐보자.


오라클이 제공하는 Lock에는 꽤 여러 종류가 있다.

DML Lock, DDL Lock, Latch, 버퍼 Lock, 라이브러리 캐시 Lock/Pin 등등..

대충 아는 것도 있고, 저건 뭐지? 생소한 것도 있을 것이다.

지금부터 하나씩 차근차근 살펴보도록 한다.


DML Lock

DML이라 함은 모두 아다시피 Data Manipulation Language(데이터 조작 언어)이므로

INSERT, UPDATE, DELETE를 할 때 거는 Lock일 것이라 추측할 수 있다.


DML Lock을 두 종류로 나누면 아래와 같다.

- DML 테이블 Lock: Enqueue Lock으로 구현

- DML 로우 Lock: 로우 단위 Lock과 트랜잭션 Lock을 조합해서 구현


음.. 근데 Enqueue Lock이 뭐지?

(1) Enqueue Lock

- 공유 리소스(테이블, 트랜잭션, 테이블스페이스, 시퀀스, Temp 세그먼트 등)에 대한 액세스를 관리하는 Lock 메커니즘

- 순서가 보장되는 Queue 구조 (Queue가 선입선출(FIFO)의 대명사임은 모두가 알 것이다!)

→ 먼저 Lock을 획득한 세션이 먼저 Lock을 해제한다는 얘기

- 테이블에 대한 Lock을 획득하기 위해서는 Enqueue 리소스를 할당받아야 함


하아.. Enqueue 리소스는 또 뭐란 말인가......

백문이 불여일견이므로 일단 한 번 봐보자.

현재 할당된 Enqueue 리소스는 v$resource 뷰를 통해 확인할 수 있다.

SELECT * FROM V$RESOURCE;

음.. 봐도 모르겠..;;

Enqueue 리소스는 소유자(owner), 대기자(waiter) 목록을 관리할 수 있는 구조체를 말한다.

Enqueue 리소스에는 고유한 식별자가 부여되며, 식별자는 <Type-ID1-ID2>로 구성된다.

아하! 그럼 위 목록에서 1번 리소스 식별자는 <XR-4-0>이 되는거구만!!

Enqueue 리소스 구조체는 테이블을 이용해 통합 관리되며 shared pool 영역에 저장된다.

* 리소스 테이블에서 관리되는 각 리소스를 찾을때는 해싱 알고리즘을 사용

ex) Hash_Function(XR-4-0) = Hash Bucket 주소 

⇒ 구해진 Hash Bucket을 찾아가 Hash Chain을 따라가며 찾고자 하는 리소스 구조체를 탐색

※ 리소스 구조체를 찾지 못한 경우 새로운 리소스 구조체를 할당 받아 Hash Chain에 연결하고 리소스 구조체의 소유자 목록에 자신을 등록한다.

- 현재 소유자가 Exclusive 모드일 때는 한 순간에 하나의 세션만 Lock 획득

- 현재 소유자가 Shared 모드일 때는 여러 세션이 동시에 Lock 획득 (여러 세션이 동시에 소유자 목록에 등록 가능)


(2) TX Lock (=트랜잭션 Lock)

앞서 트랜잭션 수준 읽기 일관성에 대해 알아보며 오라클은 레코드가 갱신 중이더라도 읽기 작업에 대해서는 블로킹 없이 작업을 진행할 수 있다는 것을 알았다.

☑[Oracle] 트랜잭션 수준 읽기 일관성 자세히보기

하지만 변경 중인 레코드를 동시에 변경하려는 트랜잭션에 대해서는 액세스 직렬화가 필요하며 이를 위해 오라클은 트랜잭션 Lock을 이용한다.

- TX Lock은 트랜잭션이 첫 번째 변경을 시작할 때 얻고, 커밋 또는 롤백할 때 해제함

- Enqueue Lock으로 구현


일단 대기 이벤트 발생 현황을 조회해보자.

SELECT SID, SEQ#, EVENT, STATE, SECONDS_IN_WAIT, P1, P2, P3

  FROM V$SESSION_WAIT

 WHERE EVENT LIKE 'enq: TX%';

▲ 한 세션에서 UPDATE 후 커밋을 하지 않은 상태에서 다른 세션에서 같은 레코드를 UPDATE 시도했을 때의 결과값이다.


※ 대기 이벤트명에 따른 발생 원인은 아래와 같다.

① TX Lock ▶ 무결성 제약 위배 가능성 또는 비트맵 인덱스 엔트리 갱신

- 두 개 이상의 트랜잭션이 같은 값(PK)을 입력하려 할 때, 선행 트랜잭션이 아직 진행중인 상황에서 발생

- 두 테이블에 FK 제약이 설정돼 있는 상황에서 TX1이 Table1에서 지운 key값으로 TX2가 Table2에 입력할 때 발생

- 비트맵 인덱스 엔트리를 두 개 이상 트랜잭션이 동시에 갱신할 때 발생


② TX Lock ▶ ITL 슬롯 부족

- 블록에 레코드를 추가/갱신/삭제하려면 ITL 슬롯을 먼저 할당받아야 하는데 비어있는 ITL 슬롯이 없다면 ITL 슬롯을 사용 중인 트랜잭션 중 하나가 커밋 또는 롤백될 때까지 기다려야 하며 이때 enq: TX - allocate ITL entry 대기 이벤트가 발생

* 블록에 ITL 슬롯 할당 (INITRANS: 기본 할당 / MAXTRANS: 최대 할당)

→ INITRANS에 의해 미리 할당된 ITL 슬롯이 모두 사용 중일 때, 새로운 트랜잭션이 ITL 슬롯을 요청하면 PCTFREE 설정에 의해 비워둔 공간을 활용하여 최대 MAXTRANS 만큼의 ITL 슬롯을 생성

CREATE TABLE T (. . .) INITRANS 5 MAXTRANS 255 PCTFREE 30;

- 동시에 블록을 갱신하려는 트랜잭션 개수가 MAXTRANS 값을 초과할 때 발생

- PCTFREE에 의해 예약된 공간이 UPDATE에 의해 모두 사용되어 새로운 트랜잭션을 위한 ITL 슬롯이 부족할 때 발생

* ITL 경합에 의한 대기 현상이 자주 발생하는 세그먼트(테이블, 인덱스, 파티션)에 대해서는 INITRANS를 늘려주어야 함


③ TX Lock ▶ 인덱스 분할

- 인덱스 분할이 진행중인 블록에 다른 트랜잭션이 로우를 삽입하려고 할 때 enq: TX - index contention 이벤트 발생


③ TX Lock ▶ DML 로우 Lock

- 두 개의 동시 트랜잭션이 같은 로우를 변경하는 것을 방지

* 로우 갱신 과정

Undo 세그먼트에서 트랜잭션 슬롯 할당 → Enqueue 리소스를 통해 TX Lock 획득 → I/U/D 문을 통해 갱신하는 각 로우마다 Exclusive 모드로 로우 단위 Lock 획득

- 로우 단위 Lock : 블록 헤더 ITL과 로우 헤더 Lock Byte 설정을 의미, 이를 통해 로우를 갱신 중인 트랜잭션 상태를 확인하고 액세스 가능 여부를 결정

- TX Lock : Enqueue 리소스를 통해 TX Lock을 설정하는 것을 의미, Lock이 설정된 레코드를 갱신하고자 할 때 Enqueue 리소스에서 대기

- enq: TX - row lock contention


③ TX Lock ▶ DML 테이블 Lock

- 오라클은 로우 Lock 획득 시, 해당 테이블에 대한 테이블 Lock도 동시에 획득, 현재 트랜잭션이 갱신 중인 테이블에 대한 호환되지 않는 DDL 오퍼레이션을 방지

* Lock 모드간 호환성

- 선행 트랜잭션과 호환되지 않는 모드로 테이블 Lock을 설정하려는 후행 트랜잭션은 대기 or 포기

- 오라클의 테이블 Lock은 Lock을 획득한 선행 트랜잭션이 해당 테이블에서 현재 어떤 작업을 수행 중인지 알리는 일종의 푯말이다.


하아...... 넘나 어렵;;