Home > General > Concurrency > 선착순 이벤트 시스템 개발하기

선착순 이벤트 시스템 개발하기
FCFS Redis Kafka Coupon

선착순 이벤트 시스템 개발하기

  • 발생할 수 있는 문제
    • 쿠폰을 100개만 발급해야 하는데, 쿠폰이 100개보다 많이 발급되었다
    • 트래픽이 급증해 이벤트 페이지 접속이 안된다
    • 이벤트랑 전혀 상관없는 페이지들도 느려졌다
  • 해결책
    • 트래픽이 몰렸을 때 대처하는 방법 적용
    • Redis를 사용해 쿠폰 발급 개수를 보장하기
    • Kafka를 활용해 다른 페이지에 미치는 영향을 줄이기

Redis로 Race Condition 해결하기

  • 기존 락 활용의 문제
    • 선착순 쿠폰 발행은 쿠폰 개수에 대한 정합성을 요구함
    • 락 활용은 요구사항의 임계영역이 길어서 성능 불이익 발생
      • 발급된 쿠폰 개수를 가져오는 것부터 쿠폰 생성까지 락을 걸면 임계영역이 길어져서 성능 불이익
  • 해결책: 레디스는 싱글 스레드로 동작해 Race Condition 해결
    • 애플리케이션의 모든 스레드는 언제나 최신 값을 가지게 됨
  • 레디스 incr 명령어 활용
    • key의 value를 1씩 증가시킴
    • 성능이 매우 빠른 명령어
  • 남은 문제점
    • 쿠폰 발급 개수가 많아질수록 RDB에 부담을 주어 서비스 지연 및 오류 발생
      • 짧은 시간 내 많은 요청 -> DB 서버 리소스 과부하
    • 쿠폰 전용 DB가 아니라면 다른 서비스에도 영향
    • e.g. MySQL이 1분에 100개 Insert가 가능하다고 가정
      • 10:00 쿠폰 생성 10000개 요청
      • 10:01 주문 생성 요청
      • 10:02 회원가입 요청
      • -> 쿠폰 생성으로 인해 100분 이후에 주문 및 회원가입 요청이 처리됨
      • -> 심지어 보통은 타임아웃이 있으므로, 쿠폰 생성 일부분과 주문 및 회원가입 처리 실패

Kafka로 처리량 조절하기

  • 데이터 정합성은 Redis로 이미 확인했으므로, 쿠폰 생성만 처리
  • Kafka
    • 분산 이벤트 스트리밍 플랫폼
    • 이벤트 스트리밍: 소스에서 목적지까지 이벤트를 실시간으로 스트리밍하는 것
      • Producer(소스) - Topic(큐) - Consumer(목적지)
  • 장점
    • API에서 직접 생성하는 것에 비해 처리량 조절이 가능 -> DB 부하 감소
      • 를 사용하므로 이벤트가 하나가 끝난 후 다음 이벤트가 처리되어 DB에 한 번에 쏠리지 않음
  • 단점
    • 이벤트 생산과 이벤트 처리약간의 텀이 발생
      • Producer의 이벤트 생산은 매우 빠르지만, Consumer는 이벤트를 처리하느라 시간차 발생

부록: 쿠폰 1인당 1개로 제한하기

  • DB 레벨 제한: Unique key 사용하기
    • userId, couponType에 유니크 제약 조건 걸기
    • 문제점: 보통 서비스는 한 유저가 같은 타입의 쿠폰을 여러개 가질 수 있으므로, 실용적이지 않음
  • 락 범위 넓혀서 쿠폰 발급 여부 조회해 판단하는 로직 추가하기
    • 쿠폰 발급 여부 판단 로직
      • 쿠폰 발급 여부 조회: select * from coupon where userId = ?
      • 쿠폰이 이미 있다면 발급하지않고, 미지급일 때만 발급
    • 락 범위: 쿠폰 발급 여부 조회 ~ Redis 동시성 체크 ~ 카프카 이벤트 생산
    • 문제점
      • 생산자와 소비자의 시간차 때문에 쿠폰이 여러 개 발급될 수 있음
        • 소비자에서 아직 발급 중인데 유저의 쿠폰 발급 요청이 한 번 더 온다면?
      • API에서 쿠폰 생성까지 하더라도 락 범위가 너무 넓어 성능 저하 발생
  • Set 자료구조 사용하기 (권장)
    • userId를 Set에 저장하면 쿠폰 발급 여부를 바로 알 수 있음
    • Redis도 Set을 지원하므로 활용

부록: Consumer 예외 처리하기

  • 문제: Consumer에서 예외가 발생하면, 발급된 쿠폰 개수는 올라갔는데 쿠폰은 발급되지 않은 상황 발생
    • 즉, 100개보다 적은 쿠폰이 발생하는 상황 발생 가능
  • 해결책: Consumer에서 예외 발생 시, 백업 데이터(FailedEvent 테이블)와 로그 남기기
    • 추후 배치 프로그램으로 주기적으로 실패한 이벤트를 다시 처리해 쿠폰 발급