트랜잭션은 어떻게 데이터 시스템의 문제들을 해결할까? (feat. ACID)

정훈희·2024년 6월 23일
0
post-thumbnail

0️⃣ 배경

데이터 중심 애플리케이션 설계 7장(트랜잭션)을 읽던 중 데이터 시스템에서 발생할 수 있는 문제들과, 안전성을 보장하는 트랜잭션의 성질인 ACID에 대한 내용을 접했다.

나는 막연히 트랜잭션은 ACID의 성질을 통해 안전성을 보장한다고 알고 있었지만, ACID의 각 성질이 어떻게 안전성을 보장하는 것인지 깊게 생각하지 못했다.

그래서, 이번에는 데이터 시스템에서 생길 수 있는 문제들을 살펴보고, 트랜잭션은 어떻게 문제들을 해결하는지 ACID의 각 성질을 살펴보며 알아보겠다.

1️⃣ 데이터 시스템에서 발생할 수 있는 문제들

⚠️ 문제 및 해결 방법 - 트랜잭션

데이터 시스템은 아래와 같은 문제들이 발생할 수 있다.

  • 작업 중 네트워크 연결이 끊기거나 프로세스가 죽는 등의 이유로 작업이 불완전하게 끝날 수 있다.
  • 정해진 비즈니스 규칙에서 벗어나는 유효하지 않은 데이터가 저장될 수 있다.
  • 여러 클라이언트가 동시에 쓰기 작업을 실행해서 다른 클라이언트가 쓴 내용을 덮어쓸 수 있다.

시스템이 신뢰성을 지니려면 이러한 문제들을 해결해야한다.

트랜잭션은 ACID라는 네 가지 성질을 통해 안전성을 보장하여 위 문제들을 해결한다.

✅ ACID 란?

ACID는 트랜잭션이 안전성을 보장할 수 있게해주는 4가지 성질이다.

4가지 성질은 다음과 같다.

  • 원자성(Atomicity)
  • 일관성(Consistency)
  • 격리성(Isolation)
  • 지속성(Durability)

ACID의 각 성질을 아래와 같은 과정으로 살펴보겠다.

  • 해당 성질이 필요한 문제 상황
  • 해당 성질의 정의
  • 해당 성질이 문제를 해결하는 방법

2️⃣ 원자성(Atomicity)

⚠️ 문제 상황

만약 트랜잭션에서 여러 변경 중 일부만 진행된 상태에서 네트워크 연결이 끊기거나 프로세스가 죽는 등의 문제로 트랜잭션이 중단되면 어떻게 될까?

여러 변경 중 어떤 변경은 효과가 있고, 어떤 변경은 그렇지 않은지 알기 어렵다. 또한 재시도를 하면서 중복되거나 잘못된 데이터가 만들어지기도 쉽다.

✅ 원자성이란?

원자성은 한 트랜잭션을 더 작은 부분으로 쪼갤 수 없는 원자처럼 취급하는 성질이다. 즉, 트랜잭션은 트랜잭션 내의 모든 작업이 전부 실행된 상태나 하나도 실행되지 않은 상태만 존재할 수 있다는 것이다.

🤝 문제 해결 방법

원자성은 트랜잭션 실행 중 문제가 발생할 경우 해당 트랜잭션에서 기록한 모든 내용을 취소하는 어보트(abort)를 진행하여 문제를 해결한다.

이를 통해 트랜잭션은 모든 작업이 실행되거나 아무 변경이 일어나지 않음을 보장하고, 트랜잭션이 어보트됐다면 애플리케이션에서 이 트랜잭션이 어떤 것도 변경하지 않았음을 알 수 있으므로 안전하게 재시도할 수 있다.

3️⃣ 일관성(Consistency)

⚠️ 문제 상황

계좌 송금 작업을 진행하기 위해서는 계좌에서 송금할 금액을 출금해야한다. 하지만, 계좌에 잔액이 충분하지 않은 상태에서 작업이 진행된다면 계좌의 잔액이 음수가 돼서 유효하지 않은 상태가 된다.

✅ 일관성이란?

일관성은 데이터베이스가 트랜잭션 실행 후에도 유효한 상태에 있어야 한다는 성질이다.

🤝 문제 해결 방법

이렇게 유효한 상태를 유지하기 위해서는 트랜잭션 진행 중에 유효하지 않은 데이터 변경이 일어날 때 트랜잭션을 어보트하면 된다.

하지만, 생각해보면 일관성을 보장하는 것이 데이터베이스만의 역할인지는 의문이다. 문자열인지 정수인지는 타입을 통해 유효성을 검증할 수 있지만, 음수인지, n글자인지 등 다양한 경우 애플리케이션에서 유효성을 검증해야하기 때문이다.

4️⃣ 격리성(Isolation)

⚠️ 문제 상황

위 그림을 보면, 사용자 1과 2가 거의 동시에 조회 및 변경 작업을 진행하고 있다. 하지만, 분명 두 사용자가 각각 카운터 증가 작업을 시행했음에도 카운터가 1만 증가하는 문제가 발생했다.

이처럼 동시에 여러 클라이언트에서 동일한 레코드에 접근하면 동시성 문제가 발생한다.

✅ 격리성이란?

격리성은 동시에 실행되는 트랜잭션은 격리된다는 것을 의미한다. 즉, 여러 트랜잭션이 동시에 실행됐더라도 결과가 트랜잭션이 순차적으로 실행됐을 때의 결과와 동일하도록 보장한다.

🤝 문제 해결 방법

격리성을 보장하기 위해 데이터베이스는 잠금 기능과 다양한 격리 수준을 제공한다. 격리 수준 중 Serializable이 트랜잭션이 순차적으로 실행되도록 보장하여 위 그림에서 발생하는 문제를 해결할 수 있다. 하지만 Serializable 격리 수준은 심각한 성능 손해를 동반하므로 거의 사용되지 않는다.

대신 스냅샷 격리라는 대안이 있다. 스냅샷 격리는 각 트랜잭션이 시작될 때의 스냅샷을 기반으로 작업을 수행한다. 그리고 트랜잭션이 커밋을 시도할 때 변경된 데이터가 중간에 다른 트랜잭션에 의해 변경되었는지(이런 현상을 Write Skew 라고 함) 확인할 수 있도록 한다. 자세한 내용은 아래 링크를 참고하자.

https://www.geeksforgeeks.org/what-is-snapshot-isolation/

5️⃣ 지속성(Durability)

⚠️ 문제 상황

여러 문제로 인해 데이터가 소실되는 문제가 발생할 수 있다.

✅ 지속성이란?

지속성은 트랜잭션이 성공적으로 커밋됐다면, 기록한 모든 데이터는 소실되지 않는다는 성질이다.

🤝 문제 해결 방법

지속성을 보장하기 위해서는 트랜잭션이 커밋되기 전에 쓰기나 복제가 완료되어야 한다. 복제, 백업도 지속성을 보장하는 하나의 방법이다.

하지만 그럼에도 불구하고 모든 HDD와 백업이 동시에 파괴된다면 어쩔 수 없기 때문에 완벽한 지속성은 존재할 수 없다.

6️⃣ 그렇다면 모든 데이터 시스템은 트랜잭션을 사용해야할까?

역시나 트랜잭션도 트레이드오프가 있다.

위에서 살펴본 것 처럼 트랜잭션은 안전성을 보장하여 여러 문제를 쉽게 해결할 수 있도록 도와준다. 하지만 트랜잭션이 실행하면서 잠금으로 인해 성능이 저하되고 데드락이 발생할 수 있다는 문제점도 존재한다.

위에서 살펴본 ACID를 통해 내 애플리케이션에 트랜잭션이 필요한지 깊게 생각해본 뒤 사용 여부를 결정해야한다.

profile
DB를 사랑하는 백엔드 개발자입니다. 열심히 공부하고 열심히 기록합니다.

0개의 댓글