DB 락(Lock)
작성일
DB 락 (Lock)
데이터베이스는 여러 사용자들이 같은 데이터를 동시에 접근하는 상황에서 데이터의 무결성과 일관성을 지키기 위해 락을 사용한다.
예를 들어, 세션 1에서 트랜잭션을 시작하고 데이터를 수정하는 동안 아직 커밋을 수행하지 않았는데, 세션 2에서 동시에 같은 데이터를 수정하게 되면 트랜잭션의 원자성이 깨진다. 또한, 세션 1이 중간에 롤백하게 되면 세션 2는 잘못된 데이터를 수정하는 문제가 발생한다. 이러한 문제를 해결하지 위해 데이터베이스는 락(Lock)
이라는 개념을 제공한다.
세션 1과 세션 2가 같은 데이터 변경을 시도한다고 가정해보자.
세션 1이 트랜잭션을 시작하고 데이터 변경을 시도하면 이때 로우의 락을 획득해야 한다. 비슷한 시점에 세션 2가 트랜잭션을 시작하고 세션 1과 같은 데이터를 변경 시도 한다. 이때 해당 로우의 락을 먼저 획득해야 하는데 락이 없으므로(세션 1이 락을 가지고 있음) 락이 돌아올 때까지 대기하며 락 대기 시간이 넘어가면 락 타임아웃 오류가 발생한다. 세션 1이 커밋을 수행한다. 커밋으로 트랜잭션이 종료되었으므로 락도 반환한다. 락을 획득하기 위해 대기하던 세션 2가 락을 획득하고 데이터 변경을 시도한다.
DB 락 종류
DB 락의 종류에는 공유 락, Shared Lock (Read Lock)과 베타 락, Exclusive Lock (Write Lock)이 있다.
공유 락
공유 락은 데이터를 읽을 때 사용되는 락이다. Read Lock이라고도 불린다. 공유 Lock은 공유 Lock 끼리는 동시에 접근이 가능하다. 즉, 하나의 데이터를 읽는 것은 여러 사용자가 동시에 할 수 있다. 그러나 공유 락이 설정된 데이터에 베타 락은 접근할 수 없다.
베타 락
베타 락은 데이터를 변경할 때 사용되며 트랜잭션이 완료될 때까지 유지된다. 베타락은 락이 해제될 때까지 다른 트랜잭션(읽기 포함)은 해당 리소스에 접근할 수 없다.
락의 설정 범위
- 데이터베이스: 전체 데이터베이스를 기준으로 락을 한다. 즉, 1개의 세션만이 DB의 데이터에 접근이 가능하다. 이 기능은 보통 사용하지 않는다. 사용할 땐 주요한 DB의 업데이트에 사용한다.
- 파일: DB 파일을 기준으로 락을 설정한다. 파일이란 테이블, row 등과 같은 실제 데이터가 쓰여지는 물리적인 저장소이다.
- 테이블: 테이블을 기준으로 락을 설정한다. 테이블의 모든 행을 업데이트 하는 등의 전체 테이블에 영향을 주는 변경을 수행 할 때 유용하다. 즉, DDL(create, alter, drop 등)구문과 함께 사용되며 DDL Lock이라고도 한다.
- 페이지와 블럭: 파일의 일부인 페이지와 블록을 기준으로 Lock을 설정한다. 잘 사용하지 않는다.
- 컬럼: 컬럼을 기준으로 Lock을 설정한다. 이 형식은 락 설정 및 해제의 리소스가 많이 들기 때문에 일반적으로 사용되지 않는다.
- 행: 행 수준의 Lock은 1개의 행을 기준으로 락을 설정한다. DML(select, insert, update, delete)에 대한 락으로 가장 일반적으로 사용하는 락이다.
블로킹(Blocking)
블로킹
은 Lock간(베타-베타, 베타-공유)의 경합이 발생하여 특정 트랜잭션이 작업을 진행하지 못하고 멈춰선 상태를 말한다. 공유락끼리는 블로킹이 발생하지 않지만 베타락은 블로킹이 발생한다. 블로킹을 해소하기 위해서는 이전의 트랜잭션이 완료(commit or rollback)되어야 한다. 뒤에 들어온 트랜잭션은 이전 트랜잭션이 마무리되어야 이후 진행이 가능하다. 이런 경합은 성능에 좋지 않은 영향을 주므로 경합을 최소화 해야 한다.
블로킹 해결 방안
- 한 트랜잭션의 길이를 짧게하여 경합의 확률을 줄인다.
- 처음부터 설계할 때 같은 데이터를 갱신하는 트랜잭션이 동시에 수행되지 않도록 해야한다.
- 트랜잭션 격리성 수준을 불필요하게 상향 조정하지 않는다.
- 쿼리를 오랜시간 잡아두지 않도록 적절한 튜닝을 진행한다.
교착상태(DeadLock)
교착상태
는 두 트랜잭션이 각각 Lock 설정된 상태에서 서로의 락에 접근하여 값을 얻어오려고 할 때 이미 각각의 트랜잭션에 의해 락이 설정되어 있기 때문에 양쪽 트랜잭션 모두 영원히 처리가 되지 않게 되는 상태를 말한다.
예를 들어 트랜잭션 A는 master 테이블의 5번 로우를 수정하고 detail 테이블의 5번 로우를 수정한다. 트랜잭션 B는 detail 테이블의 5번 로우를 수정하고 master 테이블의 5번 로우를 수정한다. 트랜잭션 A,B 모두 락이 설정되어 교착상태(데드락)가 된다.
교착상태가 발생하면 DBMS가 둘 중 한 트랜잭션에 에러를 발생시켜 문제를 해결한다. 교착상태가 발생할 가능성을 줄이기 위해서는 접근 순서를 동일하게 하는 것이 중요하다. 예를 들어 master를 업데이트 한 후 detail을 업데이트 하는 것처럼 접근 순서를 동일하게 하는 것이 중요하다.
DB 락, 조회
일반적인 조회는 락을 사용하지 않지만 조회 시점에 락이 필요한 경우가 있다. 트랜잭션 종료 시점까지 해당 데이터를 다른 곳에서 변경하지 못하도록 강제로 막아야 할 때 사용한다.
이럴 때는 select for update 구문을 사용하면 된다. 이렇게 하면 조회 시점에 락을 가져가버리기 때문에 다른 세션에서 해당 데이터를 변경할 수 없다. 물론 이 경우도 트랜잭션을 커밋하면 락을 반납한다.
참고 select for update 예시
SELECT * FROM book WHERE id=1 FOR UPDATE;