본문 바로가기

CS/데이터베이스

트랜잭션(Transaction)과 ACID

1. 트랜잭션이란?

트랜잭션(Transaction)은 데이터베이스의 상태를 변환시키는 하나의 논리적 기능을 수행하기 위한 작업의 단위 또는 한꺼번에 모두 수행되어야 할 일련의 연산들을 의미합니다.

트랜잭션의 예

트랜잭션을 설명할 때, 가장 많이 등장하는 예시는 은행계좌송금 작업입니다. 사용자 A가 사용자 B에게 10,000원을 송금할 경우를 생각해 봅시다. 현실에서는 물리적인 지폐가 이동하면 끝나지만, 데이터베이스의 세계에서는 각각의 상태를 변경해줘야 합니다. 10,000이라는 데이터가 실제로 이동하는게 아니라, 사용자 A의 데이터와 사용자 B의 데이터 상태가 각각 바뀌는 것이죠. 따라서 두 번의 update 쿼리를 실행해야 합니다. 다음은 예시에 대한 트랜잭션의 작업 절차입니다.

트랜잭션 수행 과정

  1. 디스크(데이터베이스)에서 A의 은행계좌잔고 값을 메인 메모리로 읽어옵니다.
  2. 읽어온 A의 잔고 값에서 10,000을 감소시킵니다.
  3. 디스크(데이터베이스)에서 B의 은행계좌잔고 값을 메인 메모리로 읽어옵니다.
  4. 읽어온 B의 잔고 값을 10,000 증가시킵니다.
  5. A의 계좌 값을 디스크(데이터베이스)에 기록합니다.
  6. B의 계좌 값을 디스크(데이터베이스)에 기록합니다.

위 작업절차로 사용자 A와 B의 잔고정보가 송금내용에 맞게 데이터베이스에 저장되었습니다.

2. 트랜잭션의 성질

트랜잭션은 작업의 안전성과 데이터의 무결성을 유지시키기 위해 다음의 4가지 성질을 가지고 있습니다.

  • Atomicity(원자성)
  • Consistency(일관성)
  • Isolation(고립성)
  • Durability(지속성)

이 4가지 특성의 앞글자를 따 트랜잭션의 ACID 성질이라고 부릅니다.

Atomicity(원자성)

트랜잭션의 수행결과는 데이터베이스에 전부 반영되거나, 전부 반영되지 않아야 합니다.(Nothing or All)

트랜잭션의 처리절차

다시 위 예시로 돌아가서, A가 B에게 송금하는 과정에서 문제가 발생해 A의 잔고는 줄였는데 B의 잔고는 늘리지 않아졌다고 가정합시다. 이 상태로 데이터베이스에 저장하게 되면, A는 분명 돈을 송금했는데, B는 돈을 받지 못한 상황이 발생하게 됩니다. 이런 상황을 방지하기 위해 트랜잭션은 모든 작업이 완벽히 수행되었는지 확인하고, 데이터베이스에 저장하여야 합니다. DBMS는 트랜잭션의 원자성을 보장하기 위해, 트랜잭션 작업 중 문제가 발생하면 ROLLBACK을 통해 최근 savepoint로 돌아가 다시 작업을 수행하도록 합니다. 이를 Recovery라고 합니다.

Consistency(일관성)

트랜잭션 수행 후 데이터 모델의 모든 제약조건을 만족해야 합니다.

트랜잭션 전후의 데이터베이스 상태는 일관성이 보장되는 서로 다른 상태가 되어야 합니다. 쉽게 말해, 해당 컬럼이나 레코드에 명시된 제약조건, 예를 들어 잔고의 데이터 타입은 정수형이어야 한다, 혹은 잔고의 값은 null 이면 안된다, 혹은 잔고가 늘어나면 신용평가 값도 변경되어야 한다 등 기본 키와 외래키, 속성에 대한 제약조건과 같은 명시적 무결성 제약 조건을 만족시켜야 합니다. 또한 송금 전 두 사람의 잔고 합은 송금 후와 동일해야 하는 비명시적 일관성 조건도 지켜져야 합니다.

Isolation(고립성)

트랜잭션 수행 시 다른 트랜잭션이 영향을 미치지 않아야 합니다.

A의 잔고가 100,000원이 있다고 가정합시다. A는 B에게 10,000원을 송금하는 동시에 100,000원을 인출하려 한다고 가정합시다. 이 경우 송금 트랜잭션과 인출 트랜잭션이 수행됩니다. 앞서 설명한 것처럼 A가 B에게 송금할 경우 A의 잔고에서 10,000원이 차감됩니다. 따라서 A의 잔고는 90,000원이 되어 100,000원을 인출하지 못해야 정상입니다. 그런데 만약 송금 트랜잭션 중간에 인출 트랜잭션이 끼어들게 되면 어떻게 될까요? 송금 트랜잭션이 디스크에서 100,000원을 메모리에 적재해 90,000원으로 값을 변경해 다시 DB에 저장하기 전에, 인출 트랜잭션이 A의 잔고에 접근해 작업을 마무리한다고 가정해봅시다. 이렇게 되면 송금 트랜잭션이 뒤늦게 DB에 90,000원을 저장하기 때문에 송금과 인출을 마치고도 A의 잔고에는 90,000원이 들어있을 겁니다. 트랜잭션은 이러한 문제를 해결하기 위해 shared_lockexclusive_lock을 사용합니다.

  • shared_lock(read lock): 데이터의 읽기만 가능한 lock 입니다. 데이터를 여러 트랜잭션이 접근할 수 있지만, 읽기만 가능하며 데이터를 변경할 수는 없습니다.
  • exclusive_lock(write lock): 데이터를 변경할 때 사용하는 lock 입니다. lock이 해제될 때까지 다른 트랜잭션이 데이터에 접근할 수 없습니다.

Durability(영속성)

트랜잭션의 성공결과는 장애 발생 후에도 변함없이 보관되어야 합니다.

트랜잭션이 작업을 정상적으로 완료한 경우에는 디스크(데이터베이스)에 확실히 기록하여야 하며, 부분적으로 수행된 경우에는 작업을 취소하여야 합니다. 즉, 정상적으로 완료 혹은 부분완료된 데이터는 DBMS가 책임지고 데이터베이스에 기록하는 성질을 트랜잭션의 Durability(영속성)이라고 합니다.