728x90
반응형

트랜잭션 격리 수준의 종류


레벨1 : READ UNCOMMITTED (커밋되지 않은 읽기)

  • 설명: 트랜잭션이 커밋되지 않은 변경 사항을 읽을 수 있습니다.
    • 사용자 A가 계좌에 1000원을 입금하고, 아직 커밋하지 않은 상태에서 사용자 B가 그 계좌의 잔액을 조회합니다. 사용자 B는 A의 입금이 반영된 1000원이 추가된 잔액을 보게 되지만, A가 트랜잭션을 롤백하면 B의 정보는 잘못된 것이 됩니다.
  • 장점: 성능이 가장 좋음
  • 단점: 데이터 무결성 보장이 어렵습니다.

레벨2 : READ COMMITTED (커밋 읽기)

  • 설명: 트랜잭션은 커밋된 데이터만 읽을 수 있습니다.
  • 예시:
    • 사용자 A가 1000원을 입금한 후, 이를 커밋합니다. 그 후 사용자 B가 잔액을 조회하면, B는 1000원이 추가된 올바른 잔액을 볼 수 있습니다. 그러나 B가 같은 쿼리를 두 번 실행할 경우, A가 중간에 다른 금액을 입금하면 B는 두 번의 쿼리 결과가 다를 수 있습니다(비 반복 읽기).
  • 장점: 데이터의 일관성이 어느 정도 유지됨
  • 단점: 트랜잭션 간의 충돌이 발생할 수 있음

레벨 3 : REPEATABLE READ (반복 가능한 읽기)

  • 설명: 트랜잭션 내에서 동일한 쿼리를 실행할 때 항상 같은 결과를 반환합니다.
  • 예시:
    • 사용자 A가 1000원을 입금한 후 커밋합니다. 사용자 B가 잔액을 조회하고, B가 그 결과를 기반으로 다른 트랜잭션을 시작합니다. 이 때 A가 중간에 다른 금액을 입금해도, B는 첫 번째 쿼리 실행 시의 잔액을 계속 볼 수 있습니다. 그러나 B가 새로 추가된 잔액이 반영된 결과를 쿼리하려 할 때, A가 추가한 항목 때문에 팬텀 읽기가 발생할 수 있습니다.
  • 장점: 데이터의 일관성이 높음
  • 단점: 성능 저하가 발생할 수 있음

레벨 4 : SERIALIZABLE (직렬화 가능)

  • 설명: 가장 높은 수준의 격리로, 트랜잭션이 직렬로 실행되는 것처럼 보이게 합니다.
  • 예시:
    • 사용자 A가 1000원을 입금하고, 사용자 B가 동시에 잔액을 조회하려 할 때, B는 A의 트랜잭션이 완료될 때까지 기다려야 합니다. 이렇게 함으로써 A가 커밋한 후에만 B가 잔액을 조회하게 되므로 모든 읽기 작업이 일관되게 진행됩니다. 하지만 이러한 대기 상태로 인해 성능은 저하됩니다.
  • 장점: 데이터 무결성이 가장 보장됨
  • 단점: 성능이 크게 저하될 수 있음

위의 각 트랙잭션 격리수준의 특징과 장단점을 고려해서 성능과 데이터 무결성 간의 균형을 격리수준을 선택합니다.

 

주문 동시 처리에서 발생할 수 있는 문제

  1. 재고 충돌 (Stock Conflict)
    • 여러 사용자가 동시에 같은 상품을 주문할 경우, 재고 수량이 줄어들면서 재고가 부족해질 수 있습니다. 예를 들어, A와 B가 동시에 1개씩 주문하는 경우, 재고가 1개뿐이라면 두 주문 중 하나만 처리될 수 있습니다.
  2. 더러운 읽기 (Dirty Read)
    • 한 트랜잭션이 다른 트랜잭션의 커밋되지 않은 변경 사항을 읽는 경우입니다. 예를 들어, 주문 처리 중에 다른 사용자가 주문을 변경하고 이를 조회한 경우, 올바르지 않은 정보를 기반으로 결정을 내릴 수 있습니다.
  3. 비 반복 읽기 (Non-Repeatable Read)
    • 트랜잭션이 실행되는 동안 같은 쿼리를 여러 번 실행했을 때 결과가 다르게 나타나는 문제입니다. 예를 들어, A가 주문을 생성하고, B가 중간에 그 주문을 변경하면 A는 그 변경된 결과를 반영한 정보를 보게 됩니다.
  4. 팬텀 읽기 (Phantom Read)
    • 트랜잭션이 실행되는 동안 새로운 데이터가 삽입되는 경우 발생합니다. 예를 들어, A가 특정 조건에 맞는 상품 목록을 조회하는 동안 B가 새로운 상품을 추가하면, A는 B가 추가한 상품을 볼 수 있습니다.

해결 방법

  1. 격리 수준 조정
    • 트랜잭션의 격리 수준을 높여서 동시성 문제를 줄일 수 있습니다. 예를 들어, SERIALIZABLE 격리 수준을 사용하면 모든 트랜잭션이 직렬로 처리되므로 충돌을 방지할 수 있습니다. 그러나 성능이 저하될 수 있다는 점을 고려해야 합니다.
  2. 낙관적 및 비관적 잠금
    • 낙관적 잠금: 트랜잭션이 완료될 때까지 변경 사항을 확인하고, 충돌이 발생할 경우 롤백합니다. 주로 데이터 충돌이 드물 때 유용합니다.
    • 비관적 잠금: 트랜잭션이 시작될 때 데이터에 잠금을 걸어 다른 트랜잭션이 접근하지 못하도록 합니다. 일반적으로 동시성이 높을 때 사용됩니다.
  3. 재고 체크 및 주문 처리 로직
    • 주문을 처리하기 전에 재고를 확인하고, 재고가 충분한 경우에만 주문을 진행하도록 로직을 구현합니다. 이때, 재고 수량을 업데이트하는 트랜잭션을 안전하게 처리하는 것이 중요합니다.
  4. 큐 기반 처리
    • 주문 요청을 큐에 쌓아 순차적으로 처리하는 방법입니다. 이렇게 하면 동시에 들어온 주문을 순차적으로 처리할 수 있어 충돌을 줄일 수 있습니다.
  5. 원자적 처리 (Atomic Processing)
    • 트랜잭션을 원자적으로 처리하여 모든 단계가 성공하거나 실패하도록 합니다. 이 방법은 데이터의 일관성을 높이고, 중간 상태에서 발생할 수 있는 문제를 방지합니다.
728x90
반응형

+ Recent posts