MySQL 격리 수준 분석: Repeatable Read에서의 스냅샷과 팬텀리드

narisarit·2025년 3월 5일
0
post-thumbnail

Repeatable Read

MySQL에서 기본 격리 수준은 Repeatable Read이다. MySQL에서 Repeatable Read 격리 수준은 대부분의 경우 팬텀 리드를 방지한다.
MVCC (Multi-Version Concurrency Control)갭 락, 넥스트 키 락을 통해 MySQL은 팬텀 리드를 방지한다.

[의문점]

"SELECT FOR UPDATE 후 SELECT (같은 범위)에서 팬텀 리드가 발생하지 않는 것은 갭 락, 넥스트 키 락 덕분이다."라는 글을 보았다.


SELECT FOR UPDATE는 배타적 잠금(비관적 잠금, 쓰기 잠금)을 거는 것이다. 또한, SELECT FOR UPDATE(잠금 있는 읽기)는 기본 SELECT와 달리 읽기 작업이 언두 로그에서 수행되지 않고, 테이블에서 수행된다.

결국 SELECT FOR UPDATE 후 다른 트랜잭션에서 해당 범위에 데이터 쓰기 작업을 하면 FOR UPDATE를 통해 쓰기 잠금을 가지고 있는 트랜잭션이 끝날 때까지 대기하게 된다. 따라서 SELECT FOR UPDATESELECT할 때에는 외부에서 해당 범위에 쓰기 작업이 불가능하므로 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 UPDATESELECT에서는 두 번째 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에서 팬텀 리드가 발생하지 않을까?

정답: 아니다.
SELECTSELECT FOR UPDATE의 경우, 다른 트랜잭션이 두 SELECT 사이에 쓰기 후 COMMIT을 하게 되면 SELECT FOR UPDATECOMMIT된 데이터를 반영하여 데이터를 읽게 된다.

이러한 특이한 경우를 제외하고는 MySQL에서는 팬텀 리드가 발생하지 않는다.


혼자 공부하며 작성한 글이므로 잘못된 내용이 있을 가능성이 있습니다..
피드백 환영합니다..

공식 문서

레퍼런스

profile
식물축구코딩롤

0개의 댓글