목차
트랜잭션 격리수준이란 데이터베이스에서 동시에 실행되는 트랜잭션 간의 상호작용을 제어하고, 데이터의 일관성을 유지하기 위한 규칙과 메커니즘을 정의하는 것.
하나의 트랜잭션이 다른 트랜잭션의 데이터에 접근하거나 변경할 수 있는 정도를 결정하는 것.
트랜잭션 격리 수준은 4가지로 나뉜다.
- READ UNCOMMITTED
- READ COMMITTED
- REPEATABLE READ
- SERIALIZABLE
Dirty Read | Non-Repeatable Read | Phantom Read | |
READ UNCOMMITTED | O | O | O |
READ COMMITTED | X | O | O |
REPEATABLE READ | X | X | O (MySQL의 innodb 엔진은 거의 없음) |
SERIALIZABLE | X | X | X |
격리성으로 인해 나타날 수 있는 문제점들
Dirty Read
다른 트랜잭션에 의해 수정은 됐지만 커밋되지 않은 데이터를 읽는 현상
1. 트랜잭션A가 데이터를 INSERT or UPDATE함. (커밋은 하지 않음)
2. 트랜잭션B가 커밋되지 않은 데이터를 읽음
3. 트랜잭션A가 롤백함
4. 트랜잭션B의 정합성이 깨짐
Non-Repeatable Read
한 트랜잭션에서 동일한 조건으로 2번 이상 조회했을 때 데이터가 변경되거나 삭제되는 현상
데이터가 변했냐 아니냐의 기준으로 판단한다.
1. 트랜잭션B가 SELECT. 데이터1이 조회
2. 트랜잭션A가 데이터1의 값을 2로 변경
3. 트랜잭션B가 다시 조회했을 때 값이 2로 변경되어 나옴.
Phantom Read
한 트랜잭션에서 동일한 조건으로 2번 이상 조회했을 때 첫번째 쿼리에서 없던 유령 (Phantom) 레코드가 나타나거나 없어지는 현상.
Non-Repeatable Read의 경우 1개의 Row 데이터의 값이 변경되는 것과 관련이 있으나 Phantom Read는 조회된 전체 데이터를 기준으로 했을 때의 변경이다.
1. 트랜잭션B가 데이터를 읽음
2. 트랜잭션A가 새로운 데이터 INSERT
3. 트랜잭션B가 다시 읽었을 때 새로운 데이터가 나타남
지정할 수 있는 격리성 수준
Read Uncommitted
커밋되지 않은 데이터도 읽는 것. 가장 낮은 수준의 격리성.
Dirty Read, Non-Repeatable Read, Phantom Read 모두 발생할 수 있다.
1. 트랜잭션A 시작 후 INSERT (커밋은 안함)
2. 트랜잭션B가 커밋안된 데이터를 읽음
3. 트랜잭션A가 롤백
4. 결론적으로 트랜잭션B가 이상한(?) 데이터를 가져가게 됨
Read Committed
커밋된 데이터만 읽는 것. 대부분의 환경에서 사용
Non-Repeatable Read, Phantom Read가 발생할 수 있다.
일반적인 상황
1. 트랜잭션A가 트랜잭션 시작 후 INSERT (커밋은 안함)
2. 트랜잭션B가 조회했으나 커밋된 데이터가 없어서 조회결과 없음
3. 트랜잭션A가 커밋함
4. 트랜잭션B가 조회했을 때 정상 조회됨.
3.1 트랜잭션A가 롤백함
4.1 트랜잭션B에서 여전히 읽지 못함
Non-Repetable Read 상황
1. 트랜잭션A가 트랜잭션을 시작함.
2. 트랜잭션A가 INSERT 후 커밋함
3. 트랜잭션B가 조회했을 때 1건 조회됨
4. 트랜잭션A가 UPDATE 후 커밋함
5. 트랜잭션B가 조회했을 때 변경된 데이터가 조회됨.
=> 동일 트랜잭션 내에서 2번 읽었을 때 다른 결과가 나옴.
Repetable Read
반복읽기를 했을 때 동일한 데이터가 나옴을 보장함.
일반적인 RDBMS는 변경 전의 내용을 Undo 공간에 백업하고 이를 바탕으로 반복된 조회를 했을 때 동일한 데이터가 나옴을 보장함.
이 경우 동일한 레코드에 여러버전의 데이터를 관리한다고 하여 이를 MVCC 라고 부른다고 한다. MVCC를 통해 롤백도 가능하게 하고 격리성에 따른 데이터 핸들링도 가능케 한다.
undo 로그에는 트랜잭션ID를 함께 기록한다.
하지만 Phantom Read는 여전히 발생할 수 있음. (innoDB엔진을 사용하는 MySQL은 발생하지 않는다고 함)
UPDATE 상황
1. 트랜잭션B (T1)가 >= 1 조건으로 데이터를 조회함. 1건 조회됨.
2. 트랜잭션A (T2)가 >=1 조건인 데이터 1건을 UPDATE하고 커밋함
3. 트랜잭션B (T1)가 >= 1 동일 조건으로 데이터를 조회한다. 이 때 T2에서 커밋한 내용은 T1보다 나중 트랜잭션이기 때문에 undo 로그를 보고 기존 데이터를 조회해 옴.
INSERT 상황
1. 트랜잭션B (T1)가 >= 1 조건으로 데이터를 조회함. 1건 조회됨.
2. 트랜잭션A (T2)가 >=1 조건에 부합하는 새로운 데이터를 INSERT 하고 커밋함
3. 트랜잭션B (T1)가 >= 1 동일 조건으로 데이터를 조회한다. INSERT라서 Phantom Read가 발생할 것 같지만 실제로는 발생하지 않는다. 그 이유는 MVCC에 의해 이 때 T2에서 커밋한 내용은 T1보다 나중 트랜잭션이기 때문에 undo 로그를 보고 기존 데이터를 조회해 오기 때문이다.
MySQL외 DB에서의 Phantom Read 발생 상황
SELECT FOR UPDATE -> INSERT -> SELECT
1. 트랜잭션B (T1)가 >= 1 조건으로 데이터를 조회함 (SELECT FOR UPDATE로 락을 검). 1건 조회됨.
2. 트랜잭션A (T2)가 >=1 조건에 부합하는 새로운 데이터를 INSERT 하고 커밋함
3. 트랜잭션B (T1)가 >= 1 동일 조건으로 데이터를 조회한다. MVCC에 의해 1건만 조회됨
=> Phantom Read 없음
SELECT FOR UPDATE -> INSERT -> SELECT FOR UPDATE
1. 트랜잭션B (T1)가 >= 1 조건으로 데이터를 조회함 (SELECT FOR UPDATE로 락을 검). 1건 조회됨.
2. 트랜잭션A (T2)가 >=1 조건에 부합하는 새로운 데이터를 INSERT 하고 커밋함
3. 트랜잭션B (T1)가 >= 1 동일 조건으로 데이터를 조회 (SELECT FOR UPDATE로 락을 검). 락을 걸면 undo 로그가 아닌 실제 테이블에서 조회하기 때문에 추가된 데이터가 보임
=> Phantom Read 발생
SELECT -> INSERT -> SELECT
1. 트랜잭션B (T1)가 >= 1 조건으로 데이터를 조회함. 1건 조회됨.
2. 트랜잭션A (T2)가 >=1 조건에 부합하는 새로운 데이터를 INSERT 하고 커밋함
3. 트랜잭션B (T1)가 >= 1 동일 조건으로 데이터를 조회한다. MVCC에 의해 1건만 조회됨
=> Phantom Read 없음
SELECT -> INSERT -> SELECT FOR UPDATE
1. 트랜잭션B (T1)가 >= 1 조건으로 데이터를 조회함. 1건 조회됨.
2. 트랜잭션A (T2)가 >=1 조건에 부합하는 새로운 데이터를 INSERT 하고 커밋함
3. 트랜잭션B (T1)가 >= 1 동일 조건으로 데이터를 조회 (SELECT FOR UPDATE로 락을 검. undo 로그에서 조회할 수 없으므로 모두 조회됨
=> Phantom Read 발생
MySql에서의 Phantom Read
MySql은 갭락이라는 개념이 있어서 Phantom Read가 거의 발생하지 않는다고 한다.
SELECT FOR UPDATE -> INSERT -> SELECT
1. 트랜잭션B (T1)가 >= 1 조건으로 데이터를 조회함 (SELECT FOR UPDATE로 락을 검). 1건 조회됨. 갭락으로 id>=1 조건은 락이 걸리게 됨
2. 트랜잭션A (T2)가 >=1 조건에 부합하는 새로운 데이터를 INSERT 하고 커밋하려고 하나 갭락에 의해 블록킹됨
3. 트랜잭션B (T1)가 >= 1 동일 조건으로 데이터를 조회하는데 동일한 데이터 조회
4. 트랜잭션B (T1)가 커밋 후 트랜잭션A가 데이터 INSERT
=> Phantom Read 없음
SELECT FOR UPDATE -> INSERT -> SELECT FOR UPDATE
1. 트랜잭션B (T1)가 >= 1 조건으로 데이터를 조회함 (SELECT FOR UPDATE로 락을 검). 1건 조회됨. 갭락으로 id>=1 조건은 락이 걸리게 됨
2. 트랜잭션A (T2)가 >=1 조건에 부합하는 새로운 데이터를 INSERT 하고 커밋하려고 하나 갭락에 의해 블록킹됨
3. 트랜잭션B (T1)가 >= 1 동일 조건으로 데이터를 조회하는데 동일한 데이터 조회
4. 트랜잭션B (T1)가 커밋 후 트랜잭션A가 데이터 INSERT
=> Phantom Read 없음
SELECT -> INSERT -> SELECT
1. 트랜잭션B (T1)가 >= 1 조건으로 데이터를 조회함. 1건 조회됨.
2. 트랜잭션A (T2)가 >=1 조건에 부합하는 새로운 데이터를 INSERT 하고 커밋함
3. 트랜잭션B (T1)가 >= 1 동일 조건으로 데이터를 조회한다. MVCC에 의해 1건만 조회됨
=> Phantom Read 없음
SELECT -> INSERT -> SELECT FOR UPDATE
1. 트랜잭션B (T1)가 >= 1 조건으로 데이터를 조회함. 1건 조회됨. 갭락이 걸리지 않음.
2. 트랜잭션A (T2)가 >=1 조건에 부합하는 새로운 데이터를 INSERT 하고 커밋함
3. 트랜잭션B (T1)가 >= 1 동일 조건으로 데이터를 조회 (SELECT FOR UPDATE로 락을 검. undo 로그에서 조회할 수 없고 갭락도 없으므로 Phantom Read 발생)
=> Phantom Read 발생
Serializable
참조
격리 수준별 요약
https://mangkyu.tistory.com/299
Phantom Read 상세 상황
https://parkmuhyeun.github.io/woowacourse/2023-11-28-Repeatable-Read/