07장 트랜잭션

어떤 저자들은 2단계 커밋에서 유발되는 성능이나 가용성 문제 때문에 생기는 비용이 너무 커서 이를 지원할 수 없다고 주장했다. 우리는 항상 트랜잭션 없이 코딩하는 것보다 트랜잭션을 과용해서 병목지점이 생기는 성능 문제를 애플리케이션 프로그래머가 처리하는게 낫다고 생각한다.

냉혹한 현실 세계에서 데이터 시스템은 여러 가지 문제가 생길 수 있다.

  1. 데이터베이스 소프트웨어나 하드웨어는 언제라도 실패할 수 있다.
  2. 애플리케이션은 언제라도 죽을 수 있따.
  3. 네트워크가 끊기면 애플리케이션과 데이터베이스의 연결이 갑자기 끊기거나 데이터베이스 노드 사이의 통신이 안될 수 있다.
  4. 여러 클라이언트가 동시에 데이터베이스에 쓰기를 실행해서 다른 클라이언트가 쓴 내용을 덮어 쓸 수 있다.
  5. 클라이언트가 부분적으로만 갱신돼서 비정상적인 데이터를 읽을 수 있다.
  6. 클라이언트 사이의 경쟁 조건은 예측하지 못한 버그를 유발할 수 있다.

수십년동안 트랜잭션은 문제를 단순화하는 방식으로 해결해왔다. 몇개의 읽기와 쓰기를 하나의 논리적 단위로 묶는 방법이다. (하나의 쓰기가 실패하면 해당 논리적 단위가 전부 실패)

트랜잭션은 항상 모든 애플리케이션에서 필요하지 않다. “그렇다면 트랜잭션이 필요한지 어떻게 알 수 있을까?” 이를 대답하려면 트랜잭션이 제공하는 안전성 보장에는 어떤 것이 있으며, 이와 관련된 비용은 무엇인지 정확히 이해해야 한다.

이번 장에서는 문제가 생길 수 있는 여러 예를 조사하고 이런 문제를 방지하기 위해 데이터베이스에서 사용하는 알고리즘을 살펴본다.

먼저 동시성 제어를 깊게 다루며 발생할 수 있는 다양한 종류의 경쟁 조건과 데이터베이스에서 커밋 후 읽기, 스냅샷 격리, 직렬성과 같은 격리 수준을 어떻게 구현하는지 설명한다.

애매모호한 트랜잭션의 개념

분산 데이터베이스 vs 트랜잭션의 문제는 단순하지 않다. 트랜잭션도 이점과 한계가 있다.

ACID의 의미

저자는 지금의 ACID가 마케팅 용어로 변질되었다고 하고, ACID 표준을 따르지 않는 BASE는 더 모호하고 ‘ACID’가 아닌 뭔가 정도로 묘사한다고 되어있다.

원자성

일반적으로 원자적이란 더 작은 부분으로 쪼갤 수 없는 뭔가를 가리킨다.

컴퓨터의 여러 분야에서 비슷한데 미묘한 차이가 있다.

먼저 다중 스레드 프로그래밍에서 한 스레드가 원자적 연산을 실행한다면 다른 스레드에서 절반만 완료된 연산을 관찰할 수 없다. 시스템은 연산을 실행하기 전이나 실행한 후의 상태에만 있을 수 있으며 그 중간 상태에서 머물 수 없다.

이러한 것들은, ACID의 맥락에서는 동시성과 관련이 없다.(보통 위와 같은 문제는 격리성에서 다룬다)

ACID에서는 ‘쓰기 작업 몇 개를 실행하려고 하는데, 그 중 일부만 처리된 후 결함이 생기면 무슨 일이 생기는지 설명한다’

여러 쓰기 작업이 하나의 원자적 트랜잭션으로 묶여있는데, 결함 때문에 commit 될 수 없다면 abort되고 데이터베이스는 이 트랜잭션에서 지금까지 실행한 쓰기를 무시하거나 취소해야 한다.

즉 어떠한 변경은 효과가 있고, 어떠한 것은 그렇지 않은지 알기 어려운 상태를 배제하려는 노력이고,

책에서는 abortability가 원자성보다 더 나은 설명이라고 이야기한다.

일관성

ACID의 맥락에서 일관성은 데이터베이스가 좋은 상태에 있어야 한다는 것의 애플리케이션에 특화된 개념을 가리킨다.

ACID의 일관성 아디어는 항상 진실이어야 하는, 데이터에 관한 어떤 선언 즉, 불변식(invariant)가 있다는 것이다.

다만 이러한 것들은 어플리케이션의 논리에 기댈 수 밖에 없어서, 현실적으로는 C는 ACID에 속하지 않는다고 언급하고 있다.

격리성

ACID에서 격리성은 동시에 실행되는 트랜잭션은 서로 격리된다는 것을 의미한다.

지속성

지속성은 트랜잭션이 성공적으로 커맷됐다면 하드웨어 결함이 발생하거나 데이터베이스가 죽더라도 트랜잭션에서 기록한 모든 데이터는 손실되지 않는다는 보장이다.

단일 객체 연산과 다중 객체 연산

요약하면 ACID에서 원자성과 격리성은 클라이언트가 한 트랝개션 내에서 여러번의 쓰기를 하면 데이터베이스가 어떻게 해야 하는지를 서술한다.

원자성 : 쓰기를 이어서 실행하는 도중 오류가 발생하면 트랜잭션은 어보트돼야 하고 그때까지 쓰여진 내용은 폐기되어야 한다. (전부 반영되거나 아무것도 반영되지 않는 것을 보장)

격리성 : 동시에 실행되는 트랜잭션들은 서로를 방해하지 말아야 한다. 한 트랜잭션이 여러번 쓴다면 다른 트랜잭션은 그 내용을 전부 볼 수 있든지 아무것도 볼 수 없든지 둘 중 하나여야한다.

단일 객체 쓰기

다중 객체 트랜잭션의 필요성

오류와 어보트 처리

어보트의 취지는 안전하게 재시도를 하기 위함이다.

완화된 격리 수준