본문 바로가기
Database

트랜잭션 격리 수준, 락, MVCC

by oncerun 2022. 9. 16.
반응형

트랜잭션 격리 수준

 

만약 트랜잭션에 대해 깊게 알아볼 일이 있다면 아마 격리 수준에 대해서 공부하지 않을까?

 

동시성에 대해서 고민해본다면 아마 트랜잭션들이 서로에 영향을 미치지 않도록 격리 수준을 설정해야 하기 때문이다. 

 

이와 같이 격리 수준(isolation level)에 대해 ANSI 표준으로 정의해 놓은 것들이 있다. 

 

  • Read Uncommited
  • Read Committed
  • Repeatable Read
  • Serializable

아래로 내려갈 수 록 격리 수준이 높다. 격리 수준이 높다는 것은 동시성은 감소하고 순차적으로 처리하지만 그만큼 안정성을 보장한다는 것을 의미한다. 반대로 격리 수준이 낮으면 동시성은 증가한다. 다만 격리 수준이 낮을수록 많은 문제가 발생할 수 있다.

 

Read uncommitted

 

 말 그대로 커밋되지 않은 읽기 수준이다. 다른 세션에서 수정 중인 데이터, 즉 커밋되지 않은 데이터를 또 다른 세션이 해당 데이터를 조회할 수 있다.  이것을 Dirty Read라 한다. 이는 데이터 정합성을 위협할 수 있는 심각한 문제를 초래한다.

 

Read Committed 

 

 커밋한 데이터만 읽을 수 있으며 따라서 Dirty Read는 발생할 수 없다. 하지만 Non-Repeatable Read는 발생할 수 있다고 한다.

예를 들어 데이터를 검색 중인 트랜잭션 A가 있다. 검색 중인 와중에 또 다른 트랜잭션이 해당 데이터를 수정하고 커밋하면 트랜잭션 A가 다시 해당 데이터를 조회했을 때 수정된 데이터가 조회된다. 

이 처럼 반복해서 같은 데이터를 읽을 수 없는 상태를 Non - Reapeatable Read라고 한다.

Repeatable Read

 

말 그대로 한 번 조회한 데이터를 반복해서 조회해도 같은 데이터가 조회되는 것을 말한다. 

다만 Phantom Read가 발생할 수 있는데, 조회 중 다른 트랜잭션이 데이터를 추가하고 커밋한다면 다시 반복하여 조회 시 결과 집합이 달라진다. 이를 Phantom Read라 한다. 

Serialiazble

 

가장 엄격한 트랜잭션 격리 수준이다. 이는 동시성 처리 성능이 급격히 떨어질 수 있다.

 

대부분의 애플리케이션은 동시성 처리가 중요하기 때문에 보통 Read committed 격리 수준을 기본으로 사용한다.

만약 더 높은 격리 수준이 필요하면 데이터베이스 트랜잭션이 제공하는 락 기능을 사용할 수 있다.

 

Lock

 

락 또한 여러 레벨의 락이 있지만 테이블 레벨의 락 기준으로 이해하려고 합니다. 

 

우선 LOCK은 트랜잭션의 순차적인 처리를 보장하기 위해 사용될 수 있습니다. 

락도 종류가 있는데 공유락(Share Lock)과 베타 락(Exclusive lock)으로 나누어질 수 있습니다.

 

공유락이란 데이터를 읽을 때 사용되는 락입니다. 예를 들면 select for update문을 이야기하는 것 같습니다.

즉 read lock이라는 소리죠. 다만 공유락끼리는 동시에 접근이 가능합니다. 이는 데이터를 읽기 트랜잭션에서 접근하는 것은 허용한다는 것입니다.

 

베타 락은 다른 말로 write lock이라고 합니다. 즉 데이터를 변경할 때 사용하며 트랜잭션이 완료될 때까지 락을 유지합니다.  이는 해당 트랜잭션이 완료될 때까지 어떤 트랜잭션도 진행할 수 없습니다.

 

락을 사용하게 되면 서로 필요한 데이터를 다른 트랜잭션에서 점유하고 서로 요구하는 데드록 같은 상황이나 서로 다른 트랜잭션이 경합하여 성능을 낮추는 상황이 발생할 수 있습니다. 

 

MVCC ( Muitiversion concurrency control)

 

결국 공유 데이터를 관리하는 데이터베이스들은 동시성에 대한 문제를 고민해야 합니다. 

따라서 다중 버전 동시성 제어라는 해결책을 적용시켰습니다.

이는 각각의 데이터베이스마다 MVCC를 구현한 것은 다르지만 개념은 같습니다.

 

간단히 말하면 오래된 버전과 새로운 버전의 데이터를 구별하여 저장하여 사용자들의 접근 시점에 따라 데이터를 제공해주는 것입니다.

 

데이터를 변경하는 것 대신 버전을 나누어 사용하는 것입니다. 

 

읽기 트랜잭션인 경우에는 접근 시점의 올바른 버전을 찾아 읽기 때문에 스스로 격리되는 효과를 가지고 있습니다. 

쓰기 트랜잭션인 경우 해당 데이터를 수정하는 동안 새로운 버전으로 기록되고 이때 접근되는 읽기 트랜잭션은 올드한 버전의 데이터를 가져가기 때문에 서로 스스로 격리되는 효과를 볼 수 있는 것 같습니다.

 

 

 

반응형

댓글