이번 글에서는 MySQL InnoDB에서 제공하는 트랜잭션의 격리 레벨에 대해서 알아보도록 하겠다.
격리 레벨에 대해서 본격적으로 알아보기 전에 격리 레벨을 포함한 데이터 베이스의 기본 규칙에 대해서 먼저 알아보자. 트랜잭션의 동작 방식에 밀접하게 연관이 있다.
Transaction이라고 하면 꼭 만족해야하는 규칙 4가지를 ACID라고 부른다. 이는 4개의 규칙의 앞글자를 따서 이름 붙인 것인데, 각각을 한번 알아보자.
Transaction은 어떤 작업의 최소 단위이고, Commit되거나 Rollback될 수 있다.
Transaction이 실행중이든 롤백이됐든 커밋이됐든 DB는 항상 일관된 상태로 존재해야한다.
Transaction들은 서로 서로 격리되어야한다. 서로를 방해할 수 없고, 커밋되지 않은 데이터는 서로 확인할 수 없다.
이 규칙이 바로 이번 글에서 자세히 다룰 Transaction의 격리성을 이야기하는 것이다.
한번 커밋된 Trnasaction의 결과는 영속적이어야한다.
Isolation level은 SET TRANSACTION 문을 통해서 설정할 수 있고, 세션 또는 DB 커넥션 별로 설정할 수 있다. 또, --transaction-isolation
옵션을 통해서 DB의 기본 Isolation level도 설정할 수 있다.
Isolation level에 따라서 Locking하는 정도나 방식이 달라지는데, Isolation level 4가지를 한번 살펴보자.
이름에서도 유추할 수 있듯이, 커밋되지 않은 데이터도 트랜잭션 밖에서 읽힐 수 있도록 하는 Isolation level이다. 이 레벨에서는 Dirty read, Non-Repetable read, Phantom read와 같은 이슈가 발생할 수 있고, 애초에 트랜잭션의 기본 규칙에도 부합하지 않아서, 정~말 특별한 상황이 아니면, 사용하지 않을 것을 권장한다.
커밋이 완료된 데이터만 트랜잭션 외부에서 읽히도록 하는 레벨이다.
커밋 되지 않은 데이터에 대해서는 Undo log에 있는 이전 데이터를 읽는다.
트랜잭션 A가 실행되는 중에 트랜잭션 B가 커밋되면서 트랜잭션 A에서 사용되는 Row의 값을 바꿔버리면, 트랜잭션 A가 잘못 동작하는 경우가 발생할 수 있다.
즉, Dirty read문제는 당연히 발생하지 않겠지만, Non-Repeatable read, Phantom read 이슈는 여전히 발생할 수 있다.
InnoDB의 기본 격리 레벨이다.
Non-Repeatable read 이슈가 발생하지 않는 격리 레벨이고, 이름 그대로 하나의 트랜잭션에서 여러번의 SELECT문 수행이 가능하다.
REPEATABLE READ로 설정된 트랜잭션에서는 트랜잭션이 실행된 이후에 바뀐 데이터는 조회되지 않는다. 그 이유는 트랜잭션이 수행되기 직전에 수정 또는 삭제된 데이터는 Undo log를 통해서 조회하기 때문이다. 즉, 바로 직전에 발생한 트랜잭션의 새로운 결과와 무관해지게 되는 것이다.
이로 인해서, Non-repeatable read가 발생하지 않고, Phantom read는 발생할 수 있다.
가장 엄격한 격리 레벨이다. 이름에서도 알 수 있듯이 트랜잭션들을 순차적으로 실행시킨다. 이로 인해서 격리 레벨에 따른 이슈는 전혀 발생하지 않지만, 아무래도 성능이 느릴 수 밖에 없다는 단점이 존재한다.
Dirty read 이슈는 트랜잭션B
에서 발생한 데이터의 변화가 아직 커밋되지 않았음에도 불구하고, 트랜잭션A
에서 조회되어 의도와 다르게 트랜잭션A
가 수행되는 이슈를 이야기한다.
이 이슈를 예방하기 위해서는 트랜잭션B
를 READ COMMITTED
이상의 격리 레벨로 설정해주어야한다.
이 이슈도 Dirty read 이슈와 유사한 원인을 갖고 있다.
트랜잭션A
에서 동일한 값 x
를 여러번 조회하는데, 트랜잭션B
에 의해서 x
가 변경되어서, 처음 조회한 값과 그 후 조회한 값이 달라지는 이슈를 이야기한다.
Phantom read 이슈는 NonRepeatable read 이슈와 원인이 동일하다. 다만, 그 증상이 조금 차이가 있는데 Phantom read 이슈는 하나의 트랜잭션에서 동일한 조건으로 값을 조회했는데 이전에 조회되지 않은 값이 조회되거나 이전에 조회됐던 값이 사라지는 것을 이야기한다.
동일한 것을 조회했는데, 그 결과가 다르다는 점은 NonRepeatable read 이슈와 동일하다.
격리 레벨 | Dirty read | NonRepeatable read | Phantom read |
---|---|---|---|
READ UNCOMMITTED | O | O | O |
READ COMMITTED | X | O | O |
REPEATABLE READ | X | X | O |
SERIALIZABLE | X | X | X |
SQL표준에서 정의한 격리 레벨은 위와 같다.
SQL표준에서는 3가지의 문제를 기준으로 격리 레벨을 설명하고 있는데, 사실은 이 외에도 많은 트랜잭션과 관련된 문제들이 존재한다.
예를 들자면, 아래와 같다.
결론적으로 SQL표준에 정의된 문제들 말고도 트랜잭션과 관련된 다양한 문제들이 있을 수 있으니, 내가 사용하는 RDBMS의 격리 레벨을 잘 확인하고 적절히 사용해야할 필요가 있다. 또, 제품별로 격리 레벨의 성격과 종류가 달라서 내가 사용하고 있는 RDBMS에서는 어떻게 격리 레벨을 설정하고 있는지도 잘 확인해야한다.
참고로, InnoDB는 SQL 표준과 동일하게 격리 레벨을 정의하고 있다.
Snapshot isolation
은 InnoDB
에서는 제공하고 있지 않지만, MS SQL
과 같은 RDBMS에서 추가적을 제공하는 격리 레벨이다.
이 격리 레벨은 동시성 제어를 어떻게 할지를 고민하면서 만들어졌고, 각 트랜잭션안에서 호출되는 값들을 스냅샷으로 만들어서 일관된 데이터를 읽고 수정하도록 만드는 트랜잭션이다.
Snapshot Isolation
에서는 서로 다른 트랜잭션에서 동일한 값에 대한 Write가 발생한 경우 먼저 Write한 것을 인정해주고 나중에 Write하려고한 트랜잭션은 Abort시킨다.
마지막으로 Snapshot isolation
에 대해서 요약하자면, 아래와 같다.