쿠폰 발급 시스템 디자인

죠랭이·2024년 11월 18일
0

아키텍처

목록 보기
4/4

이번 시간엔 쿠폰 발급 시스템 디자인을 진행해보고자 한다. 해당 시스템은 어떻게 디자인할까?

기능적 요구사항

이번에 만들고자 하는 쿠폰 발급 시스템의 기능 요구사항은 다음과 같다.
1. 1000장의 쿠폰을 발행하여 사용자에게 제공한다.
2. 1인당 1개의 쿠폰만 발행한다.

비기능적 요구사항

  1. Data Consistency가 보장되어야 한다.(정확하게 1장의 쿠폰이 1명의 사용자에게 할당되도록 한다.)
  2. 예약을 위해 한번에 대용량 트래픽이 몰리는 상황에서도 안정적으로 동작해야 한다.(Availability)

데이터 모델

쿠폰_발급_시스템_데이터_모델

  • 여기서, 사용자에게 발행한 쿠폰을 쿠폰 데이터 모델에서 상태값과 user_id 값으로 관리해도 되고 경우에 따라선 map_coupons_users 테이블을 하나 생성하여 할당해도 좋다. 필자의 경우는 MySQL8 버전에서 지원하는 Skip Locked 잠금을 적용해보고자 coupons 데이터 모델에서 매핑관계까지 전체 관리하는 방향으로 디자인하겠다.

API

  • 쿠폰 발급하기
POST /{user_id}/coupons
{
	"name" : "test",
    ...
}
---
{
	"coupon_id" : "BBSS123XE",
    "expired_at" : 12314313,
    ...
}

상세 설계

  • 쿠폰 발급 시스템에서는 쿠폰을 미리 1000개 발행하느냐 마느냐 이 두 가지 측면에서 접근이 가능하다. 각각의 상황에 따라 서로 다른 아키텍처가 그려지기에 먼저 쿠폰을 미리 발급하지않고 사용자 요청 시 발급하는 방향으로 디자인해보겠다.

아키텍처1. 쿠폰 발급 요청 시 쿠폰 신규 생성

아키텍처1

  • 이제 동시성을 핸들링하는 방식에 대하여 자세히 살펴보자. 일단, 남아있는 발급 가능한 쿠폰 개수의 경우는 Redis 캐시의 원자성을 활용한다. 싱글 스레드로 동작할 뿐만 아니라 SETNX 명령어로 Spin Lock 개념을 도입하여 분산락을 적용할 수 있다. 처음에 설정한 [key, value]에서 value 유효성 검증을 선행한 후 사용자에게 쿠폰 한 개를 발급한다. 이때, RDB 분산락을 활용한다. MySQL에서는 Named Lock이라고 하는 분산락으로 1인당 정확하게 1개의 쿠폰을 할당하도록 데이터 일관성을 보장한다. Redis에서 Batch Job으로 남은 쿠폰 개수를 동기화한다면 데이터 일관성을 높일 수 있다. 여기서 TPS가 만약 더 증가하게 된다면 Scale-out으로 대응하되 그럼에도 커버하기 힘든 수준의 트래픽이 몰릴 경우 메시지 큐를 중간에 도입하여 비동기 처리한다. 아키텍처는 다음과 같다.
    아키텍처2
  • 메시지 큐와 Worker 모듈을 추가하여 요청 순서에 맞게 쿠폰을 발급하는 방식으로 설계하였다. 실제로 쿠폰 발급 처리를 하는 모듈은 Worker 모듈로 Coupon 모듈은 남은 쿠폰 개수가 유효한지만 검증하여 메시지를 발행한다. 또한, Worker 모듈에서 성공적으로 쿠폰 발행을 하게될 경우 성공 메시지를 또다른 메시지 큐에 적재하여 이를 Coupon 모듈이 소비하는 방식으로 동작한다.

아키텍처2. 쿠폰 1000개 미리 발행

  • 트랜잭션 Lock 범위를 넓혀 다음의 SQL로 발행 가능 여부를 판단하여 MySQL8 버전에서 지원하는 Skip Locked 기능으로 동시성 제어할 수 있다.
SELECT COUNT(*) 
FROM coupons 
WHERE status = 'UNUSED';

UPDATE coupons
SET user_id = :id, status = 'UNUSED'
WHERE coupon_id = (
    SELECT coupon_id 
    FROM coupons 
    WHERE status = 'UNUSED' 
    LIMIT 1 
    FOR UPDATE SKIP LOCKED
);

하지만, 이렇게 할 경우 트랜잭션 Lock 범위가 넓어져 데드락에 빠질 위험이 있으며 불필요한 트랜잭션으로 어플리케이션 성능 저하가 발생할 수 있다. 따라서, RDB + Redis 분산락 조합으로 Data Consistency와 Availability를 보장하는 방향으로 설계하는 것이 베스트다.

참고

profile
슈퍼 개발자를 목표로 하는 주니어

0개의 댓글