트랜잭션과 ACID

Alan·2023년 4월 29일
0

데이터 시스템의 문제

  • DB의 소프트웨어나 하드웨어는 언제라도 실패할 수 있음

  • 애플리케이션은 언제라도 죽을 수 있음

  • 네트워크가 끊기면 애플리케이션과 DB의 연결이 끊기거나 DB 노드 사이의 통신이 끊길 수 있음

  • 여러 클라이언트가 동시에 DB를 사용해서 다른 클라이언트가 쓴 내용을 덮어쓸 수 있음

  • 클라이언트가 부분적으로만 갱신되서 비정상적인 데이터를 읽을 수 있음

  • 클라이언트 사이의 경쟁 조건은 예측하지 못한 버그를 유발할 수 있음

트랜잭션

  • 위 문제들을 단순화하는 메커니즘의 형태

  • 애플리케이션에서 몇 개의 읽기와 쓰기를 하나의 논리적 단위로 묶는 방법

  • 트랜잭션은 전체가 성공(커밋)하거나 실패(롤백)로 처리

  • 트랜잭션의 장점은 애플리케이션에서 오류 처리를 하기가 훨씬 단순해진다는 점임

  • 트랜잭션을 사용함으로써 애플리케이션은 어느 정도의 잠재적인 오류 시나리오와 동시성 문제를 무시할 수 있음

트랜잭션의 트레이드 오프

  • 거의 모든 관계형 데이터베이스와 일부 비관계형 데이터베이스에서 트랜잭션을 지원

  • NoSQL 데이터베이스는 새로운 데이터 모델, 복제와 파티셔닝 등 관계형 데이터베이스의 개선을 목표로 발전

  • 하지만, 이러한 발전에있어 트랜잭션은 고려되지 않거나, 약한 보장을 의미하는 단어로 축소됨

  • 즉, 높은 성능과 고가용성을 유지하기 위해서는 트랜잭션이 포기되어야 한다는 트레이드오프가 발생

ACID란

  • 트랜잭션이 제공하는 안정성 보장

  • 원자성(Atomicity), 일관성(Consistency), 격리성(Isolation), 지속성(Durability)을 의미하는 약어

  • 하지만, 현실의 데이터베이스에서 ACID의 구현은 제각각이며, 실제로 ACID를 준수한다고 했을 때, 어떤 것을 기대할 수 있는지 분명하지 않게 됨

  • 원자성(Atomicity)

    • 일반적으로 원자적이란 더 작은 부분으로 쪼갤 수 없는 것
    • ACID에서의 원자성이란 클라이언트가 쓰기 작업을 몇 개 실행하고, 그 중 일부만 처리된 후 결함이 생겼을 때를 설명
    • 즉, 여러 쓰기작업은 하나의 원자적인 트랜잭션으로 묶여 있어, 결함이 발생하면 커밋되지 못하고 어보트되며 롤백된다는 것을 의미
    • 클라이언트 입장에선 하나의 쓰기라도 실패한다면, 모든 쓰기가 실패했음을 알 수 있기 때문에 안전하게 재시도할 수 있음을 의미
  • 일관성(Consistency)

    • 일관성이란 항상 진실이어야 하는 데이터에 관한 어떤 선언이 있다는 것
    • 예를 들면, 회계 시스템에서 대변과 차변은 항상 맞아 떨어져야 함
    • 조금만 생각해본다면, 이는 데이터베이스의 개념이 아닌, 애플리케이션에서 처리해야할 속성임
    • 실제로 데이터베이스는 외래 키 제약 조건이나 유일성 제약 조건 등을 사용할 수 있지만, 일반적인 관점에선 애플리케이션에서 데이터가 유효한지 아닌지를 정의하며, 데이터베이스는 이를 저장함
    • 실제로 ACID에서 C는 실제로 약어를 만들기 위해 껴맞춘 것이라고 함 ...
  • 격리성(Isolation)

    • 동시에 여러 클라이언트에서 데이터베이스에 접속해 동일한 레코드에 접근하면 동시성 문제(경쟁 조건)에 맞닥뜨리게 됨
    • 격리성은 동시에 실행되는 트랜잭션은 서로 격리된다는 것을 의미 고전적인 의미에서는 직렬성이라는 용어를 사용
    • 데이터베이스는 실제로는 여러 트랜잭션이 동시에 실행됐더라도 트랜잭션이 커밋됐을 때의 결과가 트랜잭션이 순차적으로(하나씩 차례대로) 실행됐을 때의 결과와 동일하도록 보장
    • 하지만 최근에는 직렬성 격리의 경우, 성능 손해를 동반하기 때문에 거의 사용되지 않음(오라클 11g와 같은 대중적인 데이터베이스도 마찬가지로)
    • 오라클은 직렬성보다 보장이 약한 스냅숏 격리를 구현(추후에 알아봄)
  • 지속성(Durability)

    • 트랜잭션이 성공적으로 커밋됐다면 하드웨어 결함이 발생하거나 데이터베이스가 죽더라도 트랜잭션에서 기록한 모든 데이터는 손실되지 않는다는 보장
    • 복제 기능이 있는 데이터베이스에서 지속성은 데이터가 성공적으로 다른 노드에 복사됐다는 것을 의미
    • 하지만, 완벽한 지속성이라는 것은 존재할 수 없음(모든 디스크와 백업본이 동시에 파괴된다면 당연히 데이터베이스 단에서 할 수 있는 것은 아무 것도 없음)

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

  • ACID에서 원자성과 격리성은 클라이언트가 한 트랜잭션 내에서 여러 번의 쓰기를 하면 데이터베이스가 어떻게 해야 하는지를 의미

  • 원자성

    • 데이터베이스는 전부 반영되거나 아무것도 반영되지 않는 것을 보장함으로써 부분 실패를 걱정하지 않도록 함
  • 격리성

    • 동시에 실행되는 트랜잭션들은 서로를 방해하지 않음
  • 이러한 정의는 한 번에 여러 객체(로우, 문서, 레코드)를 변경할 수 있음을 가정(다중 객체 트랜잭션은 흔히 데이터의 여러 조각이 동기화된 상태로 유지되어야 할 때 필요)

  • 다중 객체 트랜잭션은 어떤 읽기와 쓰기 연산이 동일한 트랜잭션에 속하는지 알아낼 수단이 필요(일반적인 관계형 데이터베이스에서 BEGIN TRANSACTION 문과 COMMIT 문 사이의 모든 것은 같은 트랜잭션에 속하는 것으로 여김)

  • 반면 비관계형 데이터베이스에서는 이런 식으로 연산을 묶는 방법이 없는 경우가 많음

  • 원자성과 격리성은 단일 객체 쓰기에서도 적용됨

    • 예를 들어 20KB JSON 문서를 데이터베이스에 쓴다고 가정
    • 첫 10KB를 보내고 네트워크 연결이 끊긴다면 어떻게 처리?
    • 기존 값을 덮어쓰는 도중에 전원이 나가면 어떻게 처리?
    • 문서를 쓰고 있을 때 다른 클라이언트에서 문서를 읽으면 어떻게 처리?
  • 보편적으로 원자성은 장애 복구용 로그를 써서 구현되며, 격리성은 각 객체에 잠금을 사용해 동시에 한 스레드만 객체에 접근하도록 구현됨

  • 하지만 이러한 단일 객체 연산은 엄밀히 말하자면 ACID로 간주되지 않음

  • 트랜잭션은 보통 다중 객체에 대한 다중 연산을 하나의 실행단위로 묶는 메커니즘을 의미

분산 데이터베이스에서의 다중 객체 트랜잭션

  • 많은 분산 데이터스토어는 다중 객체 트랜잭션 지원을 포기(불가능 한 것은 아님, 9장에서 분산 트랜잭션에 대해 다룰 예정)

  • 여러 파티션에 걸쳐서 구현하기가 어렵고, 매우 높은 가용성과 성능이 필요한 곳에서는 방해가 되는 시나리오도 있기 때문

  • key-value 데이터 모델과 단일 객체 연산만을 사용해서 애플리케이션을 구현할 수도 있지만, 많은 경우에 있어서 여러 객체에 실행되는 쓰기 작업은 코디네이션돼야 함

    • 관계형 모델의 외래키와 같이 그래프형 모델의 정점은 간선이 존재. 즉, 서로 참조되는 여러 레코드를 삽입할 때 참조 키를 올바른 최신 정보를 반영해야 함

    • 조인 기능이 없는 문서 데이터 모델은 비정규화를 장려하기도 함. 이처럼 비정규화된 정보를 갱신할 때는 한 번에 여러 문서를 갱신해야 함을 의미

    • 보조 색인이 있는 데이터베이스는 값을 변경할 때마다 색인도 갱신되어야 함

  • 위 예시는 모두 트랜잭션이 없더라도 구현할 수는 있음. 하지만, 오류처리가 훨씬 복잡해지고 동시성 문제도 발생될 수 있음

  • 트랜잭션의 핵심기능은 오류가 생기면 어보트되고 안전하게 재시도할 수 있다는 것. ACID의 철학이며, 데이터베이스는 원자성, 격리성, 지속성 보장을 위반할 위험이 있으면 트랜잭션을 완전히 폐기함으로서 이를 달성함

  • 하지만, 어보트된 트랜잭션을 재시도한다는 것이 완벽한 것은 아님

    • 트랜잭션이 성공했지만, 성공을 알리는 도중 네트워크가 끊기게 되면 클라이언트는 재시도할 것이고 이는 트랜잭션이 두 번 실행될 수 있음을 의미함. 애플리케이션에 추가적인 중복 제거 메커니즘이 필요한 이유

    • 오류가 과부하때문이라면 재시도는 문제를 개선하는 것이 아니라 악화시킬 수 있음

    • 오류가 일시적인 오류가 아닌 영구적인 오류라면, 재시도는 아무런 의미가 없음

0개의 댓글