데이터 중심 애플리케이션 설계 7장(트랜잭션)을 읽던 중 데이터 시스템에서 발생할 수 있는 문제들과, 안전성을 보장하는 트랜잭션의 성질인 ACID에 대한 내용을 접했다.
나는 막연히 트랜잭션은 ACID의 성질을 통해 안전성을 보장한다고 알고 있었지만, ACID의 각 성질이 어떻게 안전성을 보장하는 것인지 깊게 생각하지 못했다.
그래서, 이번에는 데이터 시스템에서 생길 수 있는 문제들을 살펴보고, 트랜잭션은 어떻게 문제들을 해결하는지 ACID의 각 성질을 살펴보며 알아보겠다.
데이터 시스템은 아래와 같은 문제들이 발생할 수 있다.
시스템이 신뢰성을 지니려면 이러한 문제들을 해결해야한다.
트랜잭션은 ACID라는 네 가지 성질을 통해 안전성을 보장하여 위 문제들을 해결한다.
ACID는 트랜잭션이 안전성을 보장할 수 있게해주는 4가지 성질이다.
4가지 성질은 다음과 같다.
ACID의 각 성질을 아래와 같은 과정으로 살펴보겠다.
만약 트랜잭션에서 여러 변경 중 일부만 진행된 상태에서 네트워크 연결이 끊기거나 프로세스가 죽는 등의 문제로 트랜잭션이 중단되면 어떻게 될까?
여러 변경 중 어떤 변경은 효과가 있고, 어떤 변경은 그렇지 않은지 알기 어렵다. 또한 재시도를 하면서 중복되거나 잘못된 데이터가 만들어지기도 쉽다.
원자성은 한 트랜잭션을 더 작은 부분으로 쪼갤 수 없는 원자처럼 취급하는 성질이다. 즉, 트랜잭션은 트랜잭션 내의 모든 작업이 전부 실행된 상태나 하나도 실행되지 않은 상태만 존재할 수 있다는 것이다.
원자성은 트랜잭션 실행 중 문제가 발생할 경우 해당 트랜잭션에서 기록한 모든 내용을 취소하는 어보트(abort)를 진행하여 문제를 해결한다.
이를 통해 트랜잭션은 모든 작업이 실행되거나 아무 변경이 일어나지 않음을 보장하고, 트랜잭션이 어보트됐다면 애플리케이션에서 이 트랜잭션이 어떤 것도 변경하지 않았음을 알 수 있으므로 안전하게 재시도할 수 있다.
계좌 송금 작업을 진행하기 위해서는 계좌에서 송금할 금액을 출금해야한다. 하지만, 계좌에 잔액이 충분하지 않은 상태에서 작업이 진행된다면 계좌의 잔액이 음수가 돼서 유효하지 않은 상태가 된다.
일관성은 데이터베이스가 트랜잭션 실행 후에도 유효한 상태에 있어야 한다는 성질이다.
이렇게 유효한 상태를 유지하기 위해서는 트랜잭션 진행 중에 유효하지 않은 데이터 변경이 일어날 때 트랜잭션을 어보트하면 된다.
하지만, 생각해보면 일관성을 보장하는 것이 데이터베이스만의 역할인지는 의문이다. 문자열인지 정수인지는 타입을 통해 유효성을 검증할 수 있지만, 음수인지, n글자인지 등 다양한 경우 애플리케이션에서 유효성을 검증해야하기 때문이다.
위 그림을 보면, 사용자 1과 2가 거의 동시에 조회 및 변경 작업을 진행하고 있다. 하지만, 분명 두 사용자가 각각 카운터 증가 작업을 시행했음에도 카운터가 1만 증가하는 문제가 발생했다.
이처럼 동시에 여러 클라이언트에서 동일한 레코드에 접근하면 동시성 문제가 발생한다.
격리성은 동시에 실행되는 트랜잭션은 격리된다는 것을 의미한다. 즉, 여러 트랜잭션이 동시에 실행됐더라도 결과가 트랜잭션이 순차적으로 실행됐을 때의 결과와 동일하도록 보장한다.
격리성을 보장하기 위해 데이터베이스는 잠금 기능과 다양한 격리 수준을 제공한다. 격리 수준 중 Serializable이 트랜잭션이 순차적으로 실행되도록 보장하여 위 그림에서 발생하는 문제를 해결할 수 있다. 하지만 Serializable 격리 수준은 심각한 성능 손해를 동반하므로 거의 사용되지 않는다.
대신 스냅샷 격리라는 대안이 있다. 스냅샷 격리는 각 트랜잭션이 시작될 때의 스냅샷을 기반으로 작업을 수행한다. 그리고 트랜잭션이 커밋을 시도할 때 변경된 데이터가 중간에 다른 트랜잭션에 의해 변경되었는지(이런 현상을 Write Skew 라고 함) 확인할 수 있도록 한다. 자세한 내용은 아래 링크를 참고하자.
https://www.geeksforgeeks.org/what-is-snapshot-isolation/
여러 문제로 인해 데이터가 소실되는 문제가 발생할 수 있다.
지속성은 트랜잭션이 성공적으로 커밋됐다면, 기록한 모든 데이터는 소실되지 않는다는 성질이다.
지속성을 보장하기 위해서는 트랜잭션이 커밋되기 전에 쓰기나 복제가 완료되어야 한다. 복제, 백업도 지속성을 보장하는 하나의 방법이다.
하지만 그럼에도 불구하고 모든 HDD와 백업이 동시에 파괴된다면 어쩔 수 없기 때문에 완벽한 지속성은 존재할 수 없다.
역시나 트랜잭션도 트레이드오프가 있다.
위에서 살펴본 것 처럼 트랜잭션은 안전성을 보장하여 여러 문제를 쉽게 해결할 수 있도록 도와준다. 하지만 트랜잭션이 실행하면서 잠금으로 인해 성능이 저하되고 데드락이 발생할 수 있다는 문제점도 존재한다.
위에서 살펴본 ACID를 통해 내 애플리케이션에 트랜잭션이 필요한지 깊게 생각해본 뒤 사용 여부를 결정해야한다.