DB에서는 하나의 테이블에 여러 트랜잭션이 걸리는 동시성 문제가 발생 할 수 있다. 동시성 문제는 운이 없을 때만 발생하기에 테스트로 발견 하기 어렵다. 따라서 동시성 문제가 큰 이슈가 되는 결제, 송금 같은 경우에는 Serializable(직렬성) 격리를 사용한다. 하지만 Serializable은 오버헤드가 크게 발생하기 때문에, 비교적 동시성 문제가 크지 않을 때는 완화된 격리 수준을 사용한다. 이때 Serializable, 완화된 격리(read commited, repeatable read, dirty read)등이 Isolation Level이다.
Read Committed
Read Committed 수준에서는
- dirty read를 막아준다.
- dirty wirte를 막아준다.
이렇게 2가지를 보장해 준다.
Dirty Read
dirty read란 아직 트랜잭션이 커밋되지 않은 데이터를 읽어 오는 것을 의미한다.
dirty read를 허용하지 않는 이유
- 예를 들어 DB에서 상품을 하나 추가하고, 상품 개수 테이블에서 상품 개수를 1 증가해야하는 로직이 있다면, 상품을 하나 추가한 순간dirty read가 되면 추가된 상품은 조회가 되지만 상품개수는 증가하지 않은 상태가 된다. 위 예시처럼 부분적으로 갱신된 데이터는 혼란을 줄 수 있다.
- commit되지 않은 데이터를 읽어서 사용한 경우 읽어온 commit이 안 된 데이터가 abort가 되면 유효하지 않은 데이터를 사용하는 일이 발생할 수 있다.
Dirty Write
여러 트랜잭션에서 하나의 객체를 수정하려고 한다면, 제일 마지막에 커밋된 트랜잭션이 반영되게 되고 이를 dirty write라고 한다. 그렇다면 A가 먼저 수정을 진행하는데 뒤 늦게 수정을 한 B가 먼저 커밋을 넣게 되면 A의 수정내용은 사라지게 된다.
Read Commit 격리 수준에서는 가장 흔하게 row level locking을 사용한다. 기본적으로 객체를 변경하고 싶다면 객체에 대한 lock을 획득하고 수정후 커밋을 해야한다. 따라서 이미 lock이 걸린 객체에는 접근이 불가능해 먼저 동작하는 트랜잭션이 끝날때 까지 기다리게 된다. 하지만 같은 객체에 접근하는 트랜잭션이 많을 수록 늦게 접근한 트랜잭션은 오랜시간을 기다리게 된다는 단점이 있다.
Repeatable read(Snapshot Isolation)
repeatable read는 dirty read, dirty write를 금지하며 non-repeatable read도 막는다.
Non-Repeatable Read
위는 사용자 A가 국민은행 잔고를 확인하고, 우리 은행 잔고를 확인하기 전에 우리 은행에서 100을 국민은행으로 송금하고 커밋한 상황인다. 그렇다면 사용자 A는 자신의 총 자산을 900만원으로 보게된다. 즉 하나의 트랜젝션에서 객체를 읽었지만 변화된 값을 읽게 되는 문제가 있다.
Repeatable Read는 이 문제를 막기위해 MVCC(Multi Version Concurrency Control, 다중 버전 동시성 제어)기법을 사용한다.
MVCC(Multi Version Concurrency Control, 다중 버전 동시성 제어)
DB는 객체마다 커밋된 여러개의 버전을 유지 할 수 있어야 한다. 객체마다 다른 시점의 DB를 볼 수 있어야 하기 때문에, 위의 2-1 그림에서 사용자 A는 송금이 발생전 버전의 객체를 보고 있어야 하듯이 말이다. 이렇게 커밋마다 여러개의 버전을 가지는 기법을 MVCC라고 한다.
PostgreSQL에서는 각 트랜잭션에 1씩 증가하는 trancation Id값을 부여하고 다음의 조건에 맞춰 일관된 스냅샵을 보여주게 된다.
- DB는 트랜잭션을 시작할때 그 시점에 진행중인(commit이나 abort가 되지않은) 모든 트랜잭션 목록을 만들고, 이 트랜잭션 데이터는 모두 무시한다. 설렁 나중에 커밋 되어도 무시한다.
- abort된 쓰기 데이터는 무시한다.
- 자신의 트랜잭션 id보다 더 큰 id를 가진 트랜잭션이 쓴 데이터는 커밋의 여부에 관계없이 모두 무시된다.
- 그 밖의 모든 데이터는 query로 볼 수 있다.
이러한 조건에 맞춰 그림 2-1을 repeatalbe read로 바꾸어 보면 사용자 A의 트랜잭션 id는 1이 되고 송금의 트랜잭션 id는 2가 된다. 즉 사용자 A는 우리은행 계좌에 접근할 때 400만원으로 바뀐 id가 2인 트랜젹을 보지 못하기 때문에(3번 규칙) 500만원을 응답하게 된다.
Repeatable Read의 핵심은 읽는 쪽에서 쓰는 쪽을 결코 차단하지 않고 쓰는 쪽에서 읽는 쪽을 결코 차단하지 않느다는 것이다.
'computer science > DB' 카테고리의 다른 글
하나의 트랙잭션에서 insert 다음에 나오는 select 쿼리는 insert된 결과를 가지고 있을까? (0) | 2024.06.23 |
---|---|
[데이터 중심 애플리케이션 설계] ACID에서 애플케이션과 DB의 책임 분리 (0) | 2024.06.23 |
#1 Redis type (0) | 2024.03.04 |
DBCP(DB connection pool) (0) | 2023.11.04 |