Prologue
- 이전 트랜잭션의 ACID 특성 중 한 가지인 격리성에 대해 조금 더 깊게 공부하기 위해 해당 포스트를 작성하게 되었다.
격리성
-
트랜잭션의 특성 중 한 가지인
격리성
(Isolation)은 현재 수행 중인 트랜잭션이 완료될 때까지 트랜잭션이 생성된 중간 연산 결과에 다른 트랜잭션들이 접근할 수 없음을 의미한다. -
격리성은 여러 개의 격리 수준으로 나뉘어 격리성을 보장한다.
-
격리 수준은 아래와 같이 4개가 있다.
-
위로 갈수록 동시성이 강해지지만 격리성은 약해지고, 아래로 갈수록 동시성은 약해지지만 격리성은 강해진다.
-
또한 각 단계마다 나타나는 현상이 있다.
-
READ_UNCOMMITTED
- 팬텀 리드, 반복 가능하지 않는 조회, 더티 리드가 발생할 수 있다. -
READ_COMMITTED
- 팬텀 리드, 반복 가능하지 않는 조회가 발생한다. -
REPEATABLE_READ
- 팬텀 리드가 발생한다.
-
격리 수준에 따라 발생하는 현상
- 격리 수준에 따라 발생하는 현상은 더티 리드, 반복 가능하지 않는 조회, 팬텀 리드가 있다.
더티 리드
-
더티 리드(Dirty Read)는 반복 가능하지 않은 조회와 유사하며 한 트랜잭션이 실행 중일 때 다른 트랜잭션에 의해 수정되었지만 아직 ‘커밋되지 않은’행의 데이터를 읽을 경우 발생한다.
-
예를 들어 사용자 A의 계좌(A 트랜잭션)가 $100을 $0으로 변경한 내용이 아직 커밋되지 않은 상태라도 그 이후에 사용자 B가 조회했을 때 결과가 $0으로 나온 경우를 말한다.
-
만약 수정한 트랜잭션 A가 그 변경 사항을 롤백하면, 그 데이터를 읽은 다른 트랜잭션 B가 더티 데이터를 가지고 있다고 말한다.
반복 가능하지 않은 조회
-
반복 가능하지 않은 조회(Non-Repeatable Read)는 한 트랜잭션 내의 같은 행에 두 번 이상 조회가 발생했는데, 그 값이 다른 경우를 가리킨다.
-
예를 들어 팬시의 계좌가 1번인 잔고 안에 $100가 들어 있다.
처음에 계좌 1번의 데이터를 읽으면, $100를 읽게 된다.
하지만 이 때 팬시가 계좌가 1번인 잔고에서 $10을 다른 계좌로 송금했다.
그러면 두 번째로 계좌 1번의 데이터를 읽을 때에는 $90를 읽게 된다.
-
이처럼 한 트랜잭션 내에서 같은 Key를 가진 Row를 두 번 읽었는데 그 사이에 값이 변경되거나 삭제되어 결과가 다르게 나타나는 현상을 말한다.
팬텀 리드
-
팬텀 리드(Phantom Read)는 한 트랜잭션 내에서 동일한 쿼리를 보냈을 때 해당 조회 결과가 다른 경우를 말한다.
-
다른 트랜잭션 의한 변경 사항으로 인해 현재 사용중인 트랜잭션의 Where 절의 조건에 맞는 새로운 행이 생길 수 있는 경우에 관한 것이다.
-
예를 들어, 팬시의 잔고가 $100 미만인 계좌가 2개인 DB에서
$100 미만인 계좌를 찾는 트랜잭션이 있고, 그 트랜잭션안에서 Select 쿼리를 2번 수행한다고 가정하자.
처음에 데이터를 읽으면 2개의 계좌를 찾게 된다.
하지만 이 때 다른 트랜잭션에서 $0인 계좌를 새로 만들고 난 이후에
두 번째 데이터를 읽을 때에는 3개의 계좌를 찾게 된다.
-
이처럼 Where 절의 조건에 맞는 새로운 행이 생길 수 있는 경우를 말한다.
격리 수준
-
위의 표에서 알 수 있듯이 엄격해질수록 이상 현상을 허용하지 않는다.
-
격리 수준은 세 가지 이상 현상을 정의한 뒤 어떤 현상을 허용/허용하지 않는지에 따라 격리 수준이 나뉜다.
-
개발자는 이 격리 수준을 통해 전체 처리량(Throughput)과 데이터 일관성 사이에서 trade 할 수 있다.
격리 수준의 필요성
-
DB는 데이터 무결성을 보장하는 것이 중요하다.
데이터 무결성
은 데이터의 정확성과 일관성을 유지하고 보증하는 것을 말한다.그리고 그 무결성을 보장하기 위한 특징이 ACID(Atomicity, Consistency, Isolation, Durability)이다.
데이터베이스는 ACID 특징과 같이 트랜잭션이 독립적인 수행을 하도록 한다. 그래서 등장한 개념이 Locking이다.
-
Locking
은 트랜잭션이 DB를 다루는 동안 다른 트랜잭션이 관여하지 못하게 막는 역할이다.하지만 무조건적인 Locking으로 동시에 수행되는 수많은 트랜잭션들을 순서대로 처리하면 DB의 성능은 현저히 떨어지게 될 것이다.
그렇다고 해서 성능을 높이기 위해 Locking 범위를 줄인다면, 잘못된 값이 처리될 수 있다.
그래서 최대한 효율적인 Locking 방법이 필요하다.
-
이와 관련된 Locking 방법이 격리 수준(Isolation Level)이다.
READ_UNCOMMITTED
-
가장 낮은 격리 수준으로, 하나의 트랜잭션이 커밋되기 이전에 다른 트랜잭션에 노출되는 문제가 있지만 가장 빠르다.
-
이는 데이터 무결성을 위해 되도록이면 사용하지 않는 것이 이상적이나, 몇몇 행이 제대로 조회되지 않더라도 괜찮은 거대한 양의 데이터를 어림잡아 집계하는 데는 사용하면 좋다.
-
발생할 수 있는 문제
-
Phantom Read
-
Non-Repeatable Read
-
Dirty Read
-
READ_COMMITTED
-
가장 많이 사용하는 격리 수준이며, PostgreSQL, SQL Server, 오라클에서 기본값으로 설정되어 있다.
-
READ_UNCOMMITTED 와 달리 다른 트랜잭션이 커밋 되지 않은 정보는 읽을 수 없다.
즉, 커밋 완료된 데이터만 조회를 허용한다.
-
하지만, 어떤 트랜잭션이 접근한 행을 다른 트랜잭션이 수정할 수 있다.
예를 들어 트랜잭션 A가 수정한 행을 트랜잭션 B가 수정할 수 있다.
-
발생할 수 있는 문제
-
Phantom Read
-
Non-Repeatable Read
-
REPEATABLE_READ
“REPEATABLE READ: This is the default isolation level for InnoDB.” - MySQL 공식문서 -
-
MySQL의 InnoDB엔진
의 기본 격리수준으로, 하나의 트랜잭션이 수정한 행을 다른 트랜잭션이 수정할 수 없도록 막아주지만 새로운 행을 추가하는 것은 막지 않는다.따라서 이후에 추가된 행이 발견될 수 있다.
-
발생할 수 있는 문제
- Phantom Read
SERIALIZABLE
-
말 그대로 트랜잭션을 순차적으로 진행시키는 것을 말한다.(직렬화)
여러 트랜잭션이 동시에 같은 행에 접근할 수 없다.
이 수준은 매우 엄격한 수준으로 해당 행에 대해 격리시키고, 이후 트랜잭션이 이 행에 대해 일어난다면 기다려야 한다.
그렇기 때문에 교착 상태가 일어날 확률도 많고 가장 성능이 떨어지는 격리수준이다.
교착 상태(Dead Lock)란 두 트랜잭션이 각각 Lock을 설정한 다음 서로의 Lock에 접근하여 값을 얻어오려고 할 때 이미 각각의 트랜잭션에 의해 Lock이 설정되어 있기 때문에 양쪽 트랜잭션 모두 무한정 기다려야 하는 상태를 말한다.
- 발생할 수 있는 문제 X
예상 질문
-
트랜잭션 격리 수준이 필요한 이유는 무엇일까요?
-
트랜잭션 격리 수준에 대해 설명해 주세요.
-
트랜잭션 격리 수준에 따라 발생하는 현상에 대해 설명해 주세요.
Reference
-
면접을 위한 CS 전공지식 노트