Databases

데이터베이스 동시성 제어: MVCC

진공청소ㄱl 2025. 2. 23. 21:02

MVCC

MVCC는 데이터베이스에서 동시성을 제어하는 주요 방식 중 하나로, 데이터의 여러 버전을 유지하면서 읽기 작업과 쓰기 작업이 서로를 차단하지 않도록 하는 기법이다. 전통적인 잠금 기반의 동시성 제어와 달리, MVCC는 각 트랜잭션이 데이터의 특정 시점 스냅샷을 보게 함으로써 더 높은 동시성을 제공한다.

 

즉, MVCC의 핵심 원리는 데이터를 수정할 때 새로운 버전을 생성하는 것이다. 

 

Redo

  • 데이터베이스에서 발생하는 변경 작업을 기록하는데 사용
  • 변경 작업이 수행될 때마다 Redo 로그 버퍼에 기록되고, 일정 크기나 시간에 도달하면 디스크에 영구 저장
  • 데이터 복구시에 사용

Undo

  • 트랜잭션의 롤백을 지원하고, 일관성 및 격리 수준을 유지하기 위해 사용된다.
  • 트랜잭션 실행 도중 변경된 데이터는 Undo 세그먼트에 저장되어 이전 상태로 롤백될 수 있다.
  • Undo 데이터는 사용자에게 일관된 데이터 뷰를 제공하고 동시성 제어를 위해 사용된다.

 

mysql의 MVCC 구현 방식

위와 같이 Member 테이블에 값이 있다고 가정한다. 이는 메모리와 디스크에 모드 해당 데이터가 동일하게 저장되는 상태이다.

 

그리고 다음과 같이 update 쿼리를 실행시켰다고 가정한다.

UPDATE member SET area = "경기" WHERE id = 1;

 

update 쿼리를 실행하면 버퍼 풀에서 먼저 데이터가 변경된다. area 값이 "서울"에서 "경기"로 변경되며, 이 변경사항은 즉시 버퍼 풀에 반영된다.

 

변경 전 데이터("서울")는 Undo 로그에 저장되는데, 이는 다음과 같은 목적으로 사용된다.

  • 트랜잭션 롤백 시 데이터 복구
  • 다른 트랜잭션의 일관된 읽기 제공
  • MVCC 구현을 위한 이전 버전 데이터 유지

또한 변경 작업 자체는 Redo 로그에 기록된다.

 

트랜잭션 1

START TRANSACTION;
UPDATE member SET area = "경기" WHERE id = 1;
-- 아직 커밋되지 않음

 

트랜잭션 2

START TRANSACTION;
SELECT * FROM member WHERE id = 1;  

 

트랜잭션 2는 격리 수준에 따라 다음과 같이 동작한다.

  • READ UNCOMMITTED: 버퍼 풀의 변경된 값("경기")을 읽음
  • READ COMMITTED: Undo 로그에서 최근 커밋된 값("서울")을 읽음
  • REPEATABLE READ: 트랜잭션 시작 시점의 값("서울")을 읽음
  • SERIALIZABLE: 해당 레코드에 대한 공유 잠금 획득 후 읽음

 

Purge

데이터베이스 시스템에서 MVCC를 구현하면서 발생하는 가장 큰 과제는 오래된 데이터 버전의 관리이다. MySQL InnoDB에서는 데이터가 수정될 때마다 이전 버전의 데이터를 Undo 로그에 보관하게 되는데, 이러한 데이터가 계속 쌓이면 저장 공간 낭비와 성능 저하를 초래할 수 있다. 이러한 문제를 해결하기 위해 Purge 시스템이 필요하며, 이는 더 이상 필요하지 않은 데이터 버전을 자동으로 정리하는 역할을 수행한다.

 

Purge 시스템의 주요 대상은 Undo 로그이다. Undo 로그는 트랜잭션의 변경사항을 기록하고, MVCC를 위한 이전 버전 데이터를 저장하며, 필요한 경우 롤백을 위한 정보를 보관한다. Purge 시스템은 History List라고 불리는 커밋된 트랜잭션의 undo 로그 페이지 목록을 참조하여 작업을 수행한다. 이 목록을 순차적으로 파싱하면서 더 이상 필요하지 않은 undo 로그 레코드를 식별하고 제거한다.

 

PostgreSQL의 MVCC 구현 방식

PostgreSQL의 MVCC는 MySQL과는 다른 방식으로 구현된다. PostgreSQL은 튜플 자체에 버전 정보를 포함시키는 방식을 사용하며, 각 행(튜플)은 여러 시스템 컬럼을 포함한다. mvcc와 관련된 컬럼으로는  xmin, xmax 라는 컬럼이 숨겨져 있는데, xmin 해당 레코드에 insert 또는 update 쿼리가 수행된 마지막 트랜잭션 아이디, xmax에는 해당 레코드에 delete 또는 update쿼리가 수행된 마지막 트랜잭션 아이디를 의미한다.

 

INSERT 연산과 버전 관리

 

새로운 행이 삽입될 때, PostgreSQL은 새로운 튜플을 생성하고 여기에 실제 데이터(예: balance가 500$)와 함께 버전 정보를 기록한다. xmin에는 현재 트랜잭션 ID(40)가 할당되며, 이는 이 튜플을 생성한 트랜잭션을 식별한다. xmax는 비어있는 상태로 유지되는데, 이는 해당 행이 아직 삭제되지 않았음을 의미한다.

 

UPDATE 연산의 처리

데이터 수정이 발생하면 PostgreSQL은 이를 두 단계로 처리한다. 먼저 기존 튜플(balance: 500$)의 xmax에 현재 트랜잭션 ID(41)를 설정하여, 이 버전이 트랜잭션 41에 의해 '논리적으로 삭제'되었음을 표시한다. 그 다음, 새로운 튜플을 생성하여 수정된 데이터(balance: 600$)를 저장하고, 이 튜플의 xmin에 현재 트랜잭션 ID(41)를 설정한다. 새로운 튜플의 xmax는 비어있는 상태로 유지되어, 이것이 현재 유효한 버전임을 나타낸다.

 

DELETE 연산과 버전 관리

삭제 작업 역시 실제로 데이터를 물리적으로 제거하지 않는다. 대신, 해당 튜플의 xmax에 현재 트랜잭션 ID(42)를 설정하여 논리적 삭제를 표시한다. 이 예시에서는 첫 번째 튜플(balance: 500$)이 이미 트랜잭션 41에 의해 논리적으로 삭제된 상태이며, 두 번째 튜플(balance: 600$)은 트랜잭션 42에 의해 삭제 표시된다.

 

트랜잭션 가시성과 격리 수준

PostgreSQL은 트랜잭션 가시성을 판단할 때 특정 규칙을 적용한다. 튜플이 특정 트랜잭션에서 보이려면 해당 튜플의 xmin이 이미 완료된 트랜잭션이어야 하며, xmax가 0이거나 아직 완료되지 않은 트랜잭션이어야 한다. 이러한 규칙은 격리 수준에 따라 다르게 적용되며, 예를 들어 READ COMMITTED 격리 수준에서는 다른 트랜잭션이 변경 중인 데이터의 이전 버전을 읽게 된다.

VACUUM

PostgreSQL은 Dead tuple을 정리하기 위해 VACUUM이라는 메커니즘을 사용한다. 일반 VACUUM은 더 이상 필요 없는 튜플을 재사용 가능하도록 표시하며, 테이블에 대한 접근을 차단하지 않고 수행된다. 반면 VACUUM FULL은 테이블을 완전히 재작성하는 작업으로, 테이블 전체에 대한 잠금이 필요하다. 이러한 VACUUM 작업을 통해 PostgreSQL은 저장 공간을 효율적으로 관리하고 시스템 성능을 유지한다.

 

이러한 방식으로 PostgreSQL은 MVCC를 구현하며, 동시성과 일관성을 모두 보장한다. MySQL의 Undo 로그 기반 MVCC와 비교했을 때, PostgreSQL의 방식은 튜플 자체에 버전 정보를 포함시킴으로써 더 직관적인 버전 관리를 가능하게 한다.

 

참고

MVCC :: 알고풀자

 

MVCC

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

algopoolja.tistory.com

 

RDS MySQL 과 Aurora MySQL 에서 Innodb purge 작업 최적화 하기 | AWS 기술 블로그

 

RDS MySQL 과 Aurora MySQL 에서 Innodb purge 작업 최적화 하기 | Amazon Web Services

이 글은 AWS Database Blog에 게시된Achieve a high-speed InnoDB purge on Amazon RDS for MySQL and Amazon Aurora MySQL by Lei Zeng을 한국어 번역 및 편집 하였습니다. Purge 는 MySQL 데이터베이스의 정리 작업입니다. InnoDB 스

aws.amazon.com

 

MySQL :: MySQL 8.4 Reference Manual :: 17.8.9 Purge Configuration

 

MySQL :: MySQL 8.4 Reference Manual :: 17.8.9 Purge Configuration

17.8.9 Purge Configuration InnoDB does not physically remove a row from the database immediately when you delete it with an SQL statement. A row and its index records are only physically removed when InnoDB discards the undo log record written for the del

dev.mysql.com

 

Postgresql에서의 MVCC와 VACUUM

 

Postgresql에서의 MVCC와 VACUUM

지난 글에서 tid라는 것에 대해 간단하게 소개를 했다. tid는 튜플(레코드)의 물리적인 위치라고 알고있는데, 값이 종종 변경되는 것을 보며 어째서 변경이 되는 것인지 궁금하여 찾아보게 됐다. 

joyfulviper.tistory.com

 

PostgreSQL: The world's most advanced open source database

 

PostgreSQL

The world's most advanced open source database.

www.postgresql.org