본문 바로가기
CS/DataBase

[Database] MVCC 란?

by gamxong 2024. 11. 23.

0. 한 문단 요약

1. MVCC(Multi-Version Concurrency Control)는 데이터베이스에서 동시성을 효율적으로 관리하고, 트랜잭션 격리 수준데이터 일관성을 보장하기 위한 기술

2. 데이터를 여러 버전으로 저장하여 읽기 작업과 쓰기 작업이 동시에 수행가능

3. 이를 통해 잠금 경합 문제를 줄이고, 성능과 데이터 일관성을 동시에 확보

 


1. 동시성 제어란?

먼저 MVCC(다중 버전 동시성 제어)를 알기 위해선 "다중 버전"을 제외한 "동시성 제어"에 대해 명확히 알 필요가 있습니다.

 

동시성 제어 : 데이터베이스 관리 시스템(DBMS)에서 여러 사용자가 동시에 데이터에 접근하거나 트랜잭션을 수행할 때 발생할 수 있는 문제를 해결하고, 데이터의 일관성과 무결성을 유지하기 위한 기술입니다.

 

데이터베이스에서는 ACID 속성을 준수하기 위해 동시성 제어는 필수적입니다. 

많은 사용자가 동시에 동일한 데이터를 읽거나 수정하면 충돌이 발생할 수 있고, 데이터의 일관성이 깨질 수 있기 때문입니다.

 

이 동시성을 제어하는 방법에 MVCC가 포함되어 있습니다.

먼저 MVCC 전에 있었던 낙관적 동시성 제어, 비관적 동시성 제어에 대해 알아봅시다.

그리고 어떤 문제를 해결하기 위해 MVCC 가 도입되었는지 알아보겠습니다.

 

2. MVCC 가 나오게 된 배경 (MVCC 이전의 동시성 제어)

2 - a. 낙관적 동시성 제어

  • 충돌이 드물 것이라는 가정 하에, Lock을 사용하지 않고 작업을 진행
  • 트랜잭션의 마지막 단계(커밋 시점)에서 충돌 검사를 수행
  • 충돌 일어날 경우 롤백

2 - b. 비관적 동시성 제어

  • 충돌이 빈번할 것이라고 가정하고, 데이터를 수정하거나 읽는 동안 Lock을 통해 충돌을 예방
  • Lock 공유락 베타락으로 나뉨.
  • 공유락 : 읽기 잠금, 데이터를 읽는 동안 다른 트랜잭션의 쓰기를 막음.
  • 베타락 : 쓰기 잠금, 데이터를 수정하는 동안 다른 트랜잭션의 읽기와 쓰기를 모두 막음.

2 - c. 낙관적 vs 비관적

  • 동시에 업데이트 하는 경우가 많지 않으면, 낙관적 동시성 제어를 통해 많은 트랜잭션을 처리할 수 있습니다.
  • 동시에 업데이트 하는 경우 많다면, 비관적 동시성 제어를 사용하는 것이 더 많은 트랜잭션을 처리할 수 있습니다. 낙관적 제어는 충돌이 일어날 경우 롤백으로 인해 트랜잭션 처리양이 줄어들기 때문입니다.
  • 하지만 비관적 동시성 제어도 읽기-쓰기 경합으로 인해 동시성이 저하되고, Lock 방식으로 인해 데드락 상황이 발생할 수도 있다는 단점이 있다.
한줄 요약 : 충돌이 잦을 때는 낙관적 제어보다 비관적 제어가 유리하다. 하지만 비관적 제어도 Lock 방식으로 인해 성능이 떨어지는 것은 매 한가지이다.

 

3. MVCC에 대해 자세히 알아보자

MVCC는 데이터를 수정할 때 기존 데이터를 덮어쓰지 않고 새로운 버전을 생성하여 관리합니다. 기존 데이터는 Snapshot 으로서 저장하고 있다가 변경이 취소되면, 해당 Snapshot을 바탕으로 복구하는 방식입니다.

 

결국 MVCC는 스냅샷을 이용하는 방식으로, 기존의 데이터를 덮어 씌우는게 아니라 기존의 데이터를 바탕으로 이전 버전의 데이터와 비교해서 변경된 내용을 기록합니다. 이렇게 해서 하나의 데이터에 대해 여러 버전의 데이터가 존재하게 되고, 사용자는 마지막 버전의 데이터를 읽게 됩니다.

 

3-1. MVCC 특징

1. 높은 동시성 : 읽기와 쓰기가 경합하지 않으므로 다중 사용자가 동시에 작업하더라도 성능이 유지

2. 잠금 경합 감소 : 읽기 작업이 잠금을 요구하지 않으므로 교착 상태와 같은 문제를 최소화

3. 가비지 컬렉션 필요 : 더 이상 참조되지 않는 오래된 데이터 버전을 삭제해야 하며, 이 과정에서 리소스가 추가로 소모

4. 데이터 버전이 충돌하면 애플리케이션 영역에서 이러한 문제를 해결해야 함

 

MVCC 방식은 특정 트랜잭션에서 읽기를 시작할 때, 다른 사람이 해당 데이터를 수정하더라도 영향을 받지 않고 데이터를 읽을 수 있다. 다시 말해, 읽기-쓰기 경합이 일어나지 않는다는 것입니다. 이로 인해 성능 저하가 없으며 교착 상태와 같은 문제를 최소화할 수 있습다. 하지만 다중 버전을 유지하기 위해 가비지 컬렉션이 필요합니다. MVCC 모델은 하나의 데이터에 대한 여러 버전의 데이터를 허용하기 때문에 데이터 버전이 충돌될 수 있으므로 애플리케이션 영역에서 이러한 문제를 해결해야 합니다.

3-2. MYSQL에서의 MVCC

1. Undo Log를 사용한 데이터 버전 관리

  • MySQL은 테이블의 각 행에 대해 다중 버전 데이터를 유지합니다.
  • 데이터가 수정되면, 이전 버전의 데이터는 Undo Log에 저장됩니다. Undo Log는 롤백과 MVCC를 위해 사용됩니다.
  • 트랜잭션은 자신이 시작된 시점의 데이터 버전을 읽기 때문에, 다른 트랜잭션의 수정이 완료되지 않아도 영향을 받지 않습니다.

 

 

2. 격리 수준과 MVCC

  • Read CommittedRepeatable Read에서 MVCC가 동작합니다:
    • read Uncommitted : 커밋되지 않은 데이터를 읽을 수 있기 때문에, 버퍼 풀에 있는 최신 데이터를 읽음.
    • Read Committed, Repeatable Read: 트랜잭션은 커밋된 최신 데이터를 읽음.
  • Serializable에서는 MVCC 대신 잠금 기반 제어를 사용합니다.

3-3. 구체적인 예시

CREATE TABLE accounts (
    id INT AUTO_INCREMENT PRIMARY KEY,
    balance INT NOT NULL
);
INSERT INTO accounts (balance) VALUES (100), (200), (300);

 

이와 같은 데이터가 세팅되어있다고 가정해보겠습니다.

 

이 상황에서 A 트랜잭션에서 아래와 같은 쿼리 실행시킬 경우, 상태는 아래 그림과 같이 변경됩니다.

UPDATE accounts SET balance = 150 WHERE id = 1;

 

 

  • 먼저 COMMIT 실행 여부와 무관하게 InnoDB 버퍼 풀은 새로운 값으로 갱신됩니다. 그리고 Undo 로그에는 변경 이전의 값이 복사됩니다. 그리고 InnoDB 버퍼 풀의 내용은 백그라운드 쓰레드를 통해 디스크에 기록되는데, 디스크에도 반영되었는지 여부는 시점에 따라 다를 수 있어서 ?로 표시했습니다.
  • 버퍼 풀에 저장된 최신 데이터를 디스크로 실시간 반영을 하지 않는 이유는 I/O의 과부하를 줄이고 성능을 최적화하기 위해서라고 합니다.

이 상황에서 B 트랜잭션(다른 트랜잭션)이 해당 데이터를 조회할 경우, MySQL의 격리 수준에 따라 조회 결과가 달라집니다.

SELECT * FROM accounts WHERE id = 1;

 

  • READ_UNCOMMITTED : 이 격리 수준은 커밋되지 않은 데이터를 읽을 수 있기 때문에, 버퍼 풀에 있는 최신 데이터인 “150”을 조회합니다.
  • READ_COMMITTED : 커밋된 데이터만 읽을 수 있으므로, 커밋되지 않은 “150”이 아닌 Undo 로그에 기록된 “100”을 조회합니다.

여기서 Undo Log 영역의 데이터는 커밋 혹은 롤백을 호출하여 InnoDB 버퍼풀도 이전의 데이터로 복구되고, 더 이상 언두 영역을 필요로 하는 트랜잭션이 더는 없을 때 비로소 삭제됩니다.

 

 

 

출처:

https://mangkyu.tistory.com/53

 

 

'CS > DataBase' 카테고리의 다른 글

[DB] 제 3 정규화 vs BCNF  (0) 2023.05.29
관계대수에서 assignment 과 rename의 차이?  (0) 2023.03.14
SQL 함수  (0) 2023.03.14
WHERE 조건 절을 활용한 데이터 검색  (1) 2023.03.14
SELECT 문의 기본 문법  (0) 2023.03.14

댓글