kimyu0218
  • [mysql] 언두 로그와 MVCC
    2025년 03월 28일 23시 20분 18초에 업로드 된 글입니다.
    작성자: @kimyu0218

    버퍼 풀은 디스크 데이터를 캐싱한다. 버퍼 풀에 로드된 레코드를 UPDATE나 DELETE로 수정하면, 데이터 페이지가 새로운 값으로 갱신되고 이전 값은 언두 로그에 백업된다. 페이지와 언두 로그에 같은 레코드에 대해 서로 다른 값이 존재하게 되는데, 이를 멀티 버전이라 한다.

    InnoDB는 언두 로그를 통해 트랜잭션 롤백 시 이전 상태로 되돌릴 수 있다. 하지만 격리 수준에 따라 적절한 버전을 반환하여 동시성을 제어하는 것도 가능하다. 지금부터 언두 로그가 어떻게 격리 수준을 보장하는지 알아보자.

    아직 커밋하지 않았다고 가정한다

     

    READ UNCOMMITTED

    가장 낮은 격리 수준으로, 트랜잭션의 커밋 여부와 상관없이 다른 트랜잭션이 수정중인 레코드를 볼 수 있다. 즉, 언두 로그의 백업된 데이터가 아니라 페이지의 최신 데이터를 본다.

    이처럼 다른 트랜잭션에서 처리중인 작업을 볼 수 있는 현상을 더티 리드라 하며, Read Uncommitted는 더티 리드가 발생하는 유일한 격리 수준이다.
     

    READ COMMITTED

    오라클의 기본 격리 수준으로, 다른 트랜잭션에서 커밋한 데이터만 볼 수 있다. 따라서 트랜잭션이 아직 완료되지 않았다면 언두 로그에 기록된 이전 값을 본다. 하지만 READ COMMITTED 격리 수준에서도 Non-Repeatable Read라는 이상 현상이 발생한다.

    동일한 쿼리에 대해 다른 결과를 조회하므로, 똑같은 SELECT 쿼리를 실행했을 때 항상 같은 값을 가져와야 하는 Repeatable Read 정합성이 깨진다.

    REPEATABLE READ

    REPEATABLE READ는 InnoDB 엔진에서 기본으로 사용되는 격리 수준이다. 동일한 쿼리를 사용하여 여러 번 조회했을 때 항상 같은 값을 가져오는데, 이는 언두 영역에서 여러 버전을 관리하고 트랜잭션에 따라 적절한 버전을 반환하기 때문이다.

    현재 트랜잭션 아이디가 2이므로 나중에 시작된 아이디는 무조건 3 이상이다

    Repeatable Read를 보장하기 위해서는 현재 트랜잭션보다 늦게 시작된 트랜잭션이 변경(커밋)한 내용을 무시해야 한다. 모든 트랜잭션은 순차적으로 증가하는 고유한 식별자를 갖는다. 즉, 트랜잭션 아이디는 계속 증가하므로, 나중에 시작된 트랜잭션일 수록 더 큰 아이디를 가진다.

    트랜잭션은 데이터 페이지에서 해당 레코드의 트랜잭션 아이디를 확인한 뒤, 현재 아이디보다 크다면 언두 로그를 조회하여 이전 값을 가져온다. 이때, 언두 로그에 기록된 트랜잭션 아이디는 현재 트랜잭션의 아이디보다 작거나 같아야 한다.

    먼저 시작한 트랜잭션이 데이터를 변경하고 커밋한 경우에는 어떤 일이 발생할까? REPEATABLE READ는 일관된 읽기를 보장하므로 변경 전 데이터를 조회할 것이다. 이는 해당 격리 수준이 트랜잭션이 처음 SELECT문을 사용한 시점의 스냅샷을 이용해 언두 로그를 따라가기 때문이다.

    trx2가 커밋한 내용을 읽지 않고, 처음 조회했을 당시의 데이터를 읽는다

    그렇다면 REPEATABLE READ 격리 수준에서는 아무런 이상 현상이 발생하지 않을까? 유감스럽게도 팬텀 리드가 발생한다. 팬텀 리드는 동일한 쿼리를 여러 번 실행했을 때 이전에 읽은 적 없는 새로운 행이 추가되는 현상이다.

    일반적인 SELECT문(잠금 없는 읽기)의 경우, 현재 트랜잭션보다 늦게 시작된 트랜잭션이 추가한 값을 무시하면 되기 때문에 팬텀 리드가 발생하지 않는다. 하지만 SELECT FOR UPDATE를 사용해 쓰기 잠금을 걸게 되면, 잠금을 걸 수 없는 언두 로그를 사용하지 못하고 테이블 데이터를 직접 조회하게 된다.

    격리 수준에 따라 발생할 수 있는 이상 현상을 표로 정리하면 다음과 같다.

     Dirty ReadNon-repeatable ReadPhantom Read
    READ UNCOMMITTEDOOO
    READ COMMITTEDXOO
    REPEATABLE READXXO
    SERIALIZABLEXXX

     

    댓글