제 1절 Lock
1. Lock 기본
가. Lock 이란?
- 트랜잭션 처리능력이 핵심적인 요소.
- 같은 자원을 액세스하려는 다중 트랜잭션 환경에서 데이터베이스의 일관성, 무결성 유지를 위해 트랜잭션의 순차적 진행을 보장하는 직렬화(serialization) 장치가 필요하다.
- DBMS 마다 Lock 을 구현하는 세부 기능이 다르다.
- DBMS별 Lock 매커니즘과을 정확히 이해해야 한다.
나. 공유 Lock과 배타적 Lock
- DBMS는 각 트랜잭션의 오퍼레이션별로 적당한 수준의 Lock을 자동으로 설정한다.
- 일부 Lock 은 사용자가 직접 제어 방법도 제공한다.
- 공유 Lock과 배타적 Lock 이 있다.
- 공유 Lock
- 공유(Shared) Lock 은 데이터를 읽고자 할 때 사용된다.
- 다른 공유 Lock 과는 호환되나, 배타적 Lock 과는 호환되지 않는다.
(호환 : 한 리소스에 두 개 이상의 Lock을 동시에 설정할 수 있음.) - 공유 Lock 을 설정한 리소스에 다른 트랜잭션이 추가로 Lock을 설정할 수 있으나, 배타적 Lock은 불가능하다.
- 자신이 읽고있는 리소스를 다른 사용자가 동시에 읽을 수 있어도, 변경은 불가하다.
다른 사용자가 읽고 있는 리소스를 동시에 읽을 수 있어도 변경 중인 리소스를 동시에 읽을 수 없다.
- 배타적 Lock
- 데이터를 변경하고자 할 때 사용되며, 트랜잭션이 완료될 때 까지 유지된다.
- Lock이 해제될 때까지 다른 트랜잭션은 해당 리소스에 접근할 수 없다. 변경 불가, 읽기 불가.
- 다른 트랜잭션에 의해 Lock 이 설정된 리소스는, 공유 Lock이든 배타적 Lock 이든 배타적 Lock 을 동시에 설정할 수 없다.
다. 블로킹과 교착상태
- 블로킹 (Blocking)
- Lock 경합이 발생해 특정 세션이 작업을 진행하지 못하고 멈춰 선 상태.
- 공유 Lock 끼리는 호환되므로 블로킹 발생하지 않는다.
- 공유 Lock , 배타적 Lock 은 호환되지 않아 블로킹 발생 할 수 있다. ( 배타적 Lock 끼리도 호환 안됨 )
- 블로킹 상태 해소 방법은 커밋(또는 롤백).
- 먼저 Lock 설정한 트랜잭션이 완료될 때 까지 후행 트랜잭션은 대기해야 하므로 성능 저하가 발생한다.
{info:title=Lock 에 의한 성능저하를 최소화 하는 방안}
- 트랜잭션 원자성을 훼손하지 않는 선에서 트랜잭션을 가능한 짧게 정의해야 함.
Oracle 은 select 시 공유 Lock 을 사용하지 않으므로 다른 DBMS에 비해 상대적으로 Lock 경합이 적다.
배타적 Lock 경합은 피하지 못하므로 트랜잭션을 불필요하게 길게 정의해선 안된다. - 같은 데이터를 갱신하는 트랜잭션이 동시에 수행되지 않도록 설계한다.
트랜잭션이 활발한 주간에 대용량 갱신작업을 수행하면 안 된다. - 블로킹 현상에 의해 사용자가 무한정 기다리지 않도록 프로그래밍 기법을 도입한다.
SQL Server 에서는 세션 레벨에서 LOCK_TIMEOUT 을 설정할 수 있다.
set lock_timeout 2000
Oracle 이라면 update/delete 문장 수행 전에 no wait, wait 옵션을 지정한 select ... for update 문을 먼저 수행해서 Lock 설정을 체크할 수 있다.
select * from t where no = 1 for update no wait : 대기없이 Exception을 던짐
select * from t where no = 1 for update wait 3 : 3초 대기 후 Exception을 던짐
- 트랜잭션 격리수준 (2절 2항 참조) 를 불필요하게 상향 조정하지 않는다.
- 트랜잭션 설계를 잘 하여, SQL문장이 가장 빠른 시간 내에 처리를 완료하도록 하는 것이 Lock 튜닝의 기본이고 효과도 확실하다.
{info} - 교착상태 (Deadlock)
- 두 세션이 각각 Lock 을 설정한 리소스를 서로 액세스하려고 마주보며 진행하는 상황.
- DBMS가 둘 중 한 세션에 에러를 발생시켜 문제를 해결한다.
- 여러 테이블을 액세스하면서 발생하는 교착상태는 테이블 접근 순서를 같게 처리하면 피할 수 있다.
예) 마스터 테이블과 상세 테이블을 둘 다 갱신할 때 마스터 테이블 다음에 상세 테이블 갱신하기로 규칙을 정한다.
SQL Server 라면 아래 설명한 갱신(Update) Lock 을 사용함으로써 교착상태 발생 가능성을 줄일 수 있다.
2. SQL Server Lock
가. Lock 종류
- 공유 Lock
- SQL Server 의 공유 Lock은 다음 레코드가 읽히면 곧바로 해제된다. ( 기본 트랜잭션 격리성 수준 - Read Committed) 에서만 그렇다.
- 격리성 수준을 변경하지 않고 트랜잭션 내에서 공유 Lock이 유지되도록 하려면 아래와 같이 테이블 힌트로 holdlock을 지정하면 된다.
- 데이터 변경할 목적으로 레코드 읽을 경우, 위와 같은 패턴으로 트랜잭션을 처리해야 한다.
- 고객 데이터를 읽고 적립 포인트를 변경하기 전에 다른 트랜잭션이 고객 데이터를 변경하면, 적립포인트가 비일관된 상태가 될 수 있기 때문이다.
- 배타적 Lock
- 갱신 Lock
- 위 예제에서 같은 고객에 대해서 두 트랜잭션이 동시에 수행되었다고 가정하자.
처음에는 공유 Lock을 설정했다가 적립포인트를 변경하기 직전에 배타 Lock을 설정하려고 할 것이다.
상대편 트랜잭션에 의한 공유 Lock이 해제되기만 기다리는 교착상태에 빠지게 된다. - 이러한 잠재적 교착상태를 방지하려고 SQL Server 는 갱신(Update) Lock 을 두게 되었고 아래와 같이 updlock 힌트를 지정하면 된다.
- 한 자원에 대한 갱신 Lock 은 한 트랜잭션만 설정할 수 있다.
- 첫 번째 트랜잭션이 고객테이터를 읽을 때 갱신 Lock을 설정하면,
두 번째 트랜잭션은 첫 번째 트랜잭션이 배타 Lock으로 전환했다가 이를 해제할 때까지 기다려야 한다. - 갱신 Lock 끼리는 호환 안되나, 공유 Lock 과는 호환되므로 갱신 Lock이 설정된 데이터를 단순 읽기만 하면 대기하지 않아도 된다.
- 의도 Lock
- 특정 로우에 Lock을 설정하면 그와 동시에 상위 레벨 개체(페이지,익스텐트,테이블) 에 내부적으로 의도(Intent) Lock 이 설정된다.
- Lock 설정하려는 개체의 하위 레벨에서 선행 트랜잭션이 어떤 작업을 수행 중인지 알리는 용도로 사용. 일종의 Flag.
- 구조를 변경하기 위해 테이블 lock 하려할 때 하위의 모든 페이지, 익스텐트, 로우에 어떤 Lock 이 설정되어있는지 검사하는 것은 작업이 끝나지 않을 수 있다.
의도 Lock 은 그런 현상을 방지한다. 해당 테이블에 어떤 모드의 의도 Lock 이 설정 되어 있는지 보고 작업 진행/대기 여부를 정한다.
- 스키마 Lock
- 테이블 스키마에 의존적인 작업을 수행할 때 사용된다.
- Sch-S (Schema Stability) : SQL 컴파일 중 오브젝트 스키마를 참조할 때 발생, 읽는 스키마 정보 수정/삭제 안되도록 함.
- Sch-M(Schema Modification) : 테이블 구조 변경하는 DDL문 수행 시 발행, 수정 중인 스키마 정보를 다른 세션이 참조하지 못하도록 함.
- Bulk Update Lock
- 테이블 Lock 의 일종. 테이블에 데이터를 Bulk Copy 할 때 발생.
병렬 데이터 로딩 (Bulk Insert 나 bcp작업 동시수행)을 허용하나 일반 트랜잭션 작업 허용되지 않는다.
나. Lock 레벨과 Escalation
- 위 5가지 레벨 외에 인덱스 키(Key)에 로우 레벨 Lock 을 거는 경우도 있다.
- Lock Escalation
- 관리할 Lock 리소스가 정해진 임계치를 넘으면서 로우 락 -> 페이지, 익스텐트, 테이블 레벨 락으로 확장되는 것.
- 한정된 메모리 상에서 Lock 매니저를 통해 Lock 정보를 관리하는 DBMS 에서 공통적으로 발생할 수 있는 현상이다.
- Lock 레벨이 낮을수록 동시성은 좋으나, 관리할 Lock 개수가 증가하여 더 많은 리소스를 소비한다.
- Locking 레벨이 높을수록 적은 Lock 리소스를 사용하나, 하나의 Lock 으로 수많은 레코드를 한번에 잠그기 때문에 동시성은 나빠진다.
다. Lock 호환성
[ 스키마 Lock 호환성 ]
- Sch-S 는 Sch-M 을 제외한 모든 Lock 과 호환된다.
- Sch-M 은 어떤 Lock 과도 호환되지 않는다.
3. Oracle Lock
- Oracle 은 공유 리소스, 사용자 데이터 보호를 목적으로 DML Lock, DDL Lock, 래치, 버퍼 Lock, 라이브러리 캐시 Lock/Pin 등 다양한 종류의 Lock을 사용한다.
- DML Lock 은 다중 사용자에 의해 동시에 액세스되는 사용자 데이터의 무결성을 보호해준다.
DML Lock 에는 로우 Lock, 테이블 Lock 이 있다.
가. 로우 Lock
- 항상 배타적이다. insert, update, delete 문이나 select ... for update 문을 실행한 트랜잭션에 의해 설정되며, 이 트랜잭션이 커밋 또는 롤백할 때 까지 다른 트랜잭션은 해당 로우를 변경할 수 없다.
- 일반 select 문에 의해 읽힌 레코드에는 어떤 Lock도 설정되지 않는다.
읽기 작업에 대한 공유 Lock 을 사용하지 않기 때문에, 읽기와 갱신작업은 서로 방해되지 않는다.
- 읽으려는 데이터를 다른 트랜잭션이 갱신 중이더라도 기다리지 않는다.
- 갱신하려는 데이터를 다른 트랜잭션이 읽는 중이라도 기다리지 않는다. (select ... for update 으로 읽는 경우 제외)
- 갱신하려는 데이터를 다른 트랜잭션이 갱신 중이면 기다린다.
|
- Oracle이 공유 Lock을 사용하지 않고도 일관성을 유지할 수 있는 것은 Undo 데이터를 이용한 다중 버전 동시성 제어 메커니즘을 사용하기 때문이다.
- 별도의 Lock 매니저 없이 레코드 속성으로 로우 Lock 을 구현했기 때문에 많은 레코드 갱신에도 절대 Lock Escalation 은 발생하지 않는다.
나. 테이블 Lock
- 한 트랜잭션이 로우 Lock 을 얻는 순간, 해당 테이블에 대한 테이블도 Lock을 얻는다.
- 테이블 구조를 변경하지 못하도록 현재 트랜잭션이 갱신 중인 테이블에 대한 호환되지 않는 DDL 오퍼레이션을 방지한다.
- 테이블 Lock 종류
- Row Share(RS)
- Row Exclusive(RX)
- Share(S)
- Share Row Exclusive(SRX)
- Exclusive(X)
- select ... for update 문 수행 시 RS 모드 테이블 Lock을 얻고, insert, update, delete 문을 수행 시 RX 모드 테이블 Lock 을 얻는다.
- DML 로우 Lock을 처음 얻는 순간 묵시적으로 테이블 Lock을 얻지만, Lock Table 명령을 이용해 명시적으로 테이블 Lock 을 얻을 수도 있다.
lock table emp in row share mode; lock table emp in row exclusive mode; lock table emp in share mode; lock table emp in share row exclusive mode; lock table emp in exclusive mode; |
- 테이블 Lock 끼리의 호환성은 아래 표와 같다.
- DML 수행 시 항상 테이블 Lock이 설정되는데, SQL Server 의 테이블 레벨 Lock 처럼 테이블 전체를 잠그는 기능. 그렇다면 다른 트랜잭션이 레코드 추가, 갱신이 불가할텐데 위 표에서 RX - RX 간 호환성이 있으므로 그런 일은 발생하지 않는다.
- Oracle 의 테이블 Lock 은 테이블 전체에 Lock 이 걸리는 것이 아니라, Lock을 획득한 선행 트랜잭션이 해당 테이블에서 현재 어떤 작업을 수행 중인지 알려주는 일종의 Flag 이다.
후행 트랜잭션은 어떤 테이블 Lock이 설정돼 있는지만 보고 테이블 진입여부를 확인한다.