대기열을 이용한 [콘서트 예약 서비스] - 동시성 문제

Kyungmin·2024년 10월 29일
0

Spring

목록 보기
29/39

✍🏻 콘서트 예약 서비스에서 발생할 수 있는 동시성 이슈들은 뭐가 있을까 ?

< 프로세스 >
유저 등록 → 대기열 진입( 대기열 토큰 발급 ) → 예약 → 콘서트 결제 or 잔액 충전 및 조회 → 완료

동시성 이슈?

여러 스레드가 동시에 공유 자원에 접근할 때 발생하는 문제이다. 이는 데이터의 일관성을 해칠 수 있다.

EX ) 은행 계좌에 두 사람이 동시에 돈을 인출 → 한 사람만 가능해야 하는데 동시 접근 시 둘 다 조회가 가능하게 되는 상황 발생 → 두 명다 인출 → 내 돈 GG..


🤔 발생할 수 있는 동시성 문제

1. 대기열 관리

  • 여러 사용자가 동시에 대기열에 진입하거나, 좌석이 예약이 취소되어 대기열에서 다음 순서로 넘어가는 경우에 대기열 순서 일관성을 지켜야한다.
  • 스케쥴러를 이용하여 다음순서에게 에약을 부여한 후, 그 다음 순서 사용자들이 일관성있게 앞으로 당겨지는가?

2. 포인트 결제 ( 잔액 차감 )

시나리오 1 )

  • 콘서트 예약을 할 때, “잔액 확인 → 결제” 가 이루어지는데 “잔액 확인 & 결제” 가 동시에 일어나는 경우

시나리오 2 )

  • 사용자 한명이 A 콘서트와 B 콘서트를 동시에 예약하는 경우? → 근데 이 경우가 가능한 일이 일어날까??
  • 공동 결제라는 특수한 경우를 제외하면 일어나지 않을 경우인 것 같다..

3. 동일한 좌석을 동시에 예약하는 경우

  • A 좌석을 n 명이 동시에 접근한다면?

4. 자리가 5개 남았을 때 6명 이상이 예약을 할 경우에는 “잔여 좌석 X” → 이 경우 옳바르게 예약 경쟁이 되는지에 대한 동시성 문제

  • 실제로 남은 좌석인 5개만 예약이 되어야하는데, 동시성문제로 6개가 예약이 될 수 있다.

✏️ 동시성 제어를 위해 어떤 방법을 사용할 수 있을까

  1. DB 락
  • 낙관적 락

    데이터 접근 시 락을 사용하지 않고, 업데이트 시점에 충돌을 감지하여 처리한다. 주로 엔티티에 @Version 필드를 추가하여 구현한다.

  1. 장점

    높은 성능: 락을 사용하지 않으므로 트랜잭션 간의 대기가 발생하지 않는다.
    교착 상태 방지: 락을 사용하지 않으므로 교착 상태 문제가 발생하지 않는다.

  2. 단점

    충돌 시 재시도 필요: 업데이트 시점에 충돌이 발생하면 재시도 로직이 필요하다.
    복잡한 오류 처리: 충돌 발생 시 예외 처리를 별도로 구현해야 하다.

  3. 구현 복잡도: 중간

    성능(시간): 높음
    효율성: 중간 (충돌 시 재시도 필요)
    한계: 충돌 발생 시 처리 복잡성 증가

  • 비관적 락

    데이터에 접근할 때 즉시 락을 걸어 다른 트랜잭션이 해당 데이터에 접근하거나 수정하지 못하도록 방지. 주로 SELECT ... FOR UPDATE 구문이나 JPA의 @Lock(LockModeType.PESSIMISTIC_WRITE)를 사용

  1. 장점

    데이터 일관성 보장: 충돌 가능성이 높은 상황에서 데이터의 일관성을 강력하게 보장할 수 있다.
    단순한 구현: JPA와 같은 ORM 프레임워크를 이용하면 쉽게 구현할 수 있다.

  2. 단점

    성능 저하: 락을 오래 유지할 경우 다른 트랜잭션이 대기하게 되어 시스템 전체의 성능이 저하될 수 있다.
    교착 상태(Deadlock) 위험: 여러 트랜잭션이 서로 락을 기다리며 교착 상태에 빠질 수 있다.

  3. 구현 복잡도: 중간

    성능(시간): 낮음 (락으로 인한 대기 발생)
    효율성: 높음 (데이터 일관성 보장)
    한계: 교착 상태 발생 가능성, 성능 저하

  • 트랜잭션 격리 수준 제어

  • Redis 분산 락

    Redis와 같은 인메모리 데이터 저장소를 이용하여 분산 환경에서 락을 관리. 예를 들어, Redisson 라이브러리를 사용하여 구현할 수 있다.

  1. 장점

    분산 환경 지원: 여러 인스턴스 간에 락을 공유할 수 있어 분산 시스템에 적합.
    높은 성능: 인메모리 저장소의 특성상 빠른 락 획득과 해제가 가능.

  2. 단점

    추가 인프라 필요: Redis 서버와 같은 추가적인 인프라가 필요.
    복잡한 설정: 락 관리와 관련된 설정이 복잡할 수 있다.

  3. 구현 복잡도: 높음

    성능(시간): 높음
    효율성: 높음
    한계: 추가 인프라 필요, 설정 복잡성

  • 메시지 큐 ( 카프카 )

    메시지 큐를 사용하여 예약 요청을 순차적으로 처리. 예를 들어, Kafka, RabbitMQ 등을 활용할 수 있다.

  1. 장점

    확장성: 메시지 큐를 통해 요청을 비동기적으로 처리하므로 높은 확장성을 가질 수 있다.
    신뢰성: 메시지 큐는 메시지의 내구성과 신뢰성을 보장.

  2. 단점

    복잡성: 메시지 큐 시스템의 도입과 관리가 복잡할 수 있다.
    지연 시간: 비동기 처리로 인해 즉각적인 응답이 어려울 수 있다.

  3. 구현 복잡도: 높음

    성능(시간): 중간 (메시지 큐의 처리 속도에 의존)
    효율성: 높음 (비동기 처리로 시스템 부하 분산)
    한계: 복잡한 시스템 아키텍처, 지연 시간


😨 그렇다면 나의 서비스에서는 어떤 방법을 사용해야하지?

1. 좌석 동시 예약

"한 좌석에 여러 사용자가 동시에 접근" 하는 경우, 비관적 락을 사용하여 동시성 문제를 해결하고자 했다. 그 이유는 서비스 자체가 좌석이 있는 콘서트 예약인데 그렇다면 매우 빈번하게 여러 유저들이 같은 좌석에 접근할 것이라고 생각했기 때문이다. 비관적 락을 사용해 동시 접근을 효과적으로 막을 수 있다고 생각했다.

@Lock(LockModeType.PESSIMISTIC_WRITE)
    @Query("SELECT s FROM Seat s WHERE s.seatNumber = :seatNumber AND s.concertEvent.id = :eventId")
    Optional<Seat> findSeatForUpdate(@Param("seatNumber") String seatNumber,
                                     @Param("eventId") Long eventId);

2. 포인트 차감 동시성 제어

  • ( 한 사용자가 여러 콘서트를 동시에 예약할 경우 포인트 차감이 잘 이루어지는지? )

한 사용자가 여러 좌석을 예약하는 경우는 좌석 예약과 다르게 빈번하게 일어나는 경우가 아니다. 때문에 위에서 조사한 것과 같이 낙관적 락을 사용하여 성능적으로 더 좋은 결과를 보여줄 수 있다고 생각하여 해당 경우는 낙관적 락을 도입할 예정이다.

  1. 여러 사용자 대기열 진입 시 동시성 제어
profile
Backend Developer

0개의 댓글

관련 채용 정보