카테고리 없음

[DB] 레전드 MVCC

엄지성 2025. 2. 7. 18:52

이번에 말하는 MVCC는 Multi Concurrency Control의 기능으로 일반적으로 레코드 레벨의 트랜잭션을 지원하는 RDMS가 제공하는 기는 중 하나로 이 기능의 목적은 잠금을 사용하지 않아도 일관되게 읽기를 가능하게 하는 것이 큰 목적이다.

 

InnoDB는 Undo log를 사용해 이 기능을 구현하였는데 하나의 레코드에 대해 여러 개의 버전이 동시에 관리한다는 것이다.

이 기능을 설명하기 위해 상황을 가정해 보자면 격리 수준을 커밋된 데이터만 조회가능한 READ_COMMITTED인 MySQL 서버에 InnoDB가 어떻게 작동할까?

 

우선 테이블을 생성한 후 데이터를 넣어보자.

mysql> CREATE TABLE member (
           m_id INT NOT NULL,
           m_name VARCHAR(20) NOT NULL,
           m_area VARCHAR(20) NOT NULL,
           PRIMARY KEY (m_id),
           INDEX ix_area (m_area)
	   );

mysql> INSERT INTO member (m_id, m_name, m_area) VALUES (12, '홍길동', '서울');
mysql> COMMIT;

 

그러면 데이터베이스는 

4.10 데이터베이스 상태

그리고 변화를 보기 위해 한번 더 쿼리를 날리면

mysql> UPDATE member SET m_area='경기' WHERE m_id=12;

 

변화를 보기 위해 UPDATE를 날리게 되면

Update문 후 상태

UPDATE 쿼리를 보내게 되면 COMMIT과 상관없이 InnoDB 버퍼 풀은 새로운 값으로 변경되게 되는데 디스크의 m_area는 체크포인트나 InnoDB의 Write 스레드에 의해 업데이트가 될 수도 있고 안 될 수도 있습니다.(거의 버퍼 풀에 있는 데이터와 데이터 파일에 있는 데이터는 동일한 상태일 경우가 대부분이다.) 

 

그럼 이런 상태에서 다른 사용자가 조회를 하게 된다면 어떻게 될까?

 

이건 우리가 눈으로 볼 때는 알 수 없다. 직접 MySQL의 시스템 변수 중에 transaction_isolation)에 설정되어 있는 Isolation_level에 따라서 결정된다. 만약 격리 수준이 제일 낮은 READ_UNCOMMITTED인 경우에는 버퍼 풀이 현재 가지고 있는 데이터를 반환하게 된다. 그렇지 않고 READ_COMMITTED나 그 이상의 격리 수준은 아직 커밋이 된 데이터는 아니기 때문에 언두 로그에 있는 변경되지 않은 데이터를 반환하게 된다. 즉, 하나의 레코드가 2개의 버전이 유지되고 설정이나 필요에 따라 어떤 데이터를 반환할지 달라지게 하는 기능이 RDMS에서 MVCC라고 한다.

 

예시에서는 하나의 레코드를 예시로 테스트하며 보여줬지만 실제에서는 언두 로그에 트랜잭션이 길어지고 언두 로그영역에서 삭제되지 못하는 데이터들이 무수히 많아져 언두 영역이 저장되는 시스템 테이블스페이스의 공간이 많이 늘어나는 상황이 생긴다.

 

지금까지의 과정을 보면 UPDATE 쿼리를 실행하면 버퍼 풀은 즉시 쿼리로 바뀐 데이터로 변경되고 기존의 데이터는 언두영역으로 복사되는 과정이었고 COMMIT을 하게 되면 변경작업 없이 지금의 상태를 바로 영구적으로 만든다. 혹시 만약의 상황에 롤백을 해야 된다면 언두영역에 있는 백업된 데이터를 버퍼 풀로 다시 복구 후 언두영역의 내용을 없애버린다. 커밋된다고 언두영역에 데이터가 바로 삭제되는 것이 아닌 필요로 하는 트랜잭션이 없어질 때 삭제되어 없어진다.

 

Non-Locking Consistent Read

InnoDB는 위에 설명한 MVCC 기능을 이용하여 잠금을 걸지 않아도 읽기 기능을 수행할 수 있다. 잠금을 걸지 않기 때문에 빠르게 읽기 기능을 수행하여 빠르게 응답이 가능하다. 격리 수준 중에 SERIALIZABLE이 아닌 아래 단계인 경우 INSERT와 연결되지 않은 순수 읽기 작업은 다른 트랜잭션의 변경 작업을 신경 쓰지 않고 바로 실행이 가능하다.

 

위에 나온 이미지의 경우 언두 로그의 데이터를 읽어온다는 것은 격리 수준이 READ_UNCOMMITTED보다 높다는 것을 알 수 있다. 만약 READ_UNCOMMITTED인 경우에는 언두 로그를 지나가는 것이 아닌 InnoDB의 버퍼 풀에서 데이터를 읽는다는 예상이 가능하다.

 

위에 이미지를 보면 누군가 UPDATE를 한 것을 볼 수 있는데 그럼에도 불구하고 SELECT를 한 사람은 전혀 영향끼치지 않고 읽기 작업을 수행한 것을 볼 수 있다. 이것을 잠금 없는 일관된 읽기라고 말한다. InnoDB에서는 변경되기 전 데이터를 읽기 위해선 언두 로그를 사용한다.

 

만약 오랜 시간동안 트랜잭션이 종료되지 못하고 작업 중인 경우가 있어 MySQL 서버가 느려지거나 문제가 발생하는 경우도 존재하는데 이때 일관된 읽기를 위해서 언두 로그를 삭제하지 못하고 계속 유지해 발생하는 문제이다. 그래서 트랜잭션이 시작되었다면 최대한 커밋 또는 롤백을 통해 완료하는 것이 가장 좋다.