(이미지 출처)
트랜잭션 격리 수준이란, 한 트랜잭션이 사용 중인 데이터에 대해 다른 트랜잭션의 접근 허용 정도를 결정하는 것을 말한다. 트랜잭션 격리 수준은 아래와 같이 4단계로 나뉘며, 격리 수준을 조절함으로써, 복수의 트랜잭션이 같은 데이터를 읽고 쓰면서 생기는 Dirty Read
, Non-Repeatable Read
, Phantom Read
와 같은 문제들을 해결할 수 있다.
격리 수준이 높아질수록 동시성(Concurrency)은 낮아지지만, 이에 반비례하여 데이터 일관성(Consistency)은 높아진다. 본 포스팅에서는 각 격리 수준을 살펴보며, 각 단계에서 어떤 문제들을 해결할 수 있는지 알아본다.
Read Uncommitted
는 커밋되지 않은 데이터를 다른 트랜잭션이 조회할 수 있도록 허용하는 격리 수준이다. 따라서 커밋되지 않은 데이터를 조회함으로써 생기는 Dirty Read
문제가 발생할 수 있다.
Dirty Read
란, 한 트랜잭션의 다른 트랜잭션의 커밋되지 않은 변경사항을 조회하는 것을 말한다. Dirty Read
로 인해 어떤 문제가 발생하는지 아래 그림과 함께 자세히 설명한다.
Dirty Read
발생이러한 Dirty Read
문제는 트랜잭션 격리 수준을 Read Committed
로 격상함으로써 해결할 수 있다.
Read Committed
는 트랜잭션의 변경 작업을 외부에서 조회할 수 없게 하는 격리 수준이다. 즉, 커밋이 완료된 데이터만 조회 가능하므로, Dirty Read
문제를 해결할 수 있다. 예시를 통해 Read Committed
가 어떻게 Dirty Read
를 해결하는지 살펴보자.
Dirty Read
문제 해결Read Committed
에서는 커밋이 되지 않은 값을 읽지 않는다. 따라서 틀린 데이터를 사용할 일이 없게 되므로 Dirty Read
문제를 해결할 수 있게 된다.
그러나, Read Committed
격리 수준에서는 Non-Repeatable Read
문제가 발생할 수 있다.
Non-Repeatable Read
란 처음 데이터를 조회 후 다시 데이터를 조회 시, 첫 조회 결과와 다른 조회 결과를 얻는 현상을 말한다. 그림을 통해 Non-Repeatable Read
의 발생 원인을 알아보자.
Non-Repeatable Read
발생 T1이 두 번째로 age를 읽었을 때, 커밋된 값을 읽은 것이므로 틀린 데이터를 읽은 것은 아니다. 다만 한 트랜잭션(그림의 경우 T1에 해당)에서 같은 SQL에 대한 결과가 다른 것 자체가 문제될 수 있다. 예를 들어, 입금과 출금이 지속되는 경우 잔액을 조회하는 SELECT 쿼리가 실행될 때마다 다른 조회 결과를 가져오는 문제를 야기할 수 있다.
이러한 Non-Repeatable Read
문제는 격리 수준을 Repeatable Read
으로 격상함으로써 해결할 수 있다.
Repeatable Read
는 같은 트랜잭션 내에서 조회한 데이터의 값이 항상 동일함을 보장하는 격리 수준이다. MySQL의 경우 해당 격리 수준을 기본 격리 수준으로 사용하며, 최초 트랜잭션 SELECT 실행 시 스냅샷을 만들어두고, 이후 해당 스냅샷에 SELECT를 실행함으로써 다른 트랜잭션에서 데이터가 변경되더라도 조회에 대해 같은 결과를 보장한다.
REPEATABLE READ
"This is the default isolation level for InnoDB. Consistent reads within the same transaction read the snapshot established by the first read." - MySQL 8.0 Reference Manual
트랜잭션 격리 수준이 Read Committed
일 때는 처음 age값을 조회 후 커밋된 age값을 다시 조회하여 Non-Repeatable Read
문제가 발생하였다면, Repeatable Read
수준에서는 다른 트랜잭션의 커밋 여부와 상관없이 데이터를 조회 시 처음 조회한 값과 동일한 결과를 리턴하므로 Non-Repeatable Read
문제가 해결된다.
그러나, Repeatable
격리 수준에서는 Phantom Read
문제가 발생할 수 있다.
Phantom Read
란, 처음 데이터를 조회 후 다시 데이터를 조회할 때 이전에 없던 데이터가 조회되는 문제를 말한다. 그림을 통해 Phantom Read
의 발생 원인을 알아보자.
Phantom Read
발생트랜잭션이 읽기 작업을 반복하는 중간에, 다른 트랜잭션이 데이터를 삽입함으로써 이전에 없던 데이터가 조회된다. Phantom Read
는 격리 수준을 Serializable
로 격상함으로써 해결할 수 있다.
Serializable
은 트랜잭션의 읽기 작업 시에도 공유락을 획득하게 함으로써, 동시에 다른 트랜잭션이 같은 데이터에 접근할 수 없도록 하는 격리 레벨이다. 읽기 트랜잭션이 데이터를 사용하는 도중에 다른 트랜잭션이 INSERT를 수행할 수 없게 되므로 Phantom Read
문제를 해결할 수 있다.
Serializable
의 경우 격리 수준 중 제한이 가장 심하므로 데이터의 동시성 또한 제일 낮다. 따라서 필요성을 검증하고 사용하는 편이 좋다.
요약하자면, 트랜잭션 격리 수준은 여러 트랜잭션이 같은 데이터(또는 테이블)에 접근하여 생기는 문제들(Dirty Read
, Non-Repeatable Read
, Phantom Read
)을 해결하기 위해 트랜잭션간 조회할 수 있는 데이터의 범위를 결정하는 것이라고 볼 수 있다.
격리 수준을 적절하게 조절함으로써, 트랜잭션 동시 실행 문제(Dirty Read
, Non-Repeatable Read
, Phantom Read
)를 해결할 수 있으며, 격리 수준을 높임으로써 얻는 데이터 일관성과 동시성은 반비례하기 때문에 필요성을 따져 선택하는 것이 중요하다.
마침.
잘못된 내용이 있을 경우 언제든지 댓글로 남겨주시면 감사하겠습니다! 🙇♂️
innoDB 엔진이 왜 repetable read를 채택했는지 이해할 수 있어 좋았습니다!
다이어그램 덕분에 쉽게 이해하고 갑니다~