우선 Lock에 대해 알아보기 전에 트랜잭션에 대한 이해가 필요하므로 트랜잭션에 대해 모른다면 아래 포스팅을 참고하시길 바랍니다.
락(Lock)이란 ?
두 명의 사용자가 DB에 접근해 아래 처럼 2개의 DB 세션을 사용한다고 가정해봅시다. ( 여기서는 H2 DB 사용 )
편의상 두 개의 세션을 나눠서 세션1, 세션2라고 부르겠습니다.
세션1이 트랜잭션을 시작하고 데이터를 수정하는 동안 아직 커밋을 수행하지 않았는데,
세션2에서 동시에 같은 데이터를 수정하게 되면 여러가지 문제가 발생하게 됩니다.
바로 트랜잭션의 원자성이 깨지는 것입니다.
그런데 여기서 세션1이 중간에 롤백을 하게 되면 세션2는 잘못된 데이터를 수정하는 문제도 추가로 발생하게 됩니다.
이런 문제를 방지하기 위해서 Lock을 사용하는데
이 Lock은 DB는 여러 사용자들이 같은 데이터를 동시에 접근하는 상황에서
세션이 트랜잭션을 시작하고 데이터를 수정하는 동안에는 커밋이나 롤백 전까지 다른 세션에서 해당 데이터를 수정할 수 없게 막는 역할을 합니다.
이름 그대로 Lock "잠금"을 하는 것입니다.
Lock에 대한 쉬운 이해를 위한 예시
두 명의 사용자가 연필이라는 상품의 가격을 바꾸려고 합니다.
세션1은 연필의 가격을 1000원으로 바꾸려하고, 세션2는 500원으로 바꾸려고합니다.
먼저 세션1이 트랜잭션을 시작해 연필의 가격을 1000원으로 변경합니다.
세션1이 세션2보다 먼저 데이터의 변경을 시도 했으므로 세션1이 해당 행의 Lock을 흭득하게 됩니다.
세션1은 락을 획득했으므로 해당 로우에 연필의 가격을 1000원으로 바꾸는 update sql을 수행합니다.
이제 세션2가 트랜잭션을 시작해 연필의 가격을 변경하려고 시도합니다.
하지만 해당 행의 Lock을 세션1이 갖고있으므로 Lock이 돌아올 때 까지 대기합니다.
(이 때 Lock 대기 시간을 설정할 수 있고, 락 대기 시간이 넘어가면 락 타임아웃 오류가 발생합니다.)
세션1이 커밋을 수행합니다.
커밋으로 인해 트랙잭션이 종료되어 Lock도 반납합니다.
이제 Lock을 흭득하기 위해 대기하던 세션2가 Lock을 흭득합니다.
그리고 해당 로우에 연필의 가격을 500원으로 바꾸는 update sql을 수행합니다.
조회 Lock
추가로 데이터 변경 뿐아니라 조회에도 Lock을 사용할 수 있습니다.
데이터를 조회하는데 Lock을 흭득하고 싶다면 select for update 구문을 사용합니다.
이렇게 하면 이렇게 하면 세션1이 조회 시점에 락을 가져가버리기 때문에 다른 세션에서 해당 데이터를 변경할 수 없게 됩니다.
물론 이 경우도 트랜잭션을 커밋하면 락을 반납합니다.
조회 시점에 Lock은 트랜잭션 종료 시점까지 해당 데이터를 다른 곳에서 변경하지 못하도록 강제로 막아야 할 때 사용합니다.
예를 들어서 애플리케이션 로직에서 memberA 의 금액을 조회한 다음에 이 금액 정보로 애플리케이션에서 어떤 계산을 수행하는데,
이 계산이 돈과 관련된 매우 중요한 계산이어서 계산을 완료할 때 까지 memberA 의 금액을 다른곳에서 변경하면 안될 경우 이럴 때 조회 시점에 락을 사용할 수 있습니다.
Lock의 설정 범위
데이터베이스
전체 데이터베이스를 기준으로 Lock을 설정한
즉, 1개의 세션만이 DB의 데이터에 접근이 가능하며 일반적으로 잘 사용하지 않습니다.
DB의 소프트웨어 버전을 업그레이드하거나 DB 업데이트를 진행할 경우 사용
파일
데이터베이스 파일을 기준으로 Lock을 설정
여기서 파일이란 테이블, row등과 같은 실제 데이터가 쓰여지는 물리적인 저장소를 의미
잘 사용되지는 않습니다.
테이블
테이블을 기준으로 Lock을 설정
이는 테이블의 모든 행을 업데이트 하는 등의 전체 테이블에 영향을 주는 변경을 수행할 때 유용
DDL(create, alter, drop 등) 구문과 함꼐 사용되며 DDL Lock이라고도 합니다.
페이지와 블럭
파일의 일부인 페이지와 블록을 기준으로 Lock을 설정한
잘 사용하지 않습니다.
컬럼
컬럼을 기준으로 Lock을 설정
하지만 이 범위는 Lock 설정 및 해제의 리소스가 많이 들기 때문에 잘 사용하지 않고, 지원하는 DBMS도 많지 않습니다.
행
1개의 행을 기준으로 Lock을 설정
DML에 대한 Lock으로 가장 기본으로 사용하는 Lock
Lock의 종류
공유 락 (Shared Lock)
공유 락은 데이터를 조회 (SELECT) 할 경우 사용되며 Read Lock이라고도 불리며 Shared의 앞 글자를 따서 주로 S로 표기합니다.
여러 사용자가 동시에 데이터를 읽어도 데이터의 일관성에는 아무런 영향을 주지 않기 때문에, 공유 락끼리는 동시에 접근이 가능합니다.
즉, 내가 보고 있는 데이터는 다른 사용자가 볼 수 있지만, 변경할 수는 없습니다.
하지만 공유 락이 설정된 데이터에 베타 락을 사용할 수 없습니다.
베타 락 (Exclusive Lock)
베타 락은 데이터에 변경할 때 사용하는 Lock으로 Write Lock으로도 불리며, X로 표기합니다.
베타 락은 이름처럼 다른 세션이 해당 자원에 접근(ex, SELECT, INSERT..) 하는 것을 막고 트랜잭션이 완료 될 때까지 유지됩니다.
베타 락은 해제될 때 까지 다른 트랜잭션은 해당 리소스에 접근할 수 없으며, 변경 및 읽기가 불가능합니다.
업데이트 락 (Update)
업데이트 락은 데이터를 수정하기 위해 베타 락(X)을 걸기 전, 데드 락을 방지하기 위해 사용됩니다.
일반적으로 업데이트 락은 UPDATE 쿼리의 필터(WHERE)가 실행되는 과정에서 적용됩니다.
내재 락 (Intent Lock)
내재 락은 사용자가 요청한 범위에 대한 락(ex, 테이블 락)을 걸 수 있는지 여부를 빠르게 파악하기 위해 사용됩니다.
내재 락은 공유 락과 베타 락 앞에 I 기호를 붙인 IS, IX, SIX 등이 있습니다.
블로킹 (Blocking)
블로킹은 Lock들의 경합(Race Condition)이 발생하여 특정 세션이이 작업을 진행하지 못하고 멈춰선 상태를 말합니다.
공유 락<-> 배타적 락 또는, 배타적 락 <-> 배타적 락 끼리 블로킹이 발생할 수 있습니다.
그림을 통해 쉽게 알아보면
Member 테이블의 "카키"라는 데이터에 공유 락이 설정되어 있는 트랜잭션 1이 커밋 or 롤백 되기 전에
트랜잭션 2에서 동일한 데이터에 베타 락을 설정한 상황입니다.
이 때 경합이 발생하여 트랜잭션 2는 트랜잭션 1의 락이 해제 될 때 까지 기다리게 되는데 이것을 블로킹이라고 합니다.
이를 해결하기 위한 방법은 트랙잭션 commit 또는 rollback 입니다.
경합이 발생할 때, 먼저 Lock을 설정한 트랜젝션을 기다려야하기 때문에, 이런 현상이 반복되면 빠른 서비스를 제공할 수 없습니다.
(블로킹을 해소하기 위해서는 이전 트랙잭션이 commit 또는 rollback 되어야 함)
해결방안
- SQL 문장에 가장 빠르게 실행되도록 리펙토링하는 것이 가장 기본이며 효과적인 방법입니다.
- 트랜잭션을 가능한 짧게 정의하면 경합을 줄일 수 있습니다.
- 동일한 데이터를 동시에 변경하는 작업을 하지 않도록 설계하는 것이 좋습니다. 또한 트랜젝션이 활발한 주간에는 대용량 갱신 작업을 수행하면 안됩니다.
- 대용량작업이 불가피할 경우, 작업단위를 쪼개거나 LOCK_TIMEOUT을 설정하여 해당 Lock의 최대시간을 설정합니다.
SET LOCK_TIMEOUT 3000;
교착상태(DeadLock)이란 ?
교착상태는 두 트랜잭션이 각각 Lock을 설정하고 서로의 Lock에 접근하여 값을 얻어오려고 할 때,
이미 각각의 트랜잭션에 의해 Lock이 설정되어 서로 블로킹 상태에 있어 양쪽 트랜잭션 모두 영원히 처리가 되지않게 되는 상태를 말합니다.
이전 블로킹 그림에서 추가적인 상황이 들어간 그림으로 , 다음과 같은 과정에서 데드락이 발생합니다.
1. 트랜잭션 1에서 Member 테이블의 데이터에 공유 락을 설정, 트랜잭션 2는 Campus 테이블에 베타 락을 설정
2. 베타 락이 설정되어 있는 Campus 테이블에 트랜잭션 1이 공유 락 설정 (트랜잭션 2가 락을 반납할 때 까지 블로킹 상태)
3. 공유 락이 설정되어 있는 Member 테이블에 트랜잭션 2가 공유 락 설정 (트랜잭션 1이 락을 반납할 때 까지 블로킹 상태)
=> 트랜잭션 1, 2의 블로킹 상태는 각 트랜잭션이 종료되어야하지만 서로가 블로킹 상태이므로 블로킹 상태가 종료되지 않아 데드락 발생
데드락 감지
MySQL은 기본적으로 데드락을 감지하는 다음 옵션이 on으로 활성화 되어 있습니다.
set global innodb_deadlock_detect=on;
이 상황에서 데드락이 발생할 경우 데드락 감지가 된 트랜잭션이 자동으로 롤백되어 종료되고 다른 트랜잭션은 정상적으로 작업이 수행됩니다.
만약 off로 변경한다면, MySQL에 설정된 LockTimeout 시간이 지날 시 Lock Timeout 에러가 발생하고
트랜잭션이 롤백되고 종료되어 데드락이 해결 될 수 있습니다.
'◼ DB' 카테고리의 다른 글
데이터베이스 정규화(Normalization)란? 예시를 통해 쉽게 이해해보자 (0) | 2023.07.23 |
---|---|
[데이터베이스] DDL, DML, DCL이란? (0) | 2023.06.04 |
트랜잭션이란? 특징과 사용법에 대해 쉽게 알아보자 (1) | 2023.04.13 |
(데이터베이스 기초) DBMS, SQL 쉽고 간단하게 이해해보자. (2) | 2022.09.21 |
RDBMS와 NoSQL의 차이점 및 개념 완벽 정리 (2) | 2022.09.20 |