eelseungmin

Concurrency Control (4) - MVCC

by eelseungmin

들어가며

이번에는 대부분의 상용 RDBMS에서 단순히 Lock으로만 동시성 제어를 할 때보다 처리량을 끌어올리기 위해 사용하는  MVCC에 대해 알아보도록 하겠다. 물론 각 RDBMS마다 동작 방식은 다를 수 있으며, 여기서는 MySQL을 기준으로 설명한다.

 

MVCC(Multiversion Concurrency Control)

제어 기법에 따른 두 Lock 간 호환성

Lock

  Read Lock Write Lock
Read Lock O X
Write Lock X X

MVCC

  Read Lock Write Lock
Read Lock O O
Write Lock O X

Lock으로만 동시성 제어를 했을 때와 비교해 보면 표만 봐도 트랜잭션 처리량이 더 많으리라는 것을 직감할 수 있다.

 

MVCC 이해를 위한 예제

예제를 보기 전에 먼저 알아둬야 할 게 있다.

1. MVCC에선 데이터를 읽을 때 해당 시점에서 가장 최근에 commit된 데이터를 읽는다.

2. write 작업 시 곧바로 DB에 반영하는 것이 아닌 해당 트랜잭션에서만 조회 가능한 별도의 공간에 임시로 저장했다가 commit 시점에 저장해 둔 데이터를 DB에 반영한다.

3. read와 write가 서로를 허용한다.

여기서 Isolation Level에 따라 동작이 조금씩 다른데 MySQL, PostgreSQL 기준으로는 다음과 같다.

 

Read Uncommited(MySQL)

해당 Level에서는 MVCC가 동작하지 않는다.

 

Read Uncommited(PostgreSQL)

해당 Level이 존재하기는 하지만 실제로는 Read Committed처럼 동작한다.

 

Read Committed(MySQL==PostgreSQL)

read 작업 시점을 기준으로 이전에 commit된 데이터를 조회한다.

즉, 첫 번째 read에서 x는 10이고, 두 번째 read에서 x는 50이다.

 

Repeatable Read(MySQL==PostgreSQL)

해당 트랜잭션이 시작된 시점을 기준으로 이전에 commit된 데이터를 조회한다.

즉, 첫 번째 read에서 x는 10이고, 두 번째 read에서도 x는 10이다.

 

Serializable(MySQL)

MVCC가 아닌 Lock 기법으로 동작한다.

트랜잭션의 일반적인 select 문은 개발자가 SELECT ... 로 작성하더라도 SELECT ... FOR SHARE처럼 동작한다.

 

Serializable(PostgreSQL)

SSI(Serializble Snapshot Isolation) 기법이 적용된 MVCC로 동작한다.

 

Lost Update가 발생하는 예제

해당 예제는 MySQL 기준이고 두 트랜잭션 모두 Repeatable Read다.

tx1의 첫 번째 write 작업은 tx2가 해당 시점에 x에 대한 write lock을 보유하고 있으므로 block되어, commit까지 대기하다가 실행되는데 이때 x가 10으로 덧씌워지면서 tx2가 x를 80으로 write했던 결과가 사라지게 된다.(Lost Update 발생)

 

해결 방법

해당 예제에서 Locking Read라는 기법을 추가로 사용하게 되는데, 이는 그림에 나온 것처럼 read 작업과 동시에 exclusive lock을 획득하는 것이다.

따라서 그림에서 tx1의 첫 번째 read 작업은 block된다. tx2에서 Locking Read를 통해 write lock을 쥐고 있기 때문이다.

 

Locking Read를 추가로 설명하자면 다음과 같다.

SELECT ... FOR UPDATE;(Exclusive Lock)

SELECT ... FOR SHARE;(Shared Lock)

블로그의 정보

eel.log

eelseungmin

활동하기