트랜잭션의 격리 수준이란?
동시에 여러 트랜잭션이 실행될 때 한 트랜잭션이 다른 트랜잭션의 연산에 영향을 받지 않도록 하는 정도이다.
낮은 격리 수준은 동시 처리 능력을 높이지만, 데이터의 일관성 문제를 발생시킬 수 있고, 높은 격리 수준은 데이터의 일관성을 보장하지만, 동시 처리 능력이 떨어질 수 있다.
💡 개발자 입장에서 트랜잭션 격리 수준을 왜 알아야 할까?
여러 트랙잭션이 서로의 연산에 영향을 받아 동시성 관련 버그가 발생하면 디버깅이 어렵다.
격리 수준을 제대로 이해하면 Dirty Read, Non-repeatable Read, Phantom Read 같은 문제를 예상할 수 있고, 발생 시 더 빠른 디버깅이 가능하다.
또, 선택한 DBMS마다 기본 격리 수준이 달라 이를 모르고 개발을 진행한다면 각 환경에 따라 다른 동작을 한다고 생각할 수 있다.
트랜잭션 격리 수준
트랜잭션 격리 수준은 격리 수준이 낮은 순서대로 READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ, SERIALIZABLE이 있다. 지금부터 알아볼 예제들은 모두 자동 커밋(AUTO COMMIT)이 false인 상태에서만 발생한다.
READ UNCOMMITTED
READ UNCOMMITTED는 커밋이 되지 않은 트랜잭션의 데이터 변경 내용을 다른 트랜잭션이 조회하는 것을 허용한다. 가장 낮은 수준의 격리 수준이다.
- 트랜잭션이 커밋되지 않은 변경사항(uncommitted changes)도 다른 트랜잭션에서 즉시 볼 수 있다.
- 즉, 하나의 트랜잭션이 데이터를 변경하는 도중에 다른 트랜잭션이 해당 데이터를 읽을 수 있다.
- 변경 내용이 최종적으로 커밋되지 않고 롤백되더라도, 그 사이에 다른 트랜잭션은 변경 중인 데이터를 읽을 수 있다.
READ UNCOMMITTED에선 Dirty Read, Non-repeatable Read, Phantom Read 문제가 발생할 수 있다. 이 문제들은 같은 트랜잭션 내에서 같은 쿼리를 여러 번 실행했을 때 다른 결과가 나올 수 있다. ➡️ 데이터 일관성 깨짐
Dirty Read
한 트랜잭션이 아직 커밋되지 않은 다른 트랜잭션의 데이터 변경사항을 읽는 경우다.

- Alice와 Bob이 각각 DB에 트랜잭션을 시작한다.
- Alice는 블로그 포스트를 수정하는 트랜잭션을 시작
- Bob은 블로그 포스트를 읽는 트랜잭션을 시작
- Alice가 특정 `post`레코드의 제목을 수정한다.
- `UPDATE post SET title = 'ACID' WHERE id = 1` 수행
- 아직 DB에 수행한 SQL의 결과가 커밋되지 않았다.
- Bob이 아직 커밋되지 않은 `post`레코드를 읽는다.
- `SELECT * FROM post WHERE id = 1` 수행
- READ UNCOMMITTED 격리 수준에서 Bob은 `ACID`라는 제목을 볼 수 있다.
- Alice가 트랜잭션을 커밋하면 문제가 없고, Bob이 읽은 데이터는 DB에 반영된다. 하지만 Alice가 롤백하거나 트랜잭션이 장애로 인해 비정상적으로 종료된 경우, Bob은 DB에 존재한 적이 없 레코드를 읽게 된다. ➡️ 데이터 일관성 깨짐
Non-repeatable Read
하나의 트랜잭션 내에서 같은 쿼리를 여러 번 실행했을 때 다른 결과를 얻는 경우다.

- Alice와 Bob이 각각 DB에 트랜잭션을 시작한다.
- Bob이 `post`레코드를 읽는다.
- ` SELECT * FROM post WHERE id = 1` 수행
- `title` = "Transactions"
- Alice가 같은 레코드를 수정한다.
- `UPDATE post SET title = 'ACID WHERE id = 1` 수행
- Alice가 변경사항을 커밋한다. 이제 DB에 있는 실제 값은 "ACID"이다.
- Bob이 동일한 트랜잭션 내에서 쿼리를 수행한다.
- ` SELECT * FROM post WHERE id = 1` 수행
- `title` = "ACID"
Phantom Read
한 트랜잭션 내에서 같은 쿼리를 여러 번 실행했을 때 이전에 없던 행이 나타나거나 있던 행이 사리지는 경우다.

- Bob이 먼저 트랜잭션을 시작한다.
- ` SELECT * FROM post_comment WHERE post_id = 1` 수행
- 3개의 레코드 조회
- Alice가 트랜잭션을 시작한다.
- Alice는 새로운 레코드를 삽입한다.
- `INSERT INTO post_comment (id, post_id) VALUES (4, 1)` 수행
- post_id = 1인 레코드가 하나 더 추가
- Alice는 자신의 변경사항을 커밋한다.
- DB에는 post_id = 1인 레코드가 4개 존재
- Bob이 동일한 트랜잭션 내에서 쿼리를 수행한다.
- ` SELECT * FROM post_comment WHERE post_id = 1` 수행
- 4개의 레코드 조회
Non-repeatable Read, Phantom Read에서 하나의 트랜잭션 내에서 동일한 쿼리의 결과가 다르다는게 중요하다!
💡Non-repeatable Read vs Phantom Read
두 문제 모두 하나의 트랜잭션 내에서 동일한 쿼리의 결과가 다르다. 두 문제의 정확한 차이는 뭘까?
Non-repeatable Read는 이미 존재하는 레코드 데이터 값이 변경된 것이다.
동일한 트랜잭션에서 같은 레코드를 여러 번 읽었지만, 각 레코드의 내용이 다른 것이다.
Phantom Read는 이전 결과에 비해 새로운 레코드가 추가되거나 기존 레코드가 사라진 것을 의미한다.
동일한 트랜잭션에서 같은 레코드를 여러 번 읽었지만, 전체 레코드의 개수가 다른 것이다.
따라서, Non-repeatable Read는 UPDATE 연산에서, Phantom Read는 INSERT, DELETE 연산에서 주로 발생한다.
READ COMMITTED
READ COMMITTED는 커밋된 트랜잭션의 변경사항만 다른 트랜잭션에서 조회할 수 있는 격리 수준이다. 특정 트랜잭션이 수행되는 동안 다른 트랜잭션은 특정 트랜잭션이 접근한 데이터에 쓰기 작업을 할 수 없다. Dirty Read는 해결했지만, Non-repeatable Read와 Phantom Read는 발생한다.
- 트랜잭션은 다른 트랜잭션에 의해 커밋된 변경사항(committed changes)만 볼 수 있다.
- 즉, 아직 커밋되지 않은 변경사항은 볼 수 없다.
- 한 트랜잭션이 데이터를 변경하는 동안 해당 데이터에 대한 쓰기 잠금(lock)을 획득한다. 이 잠금은 트랜잭션이 커밋되거나 롤백될 때까지 유지된다.
READ COMMITTED는 특정 트랜잭션이 수행되는 동안 다른 트랜잭션은 특정 트랜잭션이 접근한 데이터에 접근할 수 없는 것이 아니다. 특정 트랜잭션은 쓰기 잠금만 획득했기 때문에, 다른 트랜잭션이 같은 데이터에 대해 읽기 작업을 수행할 수 있다. 이 경우 스냅샷을 참고한 데이터를 읽는다.
- 읽기 작업
- 읽기 작업은 현재 커밋된 데이터 스냅샷을 기반으로 한다.
- 각 쿼리마다 새로운 스냅샷을 사용한다. (동일한 트랜잭션 내에서도 적용)
- 쓰기 작업
- 데이터 변경 시 해당 데이터에 쓰기 잠금을 설정한다.
- 다른 트랜잭션은 이 잠금이 해제될 때까지 해당 데이터를 변경할 수 없다.
REPEATABLE READ
REPEATABLE READ는 한 트랜잭션에서 특정 레코드를 조회할 때 항상 같은 데이터를 응답하는 것을 보장한다. 하지만, SERIALIZABLE과 다르게 행이 추가되는 것을 막지는 않는다. Non-Repeatable Read 문제가 발생하지 않지만, Phantom Read 문제가 발생할 수 있다.
- 트랜잭션이 시작될 때 DB의 스냅샷을 만들고, 트랜잭션 내의 모든 읽기 작업은 이 스냅샷을 기준으로 수행한다.
- 읽기 작업에서 읽은 모든 행에 공유 잠금을 설정하고 이 잠금은 트랜잭션이 완료될 때까지 유지된다.
- 데이터 변경 시 해당 데이터에 쓰기 잠금을 설정하며, 이 잠금은 트랜잭션이 완료될 때까지 유지된다.
REPEATABLE READ에서 Phantom Read가 발생할 수 있는 이유는 각 데이터의 레코드에 대한 쓰기 잠금을 설정하기 때문에, 해당 레코드의 변경과 삭제는 막을 수 있지만 테이블에 새 레코드가 삽입되는 것은 막을 수 없기 때문이다.
💡READ COMMITTED vs REPEATABLE READ
두 격리 수준 모드 스냅샷을 통해 Dirty Read를 방지한다. 두 차이는 뭘까?
READ_COMMITTED은 각 쿼리마다 새로운 스냅샷을 생성한다. 트랜잭션 A가 특정 데이터를 두 번 조회할 때, 트랜잭션 B가 그 사이에 A가 조회했던 데이터를 변경하고 커밋하면 A의 두 번째 조회에선 변경된 값을 조회하게 된다. 따라서, Non-Repeatable Read 현상이 발생할 수 있다.
REPEATABLE READ는 트랜잭션이 시작할 때 한 번의 스냅샷을 생성하고 트랜잭션 내의 모든 쿼리가 하나의 스냅샷을 사용한다. 트랜잭션 A가 특정 데이터를 두 번 조회할 때, 트랜잭션 B가 그 사이에 데이터를 변경하고 커밋하더라도 트랜잭션 A는 트랜잭션 시작 전 생성한 스냅샷을 참조하기 때문에 두 번 조회 모두 같은 값을 조회한다. 따라서, Non-Repeatable Read 현상을 막을 수 있다.
SERIALIZABLE
SERIALIZABLE은 특정 트랜잭션이 접근하는 행과 관련된 범위에 대해 다른 트랜잭션이 변경할 수 없도록 잠근다. SERIALIZABLE은 트랜잭션이 접근하는 행에 대한 범위 잠금을 설정한다. 범위 잠금을 사용해 새로운 행이 삽입되는 것을 막는다.
SERIALIZABLE = REPEATABLE READ + 범위 잠금
범위 잠금이란?
개별 행에 대한 잠금만 설정하는 것이 아니라, 쿼리의 조건에 해당하는 데이터 범위 전체에 잠금을 설정하는 것이다.
SELECT * FROM products WHERE price BETWEEN 100 AND 200;
- `price`가 100~200 사이인 모든 행에 공유 잠금을 설정
- `price`가 100~200 사이인 새로운 행이 삽입되는 것을 방지하는 범위 잠금도 설정
- 100~200에 해당하는 인덱스 공간에는 새로운 행 삽입 X
공유 잠금은 기존 레코드에만 적용되고, SERIALIZABLE은 아직 존재하지 않은 레코드에도 영향을 미쳐야 하기 때문에 범위 잠금도 함께 사용한다. 이를 통해 Phantom Read 현상을 방지할 수 있다.
Summary
| 격리 수준 | Dirty Read | Non-repeatable Read | Phantom Read | 특징 |
| READ UNCOMMITTED | O | O | O | 커밋되지 않은 데이터도 다른 트랜잭션에서 읽을 수 있음 |
| READ COMMITTED | X | O | O | 커밋된 데이터만 다른 트랜잭션에서 읽을 수 있음 |
| REPEATABLE READ | X | X | O | 읽은 모든 행에 공유 잠금 |
| SERIALIZABLE | X | X | X | 읽기 작업에도 공유 잠금 설정 |
References
https://mangkyu.tistory.com/299
[MySQL] 트랜잭션의 격리 수준(Isolation Level)에 대해 쉽고 완벽하게 이해하기
이번에는 트랜잭션 격리 수준(Isolation Level)에 대해 알아보도록 하겠습니다. 아래의 내용은 RealMySQL과 MySQL 공식 문서 등을 참고하여 작성하였으며, 모든 내용은 InnoDB를 기준으로 설명합니다. 해
mangkyu.tistory.com
https://vladmihalcea.com/dirty-read/
A beginner's guide to Dirty Read anomaly - Vlad Mihalcea
Dirty Read is a data integrity anomaly that can occur when one transaction can read the uncommitted records of a second concurrent transaction.
vladmihalcea.com
https://vladmihalcea.com/non-repeatable-read/
A beginner’s guide to Non-Repeatable Read anomaly - Vlad Mihalcea
Non-Repeatable Read is a data integrity anomaly that can occur when one transaction observes two successive versions of the same database record.
vladmihalcea.com
https://vladmihalcea.com/phantom-read/
A beginner’s guide to Phantom Read anomaly - Vlad Mihalcea
Phantom Read is a data integrity anomaly that can occur when one transaction observes two successive versions of the same multi-record query.
vladmihalcea.com
'DB' 카테고리의 다른 글
| [DB] 데이터베이스 인덱스에 관하여 (0) | 2025.03.10 |
|---|---|
| [DB] 트랜잭션(Transaction)이란? (0) | 2024.04.17 |