[DB] Transaction Isolation Level?? 그게 뭔데?

chiyongs·2022년 6월 29일
5

DB

목록 보기
2/2
post-thumbnail

Transaction Isolation Level

지난 글에 이어서 이번 글에서는 Transaction Isolation Level(트랜잭션 격리 수준)에 대해 알아보겠습니다.

정의

트랜잭션 격리 수준은 동시에 여러 트랜잭션이 처리될 때 특정 트랜잭션이 다른 트랜잭션에서 변경하거나 조회하는 데이터를 볼 수 있도록 허용할지 말지를 결정하는 것입니다.
그렇다면, 트랜잭션 격리 수준 개념은 왜 등장했을까요??
전 글에서 다룬 트랜잭션의 ACID(원자성, 일관성, 고립성, 지속성)특성을 모두 다 지키려고 노력하다보면 동시성에 대한 성능이 저하됩니다. 따라서, 트랜잭션 격리 수준을 나누어 ACID 특성을 너무 타이트하게 지키지 않게끔 만들어줍니다.
많은 데이터베이스 어플리케이션들은 높은 트랜잭션 격리 수준을 사용하지 않으려고 합니다.
왜냐하면,

  • 동시성 처리에 대한 성능이 떨어집니다.
  • Deadlock이 발생할 확률이 높아집니다.
  • Lock으로 인한 시스템 오버헤드가 증가합니다.

추가로, DBMS는 MVCC(Multiversion Concurrency Control)을 구현하거나 Lock을 사용(lock-based concurrency control)해야 합니다.

동시성에 대한 성능이 떨어지는 이유

동시성에 대한 성능이 떨어지는 이유는 트랜잭션이 ACID 특성을 모두 지키고 있다면 각각의 원자성과 고립성을 지켜야하므로 한 트랜잭션이 데이터 X를 점유하고 있다면 그 외의 모든 트랜잭션은 전부 대기를 해야하기 때문입니다.

Isolation Level 종류

트랜잭션 격리 수준은 크게 4가지입니다.

  • Read Uncommitted
  • Read Committed
  • Repeatable Read
  • Serializable

Read Uncommitted

Read Uncommittedcommit이나 rollback에 상관없이 트랜잭션의 데이터 변경 내용을 다른 트랜잭션이 읽는 것을 허용하는 트랜잭션 격리 수준입니다.
따라서, Dirty Read가 발생합니다.
Dirty Read는 트랜잭션의 작업이 완료되지 않았는데도 다른 트랜잭션에서 해당 데이터를 읽는 현상을 말합니다.
Read Uncommitted는 데이터 정합성에 문제가 생기는 격리 수준입니다.
따라서, 데이터 정합성이 중요하다면 해당 격리 수준은 사용하지 말아야 합니다.
아래 예시처럼 Transaction A에서의 데이터 변경이 commit되지 않았는데 Transaction B에서 변경된 데이터를 조회해서 사용 가능합니다.
Read Uncommitted 예시

Read Committed

Read Committed는 트랜잭션이 commit, rollback 으로 완료되면 다른 트랜잭션에서 조회가 가능한 트랜잭션 격리 수준입니다.
Dirty Read가 발생하지 않으며, Undo영역의 백업된 레코드에서 값을 가져옵니다.
많은 RDB 서비스에서 가장 많이 사용하는 트랜잭션 격리 수준입니다.
하지만, Non-Repeatable Read가 발생합니다.

Non-Repeatable Read

Non-Repeatable Read는 한 트랜잭션 안에서 똑같은 SELECT 쿼리(read)를 실행했을 때 항상 같은 결과를 가져오지 않는 현상입니다. Lock을 통해 동시성을 제어하는 방식(lock-based concurrency control)에서는 SELECT 쿼리 실행 시에 read lock을 획득하지 않거나, 획득한 lock이 SELECT 쿼리를 실행하자마자 해제될 때 발생합니다.
Non-Repeatable Read를 예방하는 방법으로는 두 가지 방법이 존재합니다.
1. Transaction A가 commit이나 rollback이 될 때까지 Transaction B의 실행을 지연하는 것입니다.
2. Multiversion Concurrency Control(MVCC)을 사용합니다.

Non-Repeatable Read의 데이터 부정합 문제는 일반적인 서비스에서는 큰 문제가 되지 않지만, 금전을 다루는 서비스에서 특히 큰 문제가 발생할 수 있습니다.
👉 Undo 영역은 트랜잭션의 격리 수준에 사용될 뿐 아니라 트랜잭션 rollback이 발생했을 때 복구에도 사용됩니다.

아래 예시처럼 동일한 SELECT 쿼리에 다른 결과를 가져오는 경우가 발생합니다.
Read Committed 예시

Repeatable Read

Repeatable Read는 트랜잭션 범위 내에서 조회한 내용이 항상 동일함을 보장하는 트랜잭션 격리 수준입니다. 또한, MySQL의 InnoDB에서 기본으로 사용하는 트랜잭션 격리 수준입니다. 트랜잭션은 고유한 번호를 가지며 Undo 영역에 백업된 모든 레코드에는 변경을 발생시킨 트랜잭션의 번호가 포함되어 있습니다. 하나의 트랜잭션 안에서 일어나는 모든 SELECT 쿼리는 자신의 트랜잭션 번호보다 작은 트랜잭션 번호에서 변경한 사항들만 볼 수 있습니다. 이러한 방식을 MVCC라고 합니다.

MVCC(Multiversion Concurrency Control)는 다중 버젼 동시성 제어의 약자로 DBMS에서 서로 다른 세션이 동일한 데이터에 접근했을 때 각 세션마다 스냅샷 이미지를 보장해주는 매커니즘입니다.
RDBMS에서 동시성을 높이기 위해 등장한 기술입니다.
MVCC의 가장 큰 목적은 Lock을 사용하지 않는 일관된 읽기를 제공하기 위함입니다.
또한, 데이터의 여러 버전을 유지함으로써, 다수의 트랜잭션이 동시에 데이터에 접근할 수 있어 동시성을 크게 향상시켜줍니다.
하지만, 추가적인 저장공간이 필요하며 오래된 데이터 버전을 정리하는 과정이 필요합니다.

Undo 영역에는 하나의 레코드에 대한 여러 개의 백업이 존재할 수 있습니다. DBMS는 백업된 데이터가 불필요하다고 판단되는 시점에 주기적으로 삭제합니다.
Repeatable Read 예시

Phantom Read

하지만 이런 Repeatable Read에서도 Phantom Read라는 데이터 부정합 문제가 발생할 수 있습니다.
Phantom Read는 다른 트랜잭션에서 수행한 변경 작업에 의해 레코드가 보였다가 안 보였다가 하는 현상입니다. Non-Repeatable Read의 한 가지 경우이며 Transaction A가 범위를 조회하는 SELECT...WHERE 쿼리를 반복적으로 사용할 때, 그 쿼리들 사이에서 Transaction B가 해당 WHERE절을 만족하는 새로운 행을 생성했을 때 발생합니다.
아래 예시의 SELECT... FOR UPDATE 쿼리는 해당 레코드에 write lock을 거는데, Undo 영역에는 Lock을 걸 수 없기 때문에 현재 레코드의 값을 가져오게 됩니다.
Phantom Read 예시

여담으로, InnoDB는 갭 락과 넥스트키 락 덕분에 Repeatable Read에서도 Phantom Read를 예방할 수 있습니다.

Serializable

Serializable는 모든 작업을 하나의 트랜잭션에서 처리하는 것과 같이 동작하는 가장 높은 트랜잭션 격리 수준입니다.
한 트랜잭션에서 읽고 쓰는 레코드를 다른 트랜잭션에서는 절대 접근할 수 없습니다.
따라서, Phantom Read 문제가 발생하지 않습니다. 하지만, Deadlock에 걸릴 가능성이 존재하고 동시성 처리 성능이 저하되기 때문에 신중하게 사용해야 합니다.

트랜잭션 격리 수준과 발생 가능 문제

레벨Dirty ReadNon-Repeatable ReadPhantom Read
Read Uncommitted가능가능가능
Read Committed불가능가능가능
Repeatable Read불가능불가능가능
Serializable불가능불가능불가능

마무리

Transaction Isolation Level(트랜잭션 격리 수준)에 대해 공부하며 기록한 글입니다. 잘못된 부분이나 오타가 있다면 댓글로 알려주시면 감사하겠습니다. 긴 글 읽어주셔서 감사합니다 😊


Ref

0개의 댓글