Study

[데이터 중심 어플리케이션 설계] 트랜잭션

에디개발자 2022. 5. 12. 01:00
반응형

이번 글은 데이터 중심 어플리케이션 설계 도서의 트랜잭션의 내용을 정리하였습니다.

들어가기 전에

데이터 시스템의 여러 가지 문제

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

 

트랜잭션이란?

  • 위와 같은 문제를 단순화하는 매커니즘으로 채택
  • 애플리케이션에서 몇 개의 읽기와 쓰기를 하나의 논리적 단위로 묶는 방법
  • 한 트랜잭션 내의 모든 읽기와 쓰기는 한 연산으로 실행
  • 데이터베이스에 접속하는 애플리케이션에서 프로그래밍 모델을 단순화
  • 안정성 보장 ( safety guarantee )

 

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

  • 비관계형 데이터베이스 중 다수는 트랜잭션을 완전히 포기하거나 과거에 인식되었던 것보다 훨씬 약한 보장을 의미하는 단어로 재정의
  • 대규모 시스템이라도 높은 성능과 고가용성을 유지하려면 트랜잭션을 포기해야 한다는 의미로 재해석

 

ACID의 의미

원자성 ( Atomicity )

  • 더 작은 부분으로 쪼갤 수 없는 무언가를 가리킴
  • 다중 스레드 프로그래밍에서 한 스레드가 원자적 연산을 실행한다면 다른 스레드에서 절반만 완료된 연산을 관찰 불가
  • 연산을 실행하기 전이나 후의 상태만 읽을 수 있음
  • 여러 쓰기 작업이 하나의 원자적인 트랜잭션으로 묶여 있어 처리 도중 결함이 있다면 모든 쓰기는 어보트 처리
  • 어떤 것도 변경하지 않았음을 알 수 있으므로 안전하게 재시도 가능

일관성 ( Consistency )

  • 데이터에 관한 불변식 ( invariant )
  • 트랜잭션에서 실행된 모든 쓰기가 유효성을 보존한다면 불변식이 항상 만족된다는 확신
  • 애플리케이션의 불변식 개념에 의존적이고 트랜잭션을 올바르게 정의하는 것은 애플리케이션의 책임
  • 데이터베이스는 불변식을 위반하는 잘못된 데이터를 쓰지 못하도록 막을 수 없음
  • 일관성은 애플리케이션의 속성, 원자성, 격리성, 지속성은 데이터베이스의 속성

격리성 ( Isolation )

  • 클라이언트들이 동일한 데이터베이스의 레코드에 접근하여 동시성 문제(경쟁 조건)를 격리
  • 데이터베이스는 여러 트랜잭션이 동시에 실행됐더라도 커밋됐을 때의 결과가 순차적으로 실행했을 때의 결과와 동일하도록 보장
  • 성능 손해로 현실에서는 거의 사용하지 않음
  • 스냅숏 격리 사용

지속성 ( Durability )

  • 데이터베이스의 목적은 데이터를 읽어버릴 염려가 없는 안전한 저장소를 제공
  • 트랜잭션이 성공적으로 커밋됐다면 하드웨어 결함이 발생해도 기록된 데이터는 손실되지 않도록 보장
  • 일반적으로 하드디스크나 SSD 같은 비휘발성 저장소에 기록

 

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

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

원자성
- 부분 실패에 대한 걱정이 없음
- 한 트랜잭션에서 쓰기 후 오류가 발생하는 경우 Abort 처리

격리성
- 동시에 실행되는 트랜잭션들은 서로 방해할 수 없음
  • 다중 객체 트랜잭션은 데이터의 여러 조각이 동기화된 상태로 유지되어야 할때 필요
  • 다중 객체 트랜잭션은 읽기 연산과 쓰기 연산이 동일한 트랜잭션에 속하는지 알아낼 수단이 있어야 함 

관계형 데이터베이스

  • TCP 연결 기반으로 작동
  • 연결 내에서 BEGIN TRANSACTION문과 COMMIT문 사이의 모든 것은 같은 트랜잭션에 속함

비관계형 데이터베이스

  • 연산을 묶는 방법이 없는 경우가 많음
  • 다중 객체 API가 있더라도 어떤 키에 대한 연산은 성공하고 나머지 키에 대한 연산은 실패해서 데이터베이스가 부분적으로 갱신된 상태가 있을 수 있음

 

단일 객체 쓰기

  • 여러 클라이언트에서 동시에 같은 객체에 쓰려고 할 때 갱신 손실을 방지하므로 유용
  • read-modify-write, compare-and-set 로 예를 들 수 있음

 

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

  • 관계형 데이터 모델에서 테이블의 레코드는 종종 다른 테이블의 레코드를 참조하는 외래 키를 갖는데, 이 때 참조가 유효한 상태로 유지되도록 보장 ( 만약 보장되지 않는다면 데이터가 비정상 적으로 생성될 우려가 있음 )
  • 비정규화된 정보를 갱신할 때는 한 번에 여러 문서를 갱신이 필요한데 트랜잭션은 이런 상황에서 비정규화된 데이터가 동기화가 깨지는 것을 방지하는 데 유용
  • 보조 색인이 있는 데이터베이스에서 값을 변경할 때마다 색인도 갱신 필요할 경우 트랜잭션 관점에서 색인은 서로 다른 데이터베이스 객체이기 때문에 트랜잭션의 격리성이 없으면 문제가 발생
- 원자성이 없으면 오류 처리가 복잡해짐
- 격리성이 없으면 동시성에 문제 발생

 

오류와 Abort 처리

트랜잭션의 핵심 기능은 오류가 생기면 Abort 되고 안전하게 재시도 할 수 있음 but 완벽하지는 않음

  • 트랜잭션이 성공했지만 서버가 클라이언트에게 성공을 알리기 전 네트워크 장애가 발생한다면 클라이언트는 재시도를 요청할 것이고 이는 곧 중복처리로 이어짐
  • 오류가 과부하 때문이라면 트랜잭션 재시도는 문제 개선이 아닌 악화로 발전, 과부화 관련 오류가 발생한다면 재시도 횟수를 제한, 백오프, 재시도가 아닌 다른 처리 등으로 풀어야 함
  • 일시적인 오류만 재시도할 가치가 있고 영구적인 오류에 대해서는 재시도는 피해야함
  • 트랜잭션이 데이터베이스 외부에도 부수 효과가 있다면 트랜잭션이 어보트될 때도 부수 효과에 대한 처리가 필요

 

완화된 격리 수준

  • N개의 트랜잭션이 동일한 데이터에 접근하지 않으면 안전하게 병렬 실행될 수 있음
  • N개의 트랜잭션이 동일한 데이터에 접근한다면 동시성 문제가 발생할 수 있음
동시성 문제
- 다른 트랜잭션에서 동시에 변경한 데이터를 읽거나 변경하려할 때 발생 
  • 이론적으론 트랜잭션 격리를 통해 애플리케이션에서 동시성 문제를 감출 수 있음
  • 직렬성 격리를 통해 여러 트랜잭션들이 직렬적으로 실행되도록 함 but 직렬성 격리는 성능 비용이 존재하고 일부 데이터베이스는 그 비용을 지불하지 않음
  • 비용을 지불하지 않는 데이터베이스는 완화된 격리 수준을 제공하고 이런 격리성 수준은 이해하기 어려울 뿐더러 버그를 발생함

 

내용이 길어 다음 글에서 트랜잭션의 완화된 격리수준에 대해서 정리해보겠습니다.

반응형