[MySQL] REPEATABLE READ level, MVCC

gyu·2024년 7월 8일

트랜잭션 격리 레벨 중 하나인 "REPEATABLE READ"의 특징을 알아보고
MVCC가 어떻게 동작하는지 확인해보자.

REPEATABLE READ란?

MySQL의 InnoDB 스토리지 엔진에서 기본으로 사용되는 트랜잭션 격리 레벨이다.
REPEATABLE READ는 트랜잭션 범위 내에서 조회한 내용이 항상 동일함을 보장한다.
트랜잭션이 Rollback될 가능성에 대비해 변경 전 레코드를Undo영역에 백업해두고 실제 레코드 값을 변경한다. 이러한 변경방식을 MVCC(Multi Version Concurrency Control)라고 한다.

트랜잭션은 각각 고유한 번호를 갖고있으며, Undo영역에 백업된 레코드는 변경을 발생시킨 트랜잭션 번호가 포함되어있다. 하나의 트랜잭션에서 발생하는 SELECT 쿼리의 결과는 자신의 번호보다 낮은(먼저 시작된) 트랜잭션이 변경한 데이터만 볼 수 있다.

MVCC에 의해 아래 예시로 작성된 동작이 가능한지 확인해보자.
(테스트 대상 테이블: id(PK), name 컬럼을 가지는 member 테이블)

  1. Transaction A가 member 테이블에서 id가 5인 레코드를 조회한다.
    => name 컬럼은 'name5'로 조회된다.
  2. Transaction B가 id가 5인 레코드의 name 컬럼을 'name55'로 update 후 commit 한다.
  3. Transaction A가 1번과 같은 조건으로 조회해도 name 컬럼은 'name5'로 조회되어야 한다.

Transaction A가 트랜잭션 시작 후 id = 5인 레코드를 조회한다.

Transaction B가 트랜잭션 시작 후 id = 5인 레코드를 update 한다.
이때 Transaction B는 Transaction A보다 높은 번호를 부여받았기 때문에 Transaction A의 SELECT 결과는 동일해야한다.

Transaction A가 다시 id = 5인 레코드를 조회했을 때 Undo 영역에 있는 데이터를 조회하기 때문에 name이 변경된 값이 아닌 'name5'를 조회하는 것을 확인할 수 있다.

Phantom Read

하나의 트랜잭션에서 특정 조건의 데이터를 2번 이상 조회할 때
첫번째 조회 결과와 두번째 조회 결과 사이에 다른 트랜잭션이 데이터를 추가/삭제하여 결과가 달라지는 현상을 의미한다.

REPEATABLE READ 격리 레벨에서는 이러한 부정합이 발생할 수 있다.
직접 테스트를 통해 확인해보자,.

1. 트랜잭션 A 시작, id가 100,000 이상인 데이터를 조회한다.

2. 트랜잭션 B 시작, id가 100,001인 데이터를 INSERT 한다.

3. 트랜잭션 A에서 SELECT...FOR UPDATE 구문으로 id가 100,000 이상인 데이터를 조회하면 2건이 출력된다.

위와 같은 현상을 PHANTOM READ라고 한다.
발생하는 이유는 FOR UPDATE, FOR SHARE 구문으로 조회하는 경우, SELECT 하는 레코드에 쓰기잠금, 읽기잠금을 걸어야 하는데 Undo 영역에는 레코드 잠금을 걸 수 없다.
그래서 조회 시 Undo 영역에서 가져오지 않고 현재 레코드의 값을 가져오기 때문에 처음 조회한 결과와 다른 결과를 출력하게 된다.

0개의 댓글