MySQL에서 기본 격리 수준은 Repeatable Read이다. MySQL에서 Repeatable Read 격리 수준은 대부분의 경우 팬텀 리드를 방지한다.
MVCC (Multi-Version Concurrency Control)와 갭 락, 넥스트 키 락을 통해 MySQL은 팬텀 리드를 방지한다.
SELECT FOR UPDATE
는 배타적 잠금(비관적 잠금, 쓰기 잠금)을 거는 것이다. 또한, SELECT FOR UPDATE
(잠금 있는 읽기)는 기본 SELECT
와 달리 읽기 작업이 언두 로그에서 수행되지 않고, 테이블에서 수행된다.
결국 SELECT FOR UPDATE
후 다른 트랜잭션에서 해당 범위에 데이터 쓰기 작업을 하면 FOR UPDATE
를 통해 쓰기 잠금을 가지고 있는 트랜잭션이 끝날 때까지 대기하게 된다. 따라서 SELECT FOR UPDATE
후 SELECT
할 때에는 외부에서 해당 범위에 쓰기 작업이 불가능하므로 Repeatable Read가 지켜진다는 것이다.
Repeatable Read 격리 수준에서는 트랜잭션당 하나의 스냅샷을 통해 기본 SELECT
동작을 하기 때문에 기본적으로 Repeatable Read는 지켜진다.
"그렇다면 스냅샷은 언제 생성될까?
SELECT FOR UPDATE도 읽기 연산이므로 스냅샷이 생성될까? 아니면 테이블을 직접 읽으므로 생성되지 않을까?
만약 SELECT FOR UPDATE에서도 스냅샷이 생성되거나, 트랜잭션이 시작될 때 스냅샷이 만들어진다면,
"SELECT FOR UPDATE 후 같은 범위를 SELECT할 때 팬텀 리드가 발생하지 않는 이유는 갭 락과 넥스트 키 락 덕분이다."
보다는
"SELECT FOR UPDATE 후 같은 범위를 SELECT할 때 팬텀 리드가 발생하지 않는 이유는 MVCC 덕분이다."
라고 하는 것이 더 적절하지 않을까?" 라는 고민을 하게 되었다.
스냅샷이 언제 만들어지는지 정확히 알아보기 위해 공식 문서를 찾아보았다.
This is the default isolation level for InnoDB. Consistent reads within the same transaction read the snapshot established by the first read.
If the transaction isolation level is REPEATABLE READ (the default level), all consistent reads within the same transaction read the snapshot established by the first such read in that transaction. You can get a fresher snapshot for your queries by committing the current transaction and after that issuing new queries.
처음 기본 읽기 작업에서 스냅샷이 생성되는 것을 확인할 수 있다.
따라서 SELECT FOR UPDATE
후 SELECT
에서는 두 번째 SELECT
에서 해당 트랜잭션 이전에 시작된 트랜잭션 중 가장 최근이면서 COMMIT된 트랜잭션의 언두 로그를 스냅샷으로 생성한다.
그렇기 때문에 "SELECT FOR UPDATE 후 SELECT (같은 범위)에서 팬텀 리드가 발생하지 않는 것은 갭 락, 넥스트 키 락 덕분이다."는 옳은 말이었다!
The exception to this rule is that the query sees the changes made by earlier statements within the same transaction. This exception causes the following anomaly: If you update some rows in a table, a SELECT sees the latest version of the updated rows, but it might also see older versions of any rows.
위 내용도 읽어보면 좋을 것 같다.
그럼 MySQL에서는 MVCC는 물론 갭 락, 넥스트 키 락을 제공하니까 Repeatable Read에서 팬텀 리드가 발생하지 않을까?
정답: 아니다.
SELECT
후 SELECT FOR UPDATE
의 경우, 다른 트랜잭션이 두 SELECT
사이에 쓰기 후 COMMIT을 하게 되면 SELECT FOR UPDATE
는 COMMIT된 데이터를 반영하여 데이터를 읽게 된다.
이러한 특이한 경우를 제외하고는 MySQL에서는 팬텀 리드가 발생하지 않는다.
혼자 공부하며 작성한 글이므로 잘못된 내용이 있을 가능성이 있습니다..
피드백 환영합니다..