4 minute read

트랜잭션은 애플리케이션이 여러 읽기 및 쓰기를 하나의 논리적 단위로 그룹화하는 방법이다. 트랜잭션을 사용하면 일부 작업은 성공하고 일부는 실패하는 경우에 대해 걱정할 필요가 없기 때문에 애플리케이션의 오류 처리가 훨씬 간단해진다.

트랜잭션이 우리에게 해주는 것을 당연시해서는 안 된다. 트랜잭션은 자연의 법칙이 아니라 데이터베이스에 액세스하는 애플리케이션의 프로그래밍 모델을 단순화하기 위한 목적으로 만들어졌다. 트랜잭션을 사용하면 데이터베이스가 대신 처리해 주기 때문에 애플리케이션은 특정 잠재적 오류 시나리오와 동시성 문제를 무시할 수 있다(이를 안전 보장, safety guarantees이라고 한다).

트랜잭션이 필요한지 어떻게 알 수 있을까?

두 가지를 이해해야 한다.

  • 트랜잭션이 어떤 안전성을 보장할 수 있는가
  • 트랜잭션과 관련된 비용은 얼마인가

트랜잭션개념의 모호성

오늘날 거의 모든 관계형 데이터베이스와 일부 비관계형 데이터베이스는 트랜잭션을 지원한다. MySQL, PostgreSQL, Oracle, SQL Server 등의 트랜잭션 지원은 1975년 최초의 SQL 데이터베이스인 System R의 트랜잭션과 유사하다.

2000년대 후반에 비관계형(NoSQL) 데이터베이스가 인기를 얻기 시작했다. 이 새로운 세대의 데이터베이스 중 상당수는 트랜잭션을 완전히 포기하거나, 이전보다 훨씬 약한 보장 세트를 설명하기 위해 단어를 재정의했다. 트랜잭션은 확장성과 정반대되는 개념이다. 대규모 시스템은 우수한 성능과 고가용성을 유지하기 위해 트랜잭션을 포기해야 한다는 대중적인 믿음이 생겨났다.

ACID의 의미

트랜잭션이 제공하는 안전 보장은 원자성, 일관성, 격리, 내구성을 의미하는 약어인 ACID로 설명된다. 이 용어는 데이터베이스의 내결함성 메커니즘에 대한 정확한 용어를 정립하기 위해 1983년 Theo Härder 와 Andreas Reuter 의해 만들어졌다.

원자성 Aatomicity

원자성이란 더 작은 부분으로 나눌 수 없는 것을 의미한다. 예를 들어, 멀티 스레드 프로그래밍에서 한 스레드가 원자 연산을 실행하면 다른 스레드가 그 연산의 반쯤 완성된 결과를 볼 수 있는 방법이 없다. 시스템은 작업 전 또는 작업 후의 상태만 있을 수 있으며, 그 중간 상태는 존재하지 않는다.

ACID 원자성은 클라이언트가 여러 쓰기를 수행하려고 하지만 쓰기 중 일부가 처리된 후에 오류가 발생하는 경우(예: 프로세스 충돌, 네트워크 연결 중단, 디스크가 꽉 차거나 무결성 제약 조건을 위반하는 경우)에 발생하는 상황을 설명한다. 쓰기가 원자 트랜잭션으로 그룹화되어 있는데 오류로 인해 트랜잭션을 완료(커밋)할 수 없는 경우 트랜잭션이 중단되고 데이터베이스는 해당 트랜잭션에서 지금까지 수행한 모든 쓰기를 폐기하거나 실행 취소해야 한다.

원자성이 없으면 여러 변경을 수행하는 도중에 오류가 발생하면 어떤 변경이 적용되고 어떤 변경이 적용되지 않았는지 알기 어렵다. 애플리케이션이 다시 시도할 수는 있지만, 동일한 변경을 두 번 수행하여 데이터가 중복되거나 부정확해질 위험이 있다. 원자성은 이 문제를 단순화해준다. 트랜잭션이 중단된 경우 애플리케이션은 아무것도 변경하지 않았는지 확인할 수 있으므로 안전하게 재시도할 수 있다.

오류 발생 시 트랜잭션을 중단하고 해당 트랜잭션의 모든 쓰기를 삭제할 수 있는 기능이 바로 ACID 원자성의 특징이다.

멀티스레딩의 원자성과 ACID의 원자성

ACID의 맥락에서 원자성은 동시성에 관한 것이 아니다. 여러 프로세스가 동시에 동일한 데이터에 액세스하려고 시도할 때 어떤 일이 발생하는지는 설명하지 않는다. 왜냐하면 이는 격리를 위한 문자 I(Isolation)에서 다루기 때문이다.

ACID 원자성은 하나의 클라이언트가 여러 쓰기를 수행하려고 하지만, 쓰기 중 일부가 처리된 후에 오류가 발생하는 경우에 발생하는 상황을 설명한다.

예를 들어:

  • 프로세스 충돌
  • 네트워크 연결 중단
  • 디스크가 꽉 차거나 무결성 제약 조건을 위반하는 경우

쓰기가 원자 트랜잭션으로 그룹화되어 있는데 오류로 인해 트랜잭션을 완료(커밋)할 수 없는 경우 트랜잭션이 중단되고 데이터베이스는 해당 트랜잭션에서 지금까지 수행한 모든 쓰기를 폐기하거나 실행 취소해야 한다.

원자성이 없으면 발생하는 문제들

원자성이 없으면 여러 변경을 수행하는 도중에 오류가 발생하면 어떤 변경이 적용되고 어떤 변경이 적용되지 않았는지 알기 어렵다. 애플리케이션이 트랜잭션을 다시 시도할 수는 있지만, 동일한 변경을 두 번 수행하여 데이터가 중복되거나 부정확해질 위험이 있다. 원자성은 이 문제를 단순화한다. 트랜잭션이 중단된 경우 애플리케이션은 아무것도 변경하지 않았는지 확인할 수 있으므로 안전하게 재시도할 수 있다. 오류 발생 시 트랜잭션을 중단하고 해당 트랜잭션의 모든 쓰기를 삭제할 수 있는 기능이 바로 ACID 원자성의 특징이다.

일관성

데이터 시스템에서 일관성이라는 단어는 여러 의미로 사용된다.

  • 일관된 해싱은 일부 시스템에서 리밴딩에 사용하는 파티셔닝 접근 방식이다.
  • CAP 정리에서 일관성이라는 단어는 선형화 가능성을 의미하는 데 사용된다.
  • ACID의 맥락에서 일관성은 데이터베이스가 “좋은 상태”에 있다는 애플리케이션 고유의 개념을 의미한다.

회계 시스템에서 모든 계정의 대변과 차변은 항상 균형을 이루어야 하는 것처럼, 데이터에 대해 항상 참이어야 하는 불변성이 있다는 것이 ACID 일관성의 개념이다. 트랜잭션이 이러한 불변성에 따라 유효한 데이터베이스로 시작되고 트랜잭션 중 모든 쓰기가 유효성을 유지한다면 불변성이 항상 만족된다고 확신할 수 있다.

일관성을 유지하는 것은 애플리케이션이 할 일이다.

그러나 이러한 일관성 개념은 애플리케이션의 불변성 개념에 따라 달라지며, 일관성을 유지할 수 있도록 트랜잭션을 올바르게 정의하는 것은 애플리케이션의 책임이다. 불변성을 위반하는 잘못된 데이터를 작성해도 데이터베이스가 이를 막을 수는 없다.

일부 특정 종류의 불변성은 외래 키 제약 조건이나 고유성 제약 조건 등을 사용하여 데이터베이스에서 확인할 수 있다. 그러나 일반적으로 어떤 데이터가 유효하거나 유효하지 않은지는 애플리케이션이 정의하고 데이터베이스는 이를 저장할 뿐이다.

원자성, 격리성, 내구성은 데이터베이스의 속성인 반면 일관성(ACID 의미에서의 일관성)은 애플리케이션의 속성이다. 일관성은 데이터베이스에만 의존하는 것은 아니다. 따라서 문자 C는 실제로 ACID에 속하지 않는다.

격리 Isolation

대부분의 데이터베이스는 여러 클라이언트가 동시에 액세스한다. 여러 클라이언트가 동일한 데이터베이스 레코드에 액세스하는 경우에는 동시성 문제(경합 조건)가 발생할 수 있다.

데이터베이스에 저장된 카운터를 두 클라이언트가 동시에 증가시킨다고 가정해 보자. 각 클라이언트는 현재 값을 읽고 1을 더한 다음 새 값을 다시 써야 한다. 두 번의 증가가 발생하면, 카운터가 2만큼 증가해야 하지만, 실제로는 경쟁 조건으로 인해 1 만큼만 증가할 수 있다.

#직렬화 기능성 Serializability

ACID의 의미에서의 격리는 동시에 실행되는 트랜잭션이 서로 격리되어 서로의 발가락을 밟을 수 없음을 의미한다. 고전적인 데이터베이스 교과서에서는 격리를 직렬화 가능성(Serializability)으로 정의한다. 이는 각 트랜잭션이 전체 데이터베이스에서 실행 중인 유일한 트랜잭션인 것처럼 보일 수 있다는 것을 의미한다.

그러나 직렬화 가능 격리는 거의 사용되지 않는다. Oracle 11g와 같이 널리 사용되는 일부 데이터베이스는 이를 구현하지도 않는다. Oracle에는 “직렬화 가능”이라는 격리 수준이 있지만 실제로는 직렬화 가능성보다 약한 스냅샷 격리라는 것을 구현한다.

내구성 Durability

데이터베이스 시스템은 데이터를 안전하게 저장할 수 있는 장소를 제공해야 한다. 내구성이란 트랜잭션이 성공적으로 전송되면 하드웨어 결함이 발생하거나 데이터베이스가 충돌하더라도 기록된 모든 데이터가 잊혀지지 않는다는 약속을 의미한다.

단일 노드의 내구성과 복제 노드의 내구성의 차이

단일 노드 데이터베이스에서 내구성이란 일반적으로 데이터가 하드 드라이브나 SSD와 같은 비휘발성 스토리지에 기록되었다는 것을 의미한다. 또한 일반적으로 디스크의 데이터 구조가 손상된 경우 복구할 수 있도록 미리 쓰기 로그 또는 이와 유사한 로그가 포함된다.

복제된 데이터베이스에서 내구성이 있다는 것은 데이터가 일정 수의 노드에 성공적으로 복사되었다는 것을 의미할 수 있다. 내구성을 보장하기 위해 데이터베이스는 이러한 쓰기 또는 복제가 완료될 때까지 기다렸다가 트랜잭션이 성공적으로 커밋된 것으로 보고해야 한다.