트랜잭션 격리 수준이란?
트랜잭션의 격리 수준(Isolation level)이란 DB에 동시에 여러개의 트랜잭션이 처리될 때 특정 트랜잭션에서 변경 또는 조회하고 있는 데이터를 다른 트랜잭션이 어느 정도까지 조회할 수 있는지를 결정하는 것을 말한다. 말 그대로 서로 다른 트랜잭션들이 서로 어느 정도까지 격리가 되어야 하는지를 나타낸 것이다.
격리 수준은 크게 아래의 4가지로 나뉜다.
- Read Uncoommitted
- Read Committed
- Repeatable Read
- Serializable
위에서 아래로 갈 수록 트랜잭션간 격리 수준이 높아지며, 일반적으로 격리 수준이 높아질수록 동시성은 떨어지게 된다.
Read Uncommitted
격리 수준이 Read Uncommitted 일 때는 각 트랜잭션서의 변경 내용이 commit이나 rollback 여부와 상관 없이 다른트랜잭션에서 조회가 가능하다. 달리 말하면 어떤 트랜잭션에서 처리한 작업이 완료(commit)되지 않아도 다른 트랜잭션에서 볼 수 있는 것이고 이러한 현상을 더티 리드(Dirty Read)라고 한다. Read Uncommitted 격리 수준은 Dirty Read 부정합 문제가 허용되는 유일한 격리 수준이며, 정합성에 문제가 많기 때문에 RDBMS 표준에서는 트랜잭션의 격리 수준으로도 인정하지 않는다.
Read Committed
Read Committed 격리 수준에서는 어떤 트랜잭션에서 데이터를 변경했더라도 commit이 완료된 데이터만 다른 트랜잭션에서 조회할 수 있다. 오라클 DBMS에서 기본적으로 사용되는 격리 수준이며 온라인 서비스에서 가장 많이 사용된다. 항상 commit이 완료된 데이터만 조회할 수 있기 때문에 Dirty Read 부정합 문제는 발생하지 않는다. 그러나 한 트랜잭션 내에서 SELECT 쿼리를 실행했을 때 항상 같은 결과를 가져와야 한다는 Repeatable Read 정합성에 어긋나는 현상은 발생할 수 있으며 이를 Non-Repeatable Read 부정합이라고 한다.
Repeatable Read
Repeatable Read 격리 수준에서는 한 트랜잭션 내에서 SELECT 쿼리를 여러번 실행해도 개별 레코드에 대해서는 항상 같은 결과가 조회되기 때문에 Non-Repeatable Read 부정합 문제가 발생하지 않는다. MySQL에서는 트랜잭션마다 고유의 ID를 부여하게 되는데, 특정 트랜잭션 안에서 실행되는 모든 SELECT 쿼리는 자신의 트랜잭션 ID 보다 낮은 트랜잭션 ID에서 변경된 데이터만 조회할 수 있도록 하여 Repeatable Read 격리 수준을 구현했다. 그러나 한 트랜잭션에서 INSERT를 실행하는 도중에 다른 트랜잭션에서 SELECT ... FOR UPDATE 쿼리를 여러번 실행하는 경우 처음에는 조회되지 않던 레코드가 다음 쿼리에서 조회되는 경우가 발생하는데, 이처럼 다른 트랜잭션이 새로운 레코드를 INSERT 함으로써 레코드가 보였다 안보였다 하는 현상을 Phantom Read 부정합이라고 한다.
참고로 MySQL의 InnoDB 엔진에서는 Repeatable Read 격리 수준에서도 Phantom Read 부정합 현상은 발생하지 않는다.
Serializable
한 트랜잭션에서 읽고 쓰는 레코드는 다른 트랜잭션에서는 절대 조회하거나 접근할 수 없는 격리 수준이다. Serializable 격리 수준에서는 읽기 작업도 공유 잠금(읽기 잠금)을 획득해야만 하기 때문에 위에서 말한 Phantom Read 부정합 문제는 발생하지 않는다. 가장 단순하지만 가장 엄격한 격리 수준이며 그만큼 동시성 처리 성능은 다른 트랜잭션 격리 수준보다 현저히 떨어지기 때문에 동시성 처리가 중요한 서비스에서는 거의 사용되지 않는다.
트랜잭션 격리 수준과 부정합 문제
트랜잭션의 격리 수준과 각 격리 수준에서 발생할 수 있는 부정합 문제를 아래 표와 같이 정리할 수 있다.
Dirty Read | Non-Repeatable Read | Phantom Read | |
Read Uncommitted | 발생 | 발생 | 발생 |
Read Committeed | 발생하지 않음 | 발생 | 발생 |
Repeatable Read | 발생하지 않음 | 발생하지 않음 | 발생 (InnoDB 에서는 발생하지 않음) |
Serializable | 발생하지 않음 | 발생하지 않음 | 발생하지 않음 |