ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • MVCC
    카테고리 없음 2022. 7. 14. 00:19

    MySQL 은 격리수준에 따라 레코드를 읽어오는 방식에 차이가 있습니다. 그 이유는 MVCC(Multi Version Concurrency Control)와 관계가 아주 깊습니다.

    이번 포스팅에서는 MVCC 에 대해 알아보겠습니다.

    MVCC 를 지원하는 이유

    MVCC 는 잠금을 사용하지 않는 일관된 읽기를 제공하기 위해 사용됩니다.

    MVCC 는 MySQL 에서만 제공하는 기능이 아닌 레코드 레벨의 트랜잭션을 지원하는 DBMS라면 제공하는 기능입니다.

    MySQL 은 MVCC를 언두 로그(Undo log) 를 이용해 이 기능을 구현합니다. 참고로 여기서 얘기하는 Multi Version 은 하나의 레코드에 대해 여러개의 버전이 동시에 관리된다는 의미입니다.

    우선 설명을 위해 아래와 같은 테이블을 하나 만들고 데이터를 커밋하겟습니다.

    CREATE TABLE member (
      m_id INT NOT NULL,
      m_name VARCHAR(20) NOT NULL,
      m_area VARCHAR(100) NOT NULL,
      PRIMARY KEY (m_id),
      INDEX ix_area (m_area)
    );
    ​
    INSERT INTO member (m_id, m_name, m_area) VALUES (12, '홍길동', '서울');
    COMMIT;
    ​

    COMMIT 까지 완료가 되었다면 데이터베이스 상태는 다음과 같습니다.

    이 상태일 때 다음과 같은 UPDATE 쿼리를 실행한다고 생각해보겠습니다.

    UPDATE member set m_area = '경기' WHERE m_id = 12;

    그럼 다음과 같은 그림으로 DB의 상태가 변경됩니다. 여기서 눈여겨볼만한 점은 2개 있습니다.

    1. 언두 로그에 m_area 컬럼의 변경전 값만 InnoDB 버퍼 풀에서 복사 되었습니다.
    2. 데이터 파일의 m_area 값이 ? 입니다.
      • InnoDB 버퍼 풀의 변경 내용은 InnoDB 스토리지 엔진의 백그라운드 스레드에 의해서 기록됩니다.
      • 따라서, InnoDB 버퍼풀의 변경 내용이 디스크의 데이터 파일에 기록됐는지 여부는 시점에 따라 다를 수 있습니다.
      • 단, 엄격히 보면 그렇다는 것이고 InnoDB는 ACID 를 보장하기 때문에 일반적으로 InnoDB 버퍼풀과 데이터 파일은 동일한 상태라고 볼 수 있습니다.

    해당 Update 쿼리는 아직 COMMIT 이나 ROLLBACK 이 되지 않았습니다. 이때 다음과 같은 쿼리로 조회를 하면 어떻게 데이터를 조회할까요?

    SELECT * FROM member WHERE m_id = 12;

    정답은 격리수준에 따라 다르다입니다.

    격리 수준이 READ_UNCOMMITTED 인 경우엔 InnoDB 버퍼 풀이나 데이터 파일로 부터 변경되지 않은 데이터를 읽어옵니다.

    하지만 그 이상의 격리수준인 경우 아직 커밋되지 않았기 때문에 변경되기 전의 내용인 언두 로그의 데이터를 반환합니다.

    즉, 하나의 레코드에 대해 2개의 버전이 유지되고 필요에 따라 어느 데이터가 보여지는지 여러 가지 상황에 따라 달라지는 구조입니다.

    참고로, 여기서는 하나의 데이터만 가지고 설명했지만 관리해야 하는 예전 버전의 데이터가 무한히 많아질 수 있습니다.

    만약 UPDATE 쿼리 이후에 COMMIT 이 되면 InnoDB는 현재 버퍼풀에 있는 상태를 영구적인 데이터로 만듭니다. 하지만 롤백을 한다면 언두로그에 있는 백업된 데이터를 InnoDB 버퍼 풀로 다시 복구 하고 언두 로그의 내용을 삭제합니다.

    COMMIT 이 된다고 언두로그의 내용을 바로 삭제하는 것이 아닙니다. 더 이상 언두 로그를 필요로 하는 트랜잭션이 없을 때 이를 삭제합니다.

    격리 수준이 SERIALZABLE 이 아니면 INSERT 와 연결되지 않은 순수한 SELECT 작업은 다른 트랜잭셕의 변경 작업과 관계없이 항상 잠금을 대기하지 않고 바로 실행됩니다. 이를 잠금 없는 일관된 읽기 라고 표현 합니다.

    해당 기능은 위에 설명했던 언두 로그를 사용해 구현됩니다.

    잠금 없는 일관된 읽기 때문에 발생할 수 있는 부작용이 있습니다. 바로 잠금 없는 일관된 읽기를 지원하기 위해서 언두 로그를 삭제하지 못하고 계속 유지해야 하는 문제입니다.

    이를 방지하기 위해 가능한 한 빨리 롤백이나 커밋을 통해 트랜잭션을 완료하는 것이 좋습니다.

     

    댓글