Database의 Transaction Isolation Level 알아보기

Kai·2023년 12월 12일
0

MySQL

목록 보기
11/15

☕ 개요


이번 글에서는 MySQL InnoDB에서 제공하는 트랜잭션의 격리 레벨에 대해서 알아보도록 하겠다.

격리 레벨에 대해서 본격적으로 알아보기 전에 격리 레벨을 포함한 데이터 베이스의 기본 규칙에 대해서 먼저 알아보자. 트랜잭션의 동작 방식에 밀접하게 연관이 있다.

📌 Transaction 기본 규칙 : ACID

Transaction이라고 하면 꼭 만족해야하는 규칙 4가지를 ACID라고 부른다. 이는 4개의 규칙의 앞글자를 따서 이름 붙인 것인데, 각각을 한번 알아보자.

Rule 1) Atomicity : 원자성

Transaction은 어떤 작업의 최소 단위이고, Commit되거나 Rollback될 수 있다.

Rule 2) Consistency : 일관성

Transaction이 실행중이든 롤백이됐든 커밋이됐든 DB는 항상 일관된 상태로 존재해야한다.

Rule 3) Isolation : 격리성

Transaction들은 서로 서로 격리되어야한다. 서로를 방해할 수 없고, 커밋되지 않은 데이터는 서로 확인할 수 없다.

이 규칙이 바로 이번 글에서 자세히 다룰 Transaction의 격리성을 이야기하는 것이다.

Rule 4) Durability : 영속성

한번 커밋된 Trnasaction의 결과는 영속적이어야한다.


📒 Isolation level의 종류


Isolation level은 SET TRANSACTION 문을 통해서 설정할 수 있고, 세션 또는 DB 커넥션 별로 설정할 수 있다. 또, --transaction-isolation 옵션을 통해서 DB의 기본 Isolation level도 설정할 수 있다.

Isolation level에 따라서 Locking하는 정도나 방식이 달라지는데, Isolation level 4가지를 한번 살펴보자.

1. READ UNCOMMITTED

이름에서도 유추할 수 있듯이, 커밋되지 않은 데이터도 트랜잭션 밖에서 읽힐 수 있도록 하는 Isolation level이다. 이 레벨에서는 Dirty read, Non-Repetable read, Phantom read와 같은 이슈가 발생할 수 있고, 애초에 트랜잭션의 기본 규칙에도 부합하지 않아서, 정~말 특별한 상황이 아니면, 사용하지 않을 것을 권장한다.

2. READ COMMITTED

커밋이 완료된 데이터만 트랜잭션 외부에서 읽히도록 하는 레벨이다.
커밋 되지 않은 데이터에 대해서는 Undo log에 있는 이전 데이터를 읽는다.

트랜잭션 A가 실행되는 중에 트랜잭션 B가 커밋되면서 트랜잭션 A에서 사용되는 Row의 값을 바꿔버리면, 트랜잭션 A가 잘못 동작하는 경우가 발생할 수 있다.
즉, Dirty read문제는 당연히 발생하지 않겠지만, Non-Repeatable read, Phantom read 이슈는 여전히 발생할 수 있다.

3. REPEATABLE READ

InnoDB의 기본 격리 레벨이다.
Non-Repeatable read 이슈가 발생하지 않는 격리 레벨이고, 이름 그대로 하나의 트랜잭션에서 여러번의 SELECT문 수행이 가능하다.

REPEATABLE READ로 설정된 트랜잭션에서는 트랜잭션이 실행된 이후에 바뀐 데이터는 조회되지 않는다. 그 이유는 트랜잭션이 수행되기 직전에 수정 또는 삭제된 데이터는 Undo log를 통해서 조회하기 때문이다. 즉, 바로 직전에 발생한 트랜잭션의 새로운 결과와 무관해지게 되는 것이다.
이로 인해서, Non-repeatable read가 발생하지 않고, Phantom read는 발생할 수 있다.

4. SERIALIZABLE

가장 엄격한 격리 레벨이다. 이름에서도 알 수 있듯이 트랜잭션들을 순차적으로 실행시킨다. 이로 인해서 격리 레벨에 따른 이슈는 전혀 발생하지 않지만, 아무래도 성능이 느릴 수 밖에 없다는 단점이 존재한다.


👀 트랜잭션과 관련된 문제들


1) Dirty read 이슈

Dirty read 이슈는 트랜잭션B에서 발생한 데이터의 변화가 아직 커밋되지 않았음에도 불구하고, 트랜잭션A에서 조회되어 의도와 다르게 트랜잭션A가 수행되는 이슈를 이야기한다.

이 이슈를 예방하기 위해서는 트랜잭션BREAD COMMITTED이상의 격리 레벨로 설정해주어야한다.

2) NonRepeatable read 이슈

이 이슈도 Dirty read 이슈와 유사한 원인을 갖고 있다.
트랜잭션A에서 동일한 값 x를 여러번 조회하는데, 트랜잭션B에 의해서 x가 변경되어서, 처음 조회한 값과 그 후 조회한 값이 달라지는 이슈를 이야기한다.

3) Phantom read 이슈

Phantom read 이슈는 NonRepeatable read 이슈와 원인이 동일하다. 다만, 그 증상이 조금 차이가 있는데 Phantom read 이슈는 하나의 트랜잭션에서 동일한 조건으로 값을 조회했는데 이전에 조회되지 않은 값이 조회되거나 이전에 조회됐던 값이 사라지는 것을 이야기한다.

동일한 것을 조회했는데, 그 결과가 다르다는 점은 NonRepeatable read 이슈와 동일하다.


🧐 Isolation level과 숨겨진 문제들


격리 레벨에 따른 문제의 허용 여부

격리 레벨Dirty readNonRepeatable readPhantom read
READ UNCOMMITTEDOOO
READ COMMITTEDXOO
REPEATABLE READXXO
SERIALIZABLEXXX

SQL표준에서 정의한 격리 레벨은 위와 같다.
SQL표준에서는 3가지의 문제를 기준으로 격리 레벨을 설명하고 있는데, 사실은 이 외에도 많은 트랜잭션과 관련된 문제들이 존재한다.
예를 들자면, 아래와 같다.

  • Dirty write : 두 개 이상의 트랜잭션이 동시에 데이터를 쓰고, 하나의 트랜잭션이 다른 트랜잭션의 수정 중인 데이터를 덮어쓰는 경우
  • Lost update : 두 개 이상의 트랜잭션이 동시에 같은 데이터를 읽고 수정할 때, 처음에 반영된 결과가 나중에 반영된 결과에 의해서 덮어씌워지는 경우.
  • Read skew : 하나의 트랜잭션이 읽은 값이 다른 트랜잭션이 읽은 값에 영향을 미치는 경우
  • Write skew : 두 개 이상의 트랜잭션이 동시에 데이터를 쓰고, 하나의 트랜잭션이 다른 트랜잭션의 수정 중인 데이터를 읽는 경우.

결론적으로 SQL표준에 정의된 문제들 말고도 트랜잭션과 관련된 다양한 문제들이 있을 수 있으니, 내가 사용하는 RDBMS의 격리 레벨을 잘 확인하고 적절히 사용해야할 필요가 있다. 또, 제품별로 격리 레벨의 성격과 종류가 달라서 내가 사용하고 있는 RDBMS에서는 어떻게 격리 레벨을 설정하고 있는지도 잘 확인해야한다.

참고로, InnoDB는 SQL 표준과 동일하게 격리 레벨을 정의하고 있다.

Snapshot Isolation

Snapshot isolationInnoDB에서는 제공하고 있지 않지만, MS SQL과 같은 RDBMS에서 추가적을 제공하는 격리 레벨이다.
이 격리 레벨은 동시성 제어를 어떻게 할지를 고민하면서 만들어졌고, 각 트랜잭션안에서 호출되는 값들을 스냅샷으로 만들어서 일관된 데이터를 읽고 수정하도록 만드는 트랜잭션이다.

🤔 이 격리 레벨을 적용해도 동일한 값을 서로 다른 트랜잭션에서 서로 다른 값으로 업데이트하려는 경우는 어떻게 커버할 수 있을까?

Snapshot Isolation에서는 서로 다른 트랜잭션에서 동일한 값에 대한 Write가 발생한 경우 먼저 Write한 것을 인정해주고 나중에 Write하려고한 트랜잭션은 Abort시킨다.

마지막으로 Snapshot isolation에 대해서 요약하자면, 아래와 같다.

  1. 트랜잭션안에서는 해당 트랜잭션이 시작되기 전에 커밋된 데이터들만 보인다.
  2. 서로 다른 트랜잭션에서 동일한 값에 Write를 하려고 한 경우, 먼저 Commit된 트랜잭션의 결과가 반영된다.

🙏 참고


0개의 댓글