Programming Language

[DB] 트랜잭션 Transaction

wakelight23 2024. 12. 12. 21:15

트랜잭션(Transaction)이란?

● 사전적 의미 : 거래

● 컴퓨터 과학 분야에서는 "더 이상 분할이 불가능한 업무처리의 단위"

한꺼번에 수행되어야 할 일련의 연산모음을 의미함

 

DB의 특성과 또 하나의 특성

● 효과적인 데이터 인출이 가능
백업/복구에 용이하다
데이터 관리의 중앙화
민감한 데이터 보안

● 트랜잭션의 성질 === ACID 

 

ACID란?

● Atomicity(원자성) :

  - 데이터베이스의 모든 트랜잭션은 원자성을 보장. (은행에서 돈을 보내던지 안 보내던지)

  - 애매한 상태는 존재하지 않음 (보내야하나... 말아야하나... 상태는 존재하지 않음)

  - 예를 들어서 B가 A의 계좌에 100만원을 입금 할 수 없다면 B의 계좌에서도 100만원을 빼지 않는다!

  - 즉, '다 되거나', '안 되거나'

 

● Consistency(일관성) :
  - 데이터베이스에서는 트랜잭션이 완료되면 데이터의 일관성이 보장

  - 무결성 제약을 깨뜨리는 트랜잭션은 실행되지 않음

더보기

무결성 제약(Integrity Constraint)

- 데이터베이스의 정확성과 일관성을 보장하기 위해 설정되는 조건

- 데이터의 저장, 삭제, 수정 등을 제한하거나 조절하는 역할

  - IF) 게시글에 댓글을 달려고할 때, 댓글을 달려는 게시글의 ID가 없다면?

 

Isolation(격리성) :
  - 트랜잭션이 일단 수행이 되면 다른 트랜잭션으로부터 영향을 받지 않고 수행

  - A가 B에게 100만원을 보내는 트랜잭션이 완료되기 전까지는 다른 트랜잭션 작업은 A의 계좌에서
   100만원이 인출된 잔액 볼 수 없다. => 격리성이 보장되고 있다는 의미이다.

 

Durability(영속성) :
  - 트랜잭션이 성공적으로 수행되면 그 결과는 영원히 데이터베이스에 반영

 

Isolation(격리성), 트랜잭션의 격리 수준

● Isolation 트랜잭션이 독립적으로 실행될 수 있도록 보장하는 특성

 

● Q. 격리성에도 단계별로 수준이 있을까?

  - 트랜잭션이 완벽하게 격리된 상태에서만 실행된다면 성능에 문제가 생길 수 있다.

  - 이 때 등장하는 것이 격리 수준(Isolation Level)라는 특성이 존재

  - 예를 들어서, 쇼핑 앱으로 무언가를 살 때 재고가 1개남은 물품을 사려고 할 때,
    A가 장바구니에 추가하고 결제하는 트랜잭션을 진행하고 있을 때 B도 이 1개남은 물품을
    장바구니에 추가하고 결제하려고 한다. 이런 상황에서 Isolation Level이 필요한 것.

 

  ○ Serializable (가장 높은 Isolation Level)

    1) 하나의 트랜잭션이 완료될 때까지 다른 트랜잭션은 해당 상품 재고에 접근할 수 없음

    2) A가 결제를 완료할 때까지 고객 B는 재고를 확인하거나 구매 불가

    3) 일관성은 완벽하게 유지되지만 B는 기다려야하며 성능도 느려짐

    4) 쇼핑 앱에 이용자 수가 많다면 성능 문제 발생

 

 ○ Repeatable Read (MySQL 기본 설정)

    - 한 트랜잭션 내에서 같은 데이터를 반복해서 읽어도 동일한 결과를 보장

    1) 이 단계에서는 A와 B가 동시에 같은 상품의 재고를 확인하고 구매 시도 가능

    2) 단, 실제로 재고를 감소시키는 업데이트는 한 명만 가능하다... 동시에 같은 데이터 수정하려 할 때,
        한 트랜잭션만이 가능하기 때문

    3) A가 먼저 결제를 완료하면, B의 결제는 실패 or 잠금 대기 상태

    4) 데이터 일관성은 유지, B는 결제 실패 or 결제 지연

    5) 성능은 Serializable보다는 좋지만 동시 접근에 대한 충돌 처리가 필요

 

 ○ Read Committed (MySQL을 제외한 다른 DB들의 기본 설정)

    - 한 트랜잭션 내에서 커밋 완료된 데이터만 읽을 수 있는 것을 보장

    1) 이 단계에서는 B가 A의 트랜잭션이 커밋된 후에만 변경된 데이터를 읽기 가능

    2) B는 자신의 트랜잭션 중 언제든 A의 결제로 인해 변경된 재고 볼 수 있음

    3) Dirty Read는 방지되지만 Non-Repeatable Read 문제가 발생할 수 있음

    4) 각 쿼리마다 가장 최근에 커밋된 데이터를 읽기 때문에 성능과 일관성은 충족

더보기

Non-Reapeatable Read?

- 트랜잭션 내에서 같은 데이터를 여러 번 읽을 때, 그 사이에 다른 트랜잭션이 해당 데이터를 수정하거나
  삭제하여 이전에 읽은 데이터와 다른 결과를 얻게 되는 현상.

 

- 상품 구매 상황에서의 예

  1. 고객 A의 트랜잭션 : 
     1) 고객 A가 상품의 재고 확인 → 재고는 1개

     2) 고객 A가 상품을 장바구니에 추가하고 결제 과정 시작

     3) 결제 직전, 고객 A가 재고 다시 확인

  2. 고객 B의 트랜잭션 (고객 A의 1-2, 1-3 사이에 발생)

     1) 고객 B가 상품을 구매하고 결제를 완료

     2) 재고가 0으로 업데이트

  3. 고객 A의 트랜잭션 continue...

     1) 이때 재고가 0개로 변경

 

=> 고객 A가 잠깐 재고를 확인하는 사이 고객 B가 상품을 구매해 버렸다!

 ○ Read Uncommitted (가장 낮은 Isolation Level)

  - 한 트랜잭션 내에서 커밋 완료되지 않은 데이터도 읽을 수 있다.

   1) 이 단계에서는 A가 B의 결제가 끝나기 전에 변경된 재고 확인이 가능

   2) A가 아직 결제를 완료하지 않았는데도 B는 변경된 재고 상태를 볼 수 있다.

     (쇼핑 사이트에서 실시간으로 물품이 줄어드는 것을 본 적이 있던 것 같다...)

   3) 단, Dirty Read 문제가 발생할 수 있고, 일관성에 위배됨. 하지만 성능은 좋았죠?

더보기

Dirty Read?

- 트랜잭션이 커밋되기 전 커밋되지 않은 변경사항을 읽는 것.

 

상황에 따라 Isolation Level을 적절하게 사용

● 데이터 일관성이 매우 중요하다면 → Serializable 또는 Repeatable Read

● 성능이 더 중요하고 약간의 일관성 저하가 허용된다면 → Read Committed

성능이 절대적으로 중요하고 데이터 일관성 크게 중요하지 않다면  Read Uncommitted

  - Read Uncommitted는 실제 운영 환경에선 거의 사용하지 않음

     └ 성능이 아무리 중요한들 데이터가 더 중요하지!