본문 바로가기

database

[DB] 트랜잭션(2) - repeatable read 격리 수준에서의 phantom read 현상

[DB] 트랜잭션(1) - ACID와 격리 수준(isolation level)
[DB] 트랜잭션(2) - repeatable read 격리 수준에서의 phantom read 현상
[DB] 트랜잭션(3) - 격리 수준과 동시성 문제, 그 해결

 

이전 글에서 repeatable read는 팬텀 리드 현상이 발생한다고 하였다. 조금 더 자세히 살펴보자.

Phantom Read 현상

출처: Real MySQL

위 예제에서 B 트랜잭션이 emp_no >= 500000 을 select for update를 통해 명시적인 read/write lock을 걸었다. 하지만 A트랜잭션에서 emp_no이 500001인 데이터(B 트랜잭션 입장에서는 현재 존재하지 않는 데이터)를 insert 하고 있다. A 트랜잭션이 락을 거는 시점에 존재하지 않는 데이터다. 따라서 명시적인 잠금이 없는 emp_no 500001 데이터의 insert를 막지 못했다.



MySQL(repeatable read)에서도 Phantom Read 현상이 발생할까?

다음과 같이 1~3번 트랜잭션을 시작하고 테스트해보았다. 결론부터 말하자면 phantom read 현상은 발생하지 않았다. InnoDB 스토리지 엔진에서 제공하고 있는 gap lock, next key lock 덕분이다.

emp_no을 PK로 만들고 테스트하였다. phantom read가 발생하기 위해선 Transaction2가 잠금 없이 insert가 되어야 한다. 하지만 MySQL의 InnoDB 스토리지 엔진에서는 gap lock, next key lock으로 인해 Transaction2가 emp_no >= 500000 잠금으로 인해 대기해야 한다.

PK가 아닌 Indexed 컬럼에서도 동작할까?

indexed column에서도 동일하게 동작했다. 기본적으로 MySQL은 index 기반 잠금을 하고, indexed column은 정렬이 되어 있어 범위 조건으로 select ... for update 하여도 해당 범위 이상을 잠근다.

 

Index가 아예 걸리지 않은 컬럼에서도 동작할까?

index되지 않은 column에서도 동일하게 동작했다. 위에서 index 기반 잠금을 한다고 말했는데, indexed column이 아니더라도 범위 조건으로 select ... for update 하였을 시 똑같이 잠금되긴 하였다. 추측하기론 잠금 획득을 위해 I/O를 통해서 해당 데이터들을 모두 가져와  잠그기 때문에 cost가 큰 조회 조건이라면 시간이 오래 걸릴 것으로 추측한다.

 

 

[참고 자료]

Real MySQL - 백은빈, 이성욱

데이터 중심 애플리케이션 설계 - Martin Cleppmann