MVCC(Multi-Version Concurrency Control) 개념

 

 

MVCC(Multi Version Concurrency Cotnrol)란?

개념

MVCC(Multi-Version Concurrency Control)은 데이터베이스에서 동시성을 제어하는 방법 중 하나로, 다수의 사용자가 동시에 데이터베이스를 사용할 때 트랜잭션 간의 격리 수준을 유지하여 동시성을 제어한다.

 

MVCC의 가장 큰 목적은 Lock을 사용하지 않고 일관된 읽기를 제공하는 것이다.

 

동작 원리

MVCC는 데이터의 버전(snapshot)을 관리함으로써 동시성을 관리한다.

-> MVCC에서 Multi version인 이유가 하나의 데이터에 대해 여러 개의 버전이 동시에 관리된다는 의미이다.


예를 들어, 두  트랜잭션의 read-write 간 데이터를 읽는(read) 시점에 해당 데이터의 스냅샷을 유지하고 다른 트랜잭션이 동일한 데이터를 수정(write) 하는 작업을 할 수 있게 한다.

 

예시

1. 트랜잭션 A가 이름이 "T-shirt"인 상품을 조회하면, "T-shirt"의 현재 버전(읽기 시점의 데이터 상태)을 저장한다.
2. 동시에 트랜잭션 B가 "T-shirt"를 "Shoes"으로 수정할 경우, 새로운 "Shoes"라는 이름의 상품 버전(스냅샷)을 생성한다.
3. 트랜잭션 A는 자신이 조회하기 시작한 시점의 데이터("T-shirt")인 버전을 계속 읽게 되며, 수정 중인 데이터와는 격리된다.

 

MVCC와 Lock의 차이

Lock 기반 동시성 제어

보통 Lock을 이용한 제어 방식에서는 데이터에 접근을 제어하기 위해 테이블이나 row 자체에 lock을 걸어서 다른 트랜잭션에서 접근하는 것을 막아버리는 것을 통하여 동시성을 제어한다.
이는 결국 애플리케이션의 처리량을 낮추는 문제가 존재하고, 데드락 발생 가능성도 존재한다.

하지만 MVCC 기반의 동시성 제어는 read-write, read-read 간 작업에서는 데이터에 대한 다중 접근이 가능하면서도 각 트랜잭션이 일관된 데이터를 유지할 수 있도록 한다.

 

MVCC

MVCC는 동시에 실행되는 여러 트랜잭션이 데이터의 서로 다른 버전을 "볼" 수 있게 함으로써, 읽기 작업에서는 lock이 필요 없어도 동시성 제어가 가능하다.
각 트랜잭션은 읽는 시점에 저장한 스냅샷을 통해 데이터를 관리하기 때문에, 다른 트랜잭션이 데이터를 수정하더라도 서로 영향을 주지 않는다.
즉, MVCC 기반의 동시성 제어는 read-write, read-read 간 작업에서는 데이터에 대한 다중 접근이 가능하면서도 각 트랜잭션이 일관된 데이터를 유지할 수 있도록 한다.

 

격리 수준 별 MVCC 동작 방식 (MySQL 기준)

read uncommitted

DIRTY READ라고도 불리는 read uncommitted는 일반적인 db에서는 거의 사용하지 않는다.

READ UNCOMMITTED 격리 수준에서는 각 트랜잭션에서의 변경 내용이 commit이나 rollback 여부에 상관없이 다른 트랜잭션에서도 조회가 가능하다.
-> 커밋된 데이터만 읽는 MVCC는 read uncommitted 격리 수준에서는 사용하지 않는다.

더보기

어떤 트랜잭션에서 처리한 작업이 완료되지 않았는데도 다른 트랜잭션에서 볼 수 있는 현상을 바로 "Dirty read"라고 한다.
-> 이는 정합성에 많은 문제가 되는 격리 수준이다.

 

그래서 MySQL을 사용할 때 일반적인 애플리케이션에서는 격리 수준을 최소한 READ COMMITTED 이상으로 설정해 사용해야 한다.

 

read committed

각 트랜잭션은 커밋된 데이터만 읽기 때문에, read committed 격리 수준에서는 Dirty read가 발생하지 않는다. (특정 트랜잭션에서 데이터를 변경 시 commit 이 완료되어야만 다른 트랜잭션에서 조회가 가능하다.)

  • 변경 전 데이터를 언두 로그에 백업해 두고 커밋되기 전 다른 트랜잭션에서 이 데이터를 조회 시 언두 로그의 데이터를 보여준다.
  • "NON-REPEATABLE READ"라는 부정합의 문제가 존재한다.

 

예를 들어 사용자 B가 product 테이블에 name이 't-shirt'라는 상품을 조회하는데 이 시점에서는 해당 상품은 존재하지 않아 조회 결과가 없는데, 사용자 A가 product 테이블에 기존 상품의 이름을 't-shirt'로 update 하거나 새로운 상품을 insert 하고 commit 한 뒤에, 사용자 B의 동일 트랜잭션에서 다시 't-shirt' 상품을 조회하면 조회 결과를 얻을 수 있다.

사용자 B의 한 트랜잭션 내에서 똑같은 SELECT 쿼리를 실행했을 때는 항상 똑같은 결과를 가져와야 한다는 "REPEATABLE READ" 정합성에 어긋난다.

 

repeatable read

  • MySQL InnoDB 스토리지 엔진의 기본 격리 수준이다.
  • 트랜잭션이 시작되는 시점에 커밋된 데이터를 읽어 스냅샷을 저장한다.
  • 이 트랜잭션에서의 스냅샷 버전은 다른 트랜잭션에서 동일 데이터를 수정해도 스냅샷에 영향을 미치지 않기 때문에 동시성 제어를 할 수 있다.

InnoDB 스토리지 엔진은 트랜잭션이 rollback 될 가능성을 대비해 변경되기 전 데이터를 undo 공간에 백업해 두고 실제 데이터 값을 변경한다.

동일 트랜잭션 내에서 같은 데이터를 여러 번 조회할 경우 일관성 있는 데이터를 조회한다. (NON-REPEATABLE READ 문제 X)

 

serializable

  • 가장 단순하면서 가장 엄격한 격리 수준 레벨이다. (동시 처리 성능 down)
  • MVCC를 사용하지 않아 단순 읽기 작업에도 락을 얻어야 하기 때문에 성능이 떨어진다.