Database/MySQL

[MySQL/InnoDB] Gap Lock(갭 락)의 실행 원리 탐구

sechoi 2023. 9. 16. 15:33

갭 락(Gap Lock) 이란?

레코드가 아니라 레코드와 바로 인접한 레코드 사이의 간격만을 잠그는 것을 의미한다. 갭 락의 역할은 레코드와 레코드 사이의 간격에 새로운 레코드가 생성되는 것을 제어한다.

의문: 어떻게 갭에 락을 걸 수 있는 거지?

그러면 어떻게 존재하지 않는 row에 락을 걸 수 있는 것일까? 한 번 살펴보았다.

 

현재 1번과 5번 row에만 데이터가 있는 상태이다. 우선 존재하는 데이터인 5번 row에 락을 걸어보겠다.

select * from parent where id=5 for update;

LOCK_TYPE: RECORD, LOCK_MODE: X, REC_NOT_GAP 이 보인다. 갭을 제외한 레코드에 쓰기 락이 걸렸다는 의미이다. 이건 예상되는 결과였다. 

 

이제는 존재하지 않는 레코드인 3번 row에 락을 걸어보겠다.

select * from parent where id=3 for update;

LOCK_TYPE: RECORD, LOCK_MODE: X, GAP 이 보인다. 주목할 점은 우리가 쿼리를 날린 3번 row가 아니라 해당 갭이 끝나는 5번 row에 갭 락이 걸린 것이다.

 

즉 갭 락에 대한 표시는 갭이 아니라 갭이 끝나는 레코드에 존재한다. 

 

갭 락이 걸리자 갭에 해당하는 row에 insert가 되지 않는다.

의문 해결 💡

나는 갭 락에 관해 두 가지가 궁금했다.

  1. 존재하지 않는 데이터(인덱스)에 락을 어떻게 거는지
  2. 만약 기본키가 숫자가 아니라 문자열이면 갭의 크기를 어떻게 판단하고 걸 수 있는지

갭이 끝나는 레코드(인덱스)에 락을 거므로써 두 가지 의문을 해소할 수 있었다. 

 

그러면 갭이 시작하는 레코드(인덱스)에는 락을 걸지 않는 이유가 무엇일까? 이는 인덱스 탐색 과정으로 추론해볼 수 있다.

인덱스로 원하는 데이터를 탐색 시 조건에 부합하는 인덱스부터 시작해 조건에 부합하지 않는 인덱스가 나올 때까지 탐색한다. 위 예시에서 3번 인덱스를 찾기 위해서는 리프 노드를 탐색하다가 5번 인덱스가 나오면 멈춘다. 즉 끝나는 지점을 무조건 탐색할 수 밖에 없고, 해당 위치에 갭 락 표시가 되어있으면 확인할 수 있으므로 시작 레코드에 걸 이유가 없다.

 

추가 발견: 갭 락은 공유된다

위 상황 처럼 1번과 5번 row에만 데이터가 존재하는 상황에서 아래와 같이 같은 갭에 두 번 락을 걸었다.

start transaction;

select * from table where id=3 for update; -- (1)

---
start transaction;

select * from table where id=4 for update; -- (2)

---
start transaction;

select * from performance_schema.data_locks\G -- (3)

그리고 (3) 쿼리로 락을 확인하면 5번 row에 갭 락이 두 개 걸려 있음을 확인할 수 있다. 이후에 다른 트랜잭션에서 갭에 insert를 시도하면 두 개의 락 모두 해제되어야 한다.