[대규모 중심 어플케이션 설계] 07장. 트랜잭션
1. 애매모호한 트랜잭션의 개념
1-1. ACID의 의미
트랜잭션이 제공하는 안전성 보장(ACID) : 원자성(Atomicity), 일관성(Consistency), 격리성(Isolation), 지속성(Durability)
원자성(Atomicity) : 여러 쓰기 작업이 하나의 원자적인 트랜잭션으로 묶이고 트랜잭션은 완료(커밋)되거나 결함 발생 시 해당 트랜잭션의 모든 쓰기 작업이중단(어보트)되거나 둘 중 하나여야 한다.
일관성(Consistency) : ACID에서의 일관성은 애플리케이션의 불변식 개념에 의존하고, 일관성을 유지하도록 트랜잭션을 올바르게 정의하는 것은 애플리케이션의 책임이다.
격리성(Isolation) : 동시에 실행되는 트랜잭션은 서로 격리된다. 오라클의 직렬성 격리는 스냅샷 격리를 구현한 것
지속성(Durability) : 트랜잭션이 성공적으로 커밋됐다면 트랜잭션에서 기록한 모든 데이터는 손실되지 않는다는 보장
1-2. 단일 객체 연산과 다중 객체 연산
다중 객체 트랜잭션은 읽기 및 쓰기 연산들이 동일한 트랜잭션에 속하는지 알아낼 수단이 있어야 한다.
관계형 데이터베이스에서는 TCP 연결 기반으로 BEGIN TRANSACTION문, COMMIT문 사이 모든것은 같은 트랜잭션에 속하는 것으로 여겨짐
다중 객체 트랜잭션의 필요성
2. 완화된 격리 수준
2-1. 커밋 후 읽기
가장 기본적인 트랜잭션 격리로 두 가지를 보장해준다
- 데이터베이스에서 읽을 때 커밋된 데이터만 본게 된다 (더티 읽기가 없음)
- 데이터베이스에 쓸 때 커밋된 데이터만 덮어쓰게 된다 (더티 쓰기가 없음)
2-2. 스냅샷 격리와 반복 읽기
각 트랜잭션은 특정 시점에 고정된 데이터베이스의 일관된 스냅샷만을 볼 수 있다
스냅샷 격리 구현은 커밋 후 읽기 격리처럼 전형적으로 더티쓰기를 방지하기 위해 쓰기 잠금을 사용한다
스냅샷 격리의 핵심 원리는 읽는 쪽에서 쓰는 쪽을 결코 차단히지 않고 쓰는 쪽에서 읽은 쪽을 결코 차단하지 않는다는 것
다중 버전 동시성 제어(multi-version concurreny control, MVCC) : 데이터베이스는 객체마다 커밋된 버전 여러 개를 유지할 수 있어야 한다
반복 읽기(repeatable read) = 스냅샷 격리
2-3. 갱신 손실 방지
갱신 손실(lost update) : 애플리케이션이 데이터베이스에서 값을 읽고 변경한 후 변경된 값을 다시 쓸 때 발생 가능
두 트랜젝션이 이 작없을 동시에 하면 두 번째 쓰기 작업이 첫 번째 변경을 포함하지 않으므로 변경중 하나는 손실 될 수 있다
해결책
1. 원자적 쓰기 연산
2. 명시적인 잠금
3. 갱신 손실 자동 감지
4. compare-and-set
5. 충돌 해소와 복제
2-4. 쓰기 스큐와 팬텀
쓰기 스큐(write skew) : 두 트랜잭션이 동시에 같은 객체를 읽어서 두 개의 다른 객체를 갱신, 더티쓰기나 갱신 손실 이상 현상 발생
3. 직렬성(Serializability)
직렬성 격리는 여러 트랜잭션이 병렬로 실행되더라도 최종결과는 동시성 없이 한 번에 하나씩 직렬로 실행될 때와 같도록 보장
3-1. 실제적인 직렬 실행
동시성 문제를 해결하는 가장 간단한 방법은 동시성을 완전히 제거하는 것, 한 번에 트랜잭션 하나씩만 직렬로 단일 스레드에서 실행
단일 스레드를 활용하기 위해서는 트랜잭션이 전통적인 현태와는 다르게 구조화 되어야 한다
현대의 스토어드 프로시저(stored procedure) 구현은 PL/SQL을 버리고 기존의 범용 프로그래밍 언어를 사용한다
단일 스레드 시스템의 처리량을 높이기 위해 파티셔닝 기법을 활용하면 된다
3-2. 2단계 잠금(2PL, 2-phase locking)
2단계 잠금(2PL)과 2단계 커밋2PC(2PC)는 아주 비슷하게 들리지만 완전히 다르다
2PL은 쓰기 트랜잭션이 다른 쓰기 트랜잭션 뿐만아니라 읽기 트랜잭션도 진행하지 못하게 막는다
2단계 잠금의 가장 큰 약점은 성능이다. 완화된 격리 수준을 쓸 때보다 트랜잭션 처리량과 질의 응답 시간이 크게 나빠진다
2단계 잠금이 서술 잠금 까지 포함하면 모든 형태의 쓰기 스큐를 막을 수 있고 격리 수준이 직렬성 격리가 된다
서술 잠금은 성능이 좋지 않아 대부분 2PL을 지원하는 데이터베이스는 서술 잠금을 간략하게 근사한 색인 범위 잠금, 다음 키 잠금을 구현
색인 범위 잠금은 서술 잠금보다 정밀하지 않지만 오버헤드가 훨씬 더 낮기 때문에 좋은 타협안이 된다
3-3. 직렬성 스냅샷 격리(SSI, Serializable Snapshot Isolation)
직렬성 스냅샷 격리는 낙관적 동시성 제어 기법이다.
예비 용량이 충분하고 트랜잭션 사이의 경쟁이 너무 심하지 않으면 낙관적 동시성 제어 기법은 비관적 동시성 제어보다 성능이 좋은 경향이 있다
SSI는 스냅샷 격리 위에 쓰기 작업 사이의 직렬성 충돌을 감지하고 어보트시킬 트랜잭션을 결정하는 알고리즘을 추가한다
2단계 잠금과 비교할 때 트랜잭션이 다른 트랜잭션을 기다리느라 차단되지 않기 때문에 읽기 작업이 많은 경우 성능이 더 뛰어나다
순차 실행과 비교할 때 단일 CPU 코어의 처리량에 제한되지 않는다
어보트 비율은 전체적인 성능에 큰 영향을 미친다
오랫동안 데이터를 읽고 쓰는 트랜잭션은 충돌 가능성이 높아 어보트되기 쉬우므로 읽기-쓰기 트랜잭션이 짧을 수록 유리