Engineering Note

선착순 이벤트 시스템의 동시성 및 트래픽 처리 개선 본문

SW Engineering/선착순 이벤트

선착순 이벤트 시스템의 동시성 및 트래픽 처리 개선

Software Engineer Kim 2026. 1. 3. 11:09

문제 상황

선착순 이벤트 주문 기능을 구현. 동시에 여러 사용자가 주문 요청을 하면 생기는 재고 유실 문제.

 

Redis 분산락 선택 이유.

 

Race Condition 으로 발생하는 Lost Update문제를 해결하기 위해 조회시점에 락을 걸어 현재 트랜잭션이 종료 되기 전에는 데이터의 경합문제가 생기지 않게 문제를 해결했다. 비관적락으로 처음 문제를 해결한 이유는 어노테이션만으로 간단하게 구현할 수 있어서 선택. 현재 시스템 아키텍처에서 발생가능한 문제를 분석, 비관적락 사용시 DB병목이 발생할 수 있고, 선착순 이벤트 상황시 발생할 수 있는 트래픽을 처리하기 위해 scale out을 하기 어려운 구조라서 시스템을 개선, Docker를 통해 어플리케이션을 컨테이너화하고 WAS를 확장하였고, Redis를 도입하여 분산락으로 DB 병목을 개선. 

개선 후에는 실제 모니터링을 통해 개선 상황을 데이터로 검증. Redis도입후 커넥션 사용량이 90% 감소. DB 병목을 제거한 결과, 선착순 이벤트 동시성 문제만 해결된 게 아니라, 확보된 DB 자원으로 상품 조회 API 처리량이 1.3배 증가하는 등 시스템 전반의 처리 능력이 향상되었다.

 

문제 해결 과정

  1. 동시성 문제 발견 → 비관적 락으로 해결 (정합성 확보)
  2. 부하 테스트 실행 → 모니터링으로 DB 커넥션 사용량 과다 발견
  3. 문제 분석: 선착순 이벤트 트래픽이 DB로 직접 몰림
  4. 개선 방안 설계: Redis 분산락으로 DB 앞단에서 제어
  5. 구현 및 검증: 커넥션 90% 감소, 조회 API 1.3배 향상

이게 "지속적인 시스템 개선" 사이클이네요.

  • 일단 동작하게 만들고 (비관적 락)
  • 실제 부하 상황을 측정하고 (테스트 & 모니터링)
  • 병목을 찾아내고 (DB 커넥션)
  • 아키텍처를 개선하고 (Redis 도입)
  • 결과를 검증하는 (성능 지표)

 

 

 

 

"선착순 이벤트 시스템의 동시성 및 트래픽 처리 개선" → 핵심 문제에 집중

선착순 이벤트라는 특수한 상황이 만들어낸 두 가지 핵심 과제:

  1. 동시성 제어 - 같은 순간 수많은 요청의 정합성 보장
  2. 트래픽 처리 - 순간적으로 몰리는 대량 요청 감당

해결한 방법:

  • 동시성 → 비관적 락 → Redis 분산락
  • 트래픽 → Docker + WAS 확장 + DB 병목 제거

 

Redis 분산락 도입의 3가지 성과:

 

  • 동시성 문제 해결 (1차 목적)
    • 재고 데이터 100% 정합성 보장
    • 비관적 락으로 일단 해결
  • 시스템 확장성 및 성능 개선 (2차 목적)
    • 선착순 이벤트 트래픽 감당 필요
    • Scale-out 가능한 구조로 전환 필요
    • 그래서 Redis 분산락 선택
  • 개선 결과
    • DB 커넥션 사용량 90% 감소 (병목 제거)
    • 상품 조회 API 처리량 1.3배 증가 (전체 성능 향상)
    • 재고 정합성 100% 유지

 

 

 

 

event_id, member_id 성능 개선 하나만 더 해보자.

또, 지금 redis



 
현재 레디스 설계 게이트 키퍼로서 역할 다시 분석

✅ 실제로 설계된 아키텍처의 장점

1. 빠른 실패 (Fail-Fast) 전략

@Transactional
public Long createOrder(...) {
    event.processOrder(1);           // 재고 체크 → 빠른 실패
    validateDuplicateOrder(...);      // 중복 체크 → 빠른 실패
    orderRepository.save(order);      // 성공 케이스만 DB 부하
    participantRepository.save(...);
}

2. Redis 락으로 DB 보호

128,941 요청/10초
    ↓
Redis Lock (순차 처리) ← 여기서 트래픽 제어!
    ↓
MySQL (안전한 부하) ← 실제로는 100건만 처리

3. 결과

✅ 11,394 req/sec 부하를 받음
✅ 대부분 Redis 레벨에서 빠른 실패 (락 획득 실패)
✅ DB는 실제 100건만 처리 → 부하 없음
✅ 시스템 전체 안정성 유지

📊 최종 정리

아키텍처의 핵심 설계

  1. Redis 락: 트래픽 게이트키퍼 역할
  2. 빠른 실패: 불필요한 DB 접근 차단
  3. DB 보호: 실제 필요한 요청만 처리

성능 지표

  • 외부 처리량: 11,394 req/sec (매우 높음)
  • DB 실제 부하: 100건/10초 = 10 TPS (매우 낮음)
  • 응답 속도: 평균 79ms (빠름)

비즈니스 가치

  • ✅ 선착순 100명 정확히 처리
  • ✅ 대규모 트래픽에도 시스템 안정
  • ✅ DB 부하 최소화로 비용 절감

 
 
 

maximum-pool-size: 10
minimum-idle: 5

 
최대 커넥션 풀을 10개로 설정 해두고 비관적락과 분산락의 커넥션 사용량과 
 
idle : 커넥션 풀에 존재하지만 현재 사용중이지 않은 커넥션
pending thread : 커넥션을 요청했지만 받지 못한 쓰레드
 
 
비관적락 커넥션

 
 
 
 
 
Redis

 
반면, Redis를 이용했을 때는 Redis가 락을 제어하기 때문에 쓰레드들은 레디스 락이 해제 된 후에 순차적으로 커넥션을 할당 받기 때문에 커넥션이용률이 10%이고,  Pending thread가 0이다. Pending Thread가 ‘0’이라는 의미는 모든 스레드가 요청시 즉시 커넥션을 받았다는 의미이고 락을 획득 하지 못했을 때도 커넥션을 요청 받기 위해 자원을 낭비하지 않고 대기한다는 의미이다.


이렇게 비관적 락을 사용하면 DB가 병목이 되어 상품조회 등 다른 API는 커넥션을 이용할 수 없다는 뜻이기도 하다. 이러한 상태를 부하테스트를 통해 확인해보기 위해 상품조회 API와 선착순 이벤트를 함께 부하테스트를 Jmeter를 이용해 테스트해보았다.
 

테스트 방법 
비관적락 선착순 이벤트 요청 + 상품 조회 API 요청을 보내고 상품 조회 API의 처리량을 테스트해보고, 다시 분산락을 통한 선착순 이벤트 요청과 상품 조회 API를 함께 요청하고 상품 조회 API의 처리량을 비교해보았다.
 
 
 
테스트 조건
비관적락 + 상품조회
비관적락 요청 쓰레드(1000) + duration(10)
상품
 
아래는 Jmeter를 이용해 테스트 한 결과이다.

  sample average Min Max Error Throughput 최초 재고 남은 재고(소진재고) 유실데이터 선착순 당첨
비관적락 123471 81 0 7620 99.92% 11378/sec        
상품조회 API(with 비관적락) 8376 60 0 4800 98.39% 788.9/sec 100 0(100 0 100
분산락 + 상품조회API 112592 90 0 6239 99.91% 10075.6/sec 100 0(100) 0 100
상품 조회 API(분산락) 10934 45 0 1110 99.30% 1096.6/sec        

 
 
테스트 결과 분산락과 상품조회를 했을 때 상품 조회 처리량이 1.3배 많아졌다.
 ----

 

선착순 이벤트 시스템의 동시성 및 트래픽 처리 개선 정리

1. 문제 상황

  • 선착순 이벤트 주문 기능 구현 필요
  • 동시 다발적인 주문 요청으로 인한 재고 유실 문제 발생
  • Race Condition으로 인한 Lost Update 문제

2. 1차 해결: 비관적 락 적용

  • 문제 원인 분석: 조회 시점의 데이터 경합
  • 해결 방법: 비관적 락으로 트랜잭션 종료 전까지 락 유지
  • 선택 이유: 어노테이션 기반으로 간단하게 구현 가능
  • 결과: 재고 정합성 100% 보장

3. 병목 지점 발견

  • 부하 테스트 실행
  • 모니터링을 통한 문제 발견: DB 커넥션 사용량 과다
  • 원인 분석: 선착순 이벤트 트래픽이 DB로 직접 몰림
  • 추가 문제 인식: Scale-out이 어려운 구조

4. 시스템 개선

아키텍처 변경:

  • Docker를 통한 애플리케이션 컨테이너화
  • WAS 확장 (Scale-out 구조 확보)
  • Redis 분산락 도입으로 DB 앞단에서 트래픽 제어

Redis 분산락 선택 이유:

  • DB 병목 해소
  • 분산 환경에서 동시성 제어
  • 시스템 확장성 확보

5. 개선 결과 검증

  • DB 커넥션 사용량 90% 감소
  • 상품 조회 API 처리량 1.3배 증가
  • 재고 데이터 정합성 100% 유지
  • 확보된 DB 자원으로 전체 시스템 처리 능력 향상

6. 결론

  • 데이터 기반 모니터링을 통한 병목 지점 발견
  • 아키텍처 개선을 통한 확장 가능한 시스템 구축
  • 동시성 제어와 성능 개선을 동시에 달성


 
 

Comments