본문 바로가기
DB/SQL튜닝

SQL 튜닝이론 - 데이터 정합성을 위한 다양한 LOCK

by 참외롭다 2024. 2. 11.
반응형

SQL 튜닝이론 - 데이터 정합성을 위한 다양한 LOCK

 

LOCK 은 데이터베이스의 특징을 결정짓는 핵심적인 메커니즘입니다. 오라클은 공유 리소스와 사용자 데이터를 보호할 목적으로 다양한 형태의 LOCK을 제공합니다. 대표적인 LOCK 의 종류는 다음과 같습니다.

 

- DML LOCK

- DDL LOCK

- 래치버퍼 LOCK

- 라이브러리 캐시 LOCK/PIN 

 

래치 LOCK은 SGA에 공유된 자료구조를 보호하기 위해 사용하고, 버퍼 LOCK은 버퍼 블록에 대한 액세스 직렬화를 위해 사용합니다. 라이브러리 캐시 LOCK과 PIN 은 라이브러리 캐시에 공유된 SQL 커서와 PL/SQL 을 보호하기 위해 사용합니다. 애플리케이션 개발 측면에서 가장 중요하게 다뤄야할 LOCK은 무엇보다 DML LOCK 입니다. DML LOCK은 다중 트랜잭션이 동시에 액세스하는 사용자 데이터의 무결성을 보호해 줍니다. DML LOCK에는 TABLE LOCK 과 ROW LOCK 이 있습니다.

 

DML ROW LOCK

DML ROW LOCK 은 두 개의 트랜잭션이 동시에 같은 로우를 변경하는 것을 방지합니다. 하나의 트랜잭션이 하나의 로우를 변경하려면 로우 LOCK 을 먼저 설정해야 합니다. LOCK을 통해 베타적 모드를 구현합니다. 베타적 모드란, UPDATE 또는 DELETE 진행중인 로우를 다른 트랜잭션이 UPDATE 하거나 DELETE 할 수 없게하는 것 입니다.

 

INSERT에 대한 로우 LOCK 경합은 UNIQUE 인덱스가 있을 때만 발생합니다. UNIQUE 인덱스가 있는 상황에서 두 트랜잭션이 UNIQUE 한 컬럼에 같은 값을 입력하려고 할 때 블로킹이 발생합니다. 블로킹이 발생하면, 후행 트랜잭션은 선행 트랜잭션이 커밋하기까지 대기합니다. 선행 트랜잭션이 커밋하면 후행은 INSERT에 실패하고, 롤백하면 성공합니다. 두 트랜잭션이 서로 다른 값을 입력하거나 UNIQUE 인덱스가 아예 없으면 INSERT 에 대한 로우 LOCK 경합은 발생하지 않습니다.

 

오라클은 다른 트랜잭션이 변경한 로우를 읽을때 복사본 블록을 만들어 쿼리가 시작된 시점으로 되돌려서 읽습니다. 변경이 진행중인 로우를 읽을 때도 LOCK 이 풀리때까지 기다리지 않고 복사본을 만들어 읽습니다. 따라서 SELECT 문에 ROW LOCK 을 사용할 필요가 없습니다.

결국, 오라클에서는 DML과 SELECT는 서로 진행을 방해하지 않습니다. SELECT 끼리도 마찬가지 입니다. DML 끼리는 로우 LOCK으로 인해 서로 방해할 수 있습니다.

 

MVCC 모델을 사용하지 않는 DBMS는 SELECT 문에 공유 LOCK을 사용합니다. 두 트랜잭션이 같이 LOCK을 설정할 수 있습니다. 공유 LOCK은 배타적 LOCK 과 호환되지 않기 때문에 DML과 SELECT가 서로 진행을 방해할 수 있습니다. 즉, 다른 트랜잭션이 읽고 있는 로우를 변경하려면 다음 레코드로 이동할 때까지 기다려야하고, 반대로 변경 중인 로우를 읽으려면 DML 트랜잭션이 커밋할때까지 기다려야 합니다.

 

DML ROW LOCK 에 의한 성능 저하를 방지하려면, 온라인 트랜잭션을 처리하는 주간에 LOCK을 필요 이상으로 오래 유지하지 않도록 커밋 시점을 조절해야 합니다. 최대한 트랜잭션이 빨리 일을 마치도록 즉, LOCK이 오래 지속되지 않도록 관련 SQL을 모두 튜닝해야 합니다.

 

DML TABLE LOCK

오라클은 DML ROW LOCK 을 설정하기에 앞서 DML TABLE LOCK을 우선 설정합니다. 현재 트랜잭션이 갱신 중인 테이블을 다른 트랜잭션이 변경하지 못하게 막기 위해서입니다. 이에 TM LOCK이라 부르기도 합니다. DML ROW LOCK 은 항상 배타적 모드를 사용하지만 TABLE LOCK은 여러가지 LOCK 모드를 사용합니다. 선행 트랜잭션과 호환되지 않는 모드로 TABLE LOCK을 설정하려는 후행 트랜잭션은 대기하거나 작업을 포기해야 합니다.

 

DML TABLE LOCK 의 종류와 호환 범위

  NULL RS(ROW SHARE) RX(ROW EXCLUSIVE) S(SHARE) SRX(SHARE ROW EXCLUSIVE) X(EXCLUSIVE)
NULL O O O O O O
ROW SHARE O O O O O  
ROW EXCLUSIVE O O O      
SHARE O O O O    
SHARE ROW EXCLUSIVE O O        
EXCLUSIVE O          

 

 

INSERT, DELETE, UPDATAE, MERGE 를 위해 ROW LOCK을 설정하려면 해당 테이블에 RX(ROW EXCLUSIVE) 모드 테이블 LOCK을 먼저 설정해야합니다. 

 

SELECT FOR UPDATE 문을 위해 ROW LOCK을 설정하려면 ORACLE 10G 이하는 RS모드, 11G 이상은 RX 모드 테이블 LOCK 을 먼저 설정해야 합니다. RS와 RX는 호환되므로 SELECT FOR UPDATE나 DML 문 수행시 테이블 LOCK에 의한 경합은 발생하지 않습니다. 같은 로우를 갱신하려고 할 때만 로우 LOCK에 의한 경합이 발생합니다. 

 

테이블 LOCK은 트랜잭션이 TABLE LOCK을 설정한 테이블에서 어떤 작업을 하고 있는지 알려주는 푯말입니다. 위의 표처럼 테이블 LOCK에는 여러가지 모드가있고 어떤 모드를 사용했느냐에 따라 후행 트랜잭션이 수행할 수 있는 작업의 범위가 결정됩니다. 진행할 수 없다면 대기할지 포기할지에 대해 진로를 결정해야 합니다. 기다려야 한다면, 대기자 목록에 LOCK 요청을 등록하고 기다려야합니다.

 

예를 들어, DDL을 이용해 테이블 구조를 변경하려는 트랜잭션은 해당 테이블에 TM LOCK이 설정돼 있는지 확인해야 합니다. TM LOCK을 RX(ROW EXCLUSIVE) 모드로 설정한 트랜잭션이 하나라도 있으면, 현재 테이블을 갱신 중인 트랜잭션이 있다는 의미입니다. 따라서 ORA-00054 메시지를 남기고 작업을 멈춥니다. 반대로 DDL 문이 먼저 수행 중일 때는, DML 문을 수행하려는 트랜잭션이 대기합니다.

 

대상 리소스가 사용중일때, 진로선택

LOCK을 얻고자 하는 리소스가 사용중일때, 프로세스는 세 가지 방법 중 하나를 택합니다. SELECT FOR UPDATE 구문은 사용자가 세 가지 프로세스 중 하나를 선택할 수 있습니다.

 

1. LOCK이 해제될때까지 기다립니다. (SELECT \* FROM T FOR UPDATE)

 

2. 일정시간만 기다리다가 포기합니다. (SELECT \* FROM T FOR UPDATE WAIT 3)

 

3. 기다리지 않고 작업을 포기합니다.  (SELECT \* FROM T FOR UPDATE NOWAIT)

 

DML을 수행할 때 묵시적으로 테이블 LOCK을 설정하는데 이때  리소스가 사용중이라면 기다리는 방법을 선택합니다. LOCK TABLE 명령을 이용해 명시적을 테이블 LOCK을 설정할 때도 기본적으로 기다리는 방법을 선택하지만, NOWAIT 옵션을 이용해 곧바로 작업을 포기하도록 사용자가 지정할 수 있습니다.

 

LOCK TABLE EMP IN EXCLUSIVE MODE NOWAIT;

 

 

DDL을 수행할 때 내부적으로 테이블 LOCK을 설정하는데, 이때 NOWAIT 옵션이 자동을 지정됩니다. 

 

반응형