Isolation level
필요성
격리 수준은 트랜잭션의 ACID 특성을 보장하기 위해서 사용한다.
Locking 을 통해 이를 해결할 수 있지만, 무조건적인 Locking 은 성능저하를 가져온다.
반대로 느슨한 Locking 은 데이터 무결성에 큰 문제를 가져온다.
효율적인 Locking 의 사용을 위해 적절한 격리수준은 반드시 필요하다.
표준 이상현상
1. Dirty read
커밋되지 않은 변화를 읽었을때 나오는 현상
2. Non-repeatable read(Fuzzy read)
같은 데이터의 값이 달라지는 현상
다른 트랜잭션이 커밋한 데이터를 읽을 수 있는 것을 의미한다.
즉, 한 트랜잭션에서 같은 쿼리로 2번이상 조회했을 때 그 결과가 상이한 상황을 말한다.
보통 데이터의 수정/삭제가 발생했을 경우 발생한다.
3. Phantom read
없던 데이터가 생기는 현상
다른 트랜잭션이 커밋한 데이터가 있더라도 자신의 트랜잭션에서 읽었던 내용만 사용하는 것을 의미한다.
즉, 한 트랜잭션에서 같은 쿼리를 2번이상 조회했을 때 없던 결과가 조회되는 상황을 말한다.
보통 데이터의 삽입이 발생했을 경우 발생한다.
이런 이상한 현상들이 모두 발생하지 않게 만들 수 있지만 그러면 제약사항이 많아져서 동시 처리 가능한 트랜잭션 수가 줄어들어 결국 DB 의 전체 처리량(Throughput)이 하락하게 된다.
일부 이상한 현상을 허용하는 몇가지 level 을 만들어서 사용자가 필요에 따라 적절하게 선택할 수 있도록 하는 이 방법을 적용한 것이 Isolation level 이다.
이 Isolation level 은 위의 3가지 현상중에 몇가지를 허용할 것인가에 따라 달라진다.
Isolation level | 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 |
비표준 이상현상
1. Dirty write
커밋이 안된 데이터를 wirte 한다.
rollback 시 정상적인 recovery 는 매우 중요하기 때문에 모든 isolation level 에서 dirty write 를 허용하면 안된다.
2. Lost update
업데이트를 했는데 반영이 되지 않는다.
업데이트를 덮어 썼을 때 발생하는 현상
3. Dirty read 확장
커밋이 되지 않은 변화를 읽는다.
abort 가 발생하지 않아도 dirty read 가 될 수 있다.
4. Read skew
inconsistent 한 데이터 읽기
5. Write skew
inconsistent 한 데이터 쓰기
6. Snapshot isolation
type of MVCC
격리 수준
1. Read uncommitted
어떤 트랜잭션의 변경 내용이 커밋이나 rollback 과 상관없이 다른 트랜잭션에서 보여진다.
정합성의 문제가 많은 격리수준이기 때문에 RDBMS 표준에서는 격리수준으로 인정하지 않는다.
select 문이 실행되는 동안 해당 data 에 Shared Lock 이 걸리지 않는다.
1) A 트랜잭션에서 10번 사원의 나이를 27살에서 28살로 바꿈
2) 아직 커밋하지 않음
3) B 트랜잭션에서 10번 사원의 나이를 조회함
4) 28살이 조회됨 -> Dirty Read
5) A 트랜잭션에서 문제가 발생해 rollback 함
6) B 트랜잭션은 10번 사원이 여전히 28살이라고 생각하고 로직을 수행함
이런식으로 데이터 정합성에 문제가 많으므로 RDBMS 표준에서는 격리 수준으로 인정하지도 않는다.
2. Read committed
어떤 트랜잭션의 변경내용이 커밋되어야만 다른 트랜잭션에서 조회할 수 있다.
대부분의 RDBMS 에서 기본적으로 사용하는 격리수준이다.
select 문이 실행되는 동한 Shared Lock 이 걸린다. 조회시에는 실제 테이블 값이 아니라 Undo 영역에 백업된 레코드 값을 가져온다.
하지만 하나의 트랜잭션에서 똑같은 select 쿼리를 실행했을 때는 항상 같은 결과를 가져와야 하는 repeatable read 의 정합성에 어긋난다.
즉, Non-repeatable read 가 발생한다.
1) B 트랜잭션에서 10번 사원의 나이를 조회
2) 27살이 조회됨
3) A 트랜잭션에서 10번 사원의 나이를 27살에서 28살로 바꾸고 커밋
4) B 트랜잭션에서 10번 사원의 나이를 다시 조회
5) 28 살이 조회됨
일반적인 웹 어플리케이션에는 크게 문제되지 않지만, 작업이 금전적인 처리와 연결되어 있다면 문제가 발생할 수 있다.
예를 들어 여러 트랜잭션에서 입금/출금 처리가 계속 진행되는 트랜잭션들이 있고 오늘의 입금 총 합을 보여주는 트랜잭션이 있다고 하면 총합을 계산하는 select 쿼리는 실행될 때마다 다른 결과값을 가져올 것이다.
3. Repeatable read
트랜잭션이 시작되기 전에 커밋된 내용에 대해서만 조회할 수 있는 격리 수준이다.
MySQL DBMS 에서 기본적으로 사용하고 있고, 이 격리 수준에서는 Non-repetable read 부정합이 발생하지 않는다.
트랜잭션이 완료될 때까지 select 문이 사용하는 모든 데이터에 Shared Lock 이 걸린다. 따라서 트랜잭션이 범위 내에서 조회한 데이터의 내용이 항상 동일함을 보장한다.
트랜잭션이 시작 시점 데이터의 일관성을 보장해야 하기 때문에 트랜잭션의 실행시간이 길어질수록 계속 멀티버전을 관리해야하는 단점이 발생하고 Phantom Read 가 발생할 수 있다.
1) 10번 트랜잭션이 5000번 사원을 조회
2) 12번 트랜잭션이 5000번 사원의 이름을 변경하고 커밋
3) 10번 트랜잭션이 5000번 사원을 다시 조회
4) 언두영역에 백업된 데이터 반환
자신의 트랜잭션 번호보다 낮은 트랜잭션 번호에서 변경된(+커밋된) 것만 보게 되는 것이다.
실제로 영향을 미칠 정도로 오래 지속되는 경우는 없어서 Read committed 와 Repetable read 의 성능차이는 거의 없다고 한다.
4. Serializable
가장 단순하고 가장 엄격한 격리 수준이다.
격리 수준이 serializable 일 경우 읽기 작업에도 Shared Lock 을 설정하게 되고 이러면 동시에 다른 트랜잭션에서 이 레코드를 변경하지 못하게 된다.
이러한 특성 때문에 동시처리 능력이 다른 격리수준보다 떨어지고 성능저하가 발생하게 된다.
참고링크
https://akasai.space/db/about_isolation/
https://www.youtube.com/watch?v=bLLarZTrebU