MSA - 좌석 예매 시스템 캐싱 설계

헨도·2025년 5월 19일

Redis

목록 보기
4/4
post-thumbnail

RBucket, RList 를 통해 효율적인 좌석 조회와 관리

개요

야구장 예매 서비스를 MSA 환경으로 개발하면서, 실시간 좌석 상태를 빠르게 조회하고 예매할 수 있어야 했습니다.
동시 접속자 수가 많은 상황에서도 병목 없이 안정적인 서비스를 제공하기 위해 Redis를 활용한 캐싱 전략을 활용했습니다.
이 과정에서 좌석 데이터를 효율적으로 캐싱하고 조회하는 방법으로 RBucket 과 RList를 조합하는 방식을 선택했습니다.

좌석 데이터 캐싱 구조

Redis Key 구성

  • seat:{date}:{block}:{column}:{row}
  • ex) seat:20250410:101:1:1

좌석의 상태 데이터는 위와 같은 키 형태로 Redis 에 저장했습니다.
값은 다음과 같은 구조의 Map 형태로 저장했습니다.

{
  "status": "available",
  "userId": null,
  "matchId": 3,
  "createdAt": "20250410 10:24:10",
  "expiredAt":
}

여기서 각 좌석 상태는 Redisson의 RBucket을 활용해 저장했습니다.

RBucket<Map<String, String>> bucket = redissonClient.getBucket(cacheKey);
bucket.set(cacheValue, Duration.ofMinutes(5));

5분 유효기간 설정

  • 테스트 기간에는 5분으로, 실제 서비스에서는 예매 시간에 맞춰 expire 시간을 조절 가능

블록 단위 좌석 키 관리

문제점

특정 날짜, 블록을 선택했을 때 seat:20250410:101:*:* 형식으로 와일드카드 조회를 하려면 getKeysByPattern() 또는 SCAN 명령어를 사용해야 합니다.
하지만 이 방법은 실시간 서비스에서

  • CPU 부하 급증
  • 속도 지연
  • Redis 싱글스레드 특성상 병목 현상 발생

이라는 심각한 문제가 있습니다.

개선 방법 - 좌석 키 리스트 따로 관리

블록별로 좌석 키를 RList로 저장하여 해당 블록 좌석 조회 시, 리스트에서 키만 가져오고 필요한 좌석 정보는 각 키로 getBucket() 해서 조회

RList<String> blockSeats = redissonClient.getList(blockKey);
if (!blockSeats.contains(cacheKey)) {
	blockSeats.add(cacheKey);
}

예시

사용자 UI

  • 날짜 : 20250410
  • 블록 : 101

조회 흐름

  1. seatKeys:20250410:101 리스트에서 좌석 키 조회
  2. 각 좌석 키로 Rbucket 값 조회
  3. 좌석 상태, 사용자, 만료 시간 정보 가져오기

Redis 구조 요약

Key데이터 타입내용
seat:{date}:{block}:{column}:{row}Rbucket좌석 상태 정보
seatKeys:{date}:{block}RList해당 블록의 좌석 키 목록

장점

빠른 조회 속도

  • 리스트에서 키만 조회하므로 패턴 스캔 없이 빠르게 가능

CPU 부하 최소화

  • SCAN 사용하지 않음
  • Redis의 싱글스레드 특성에 최적화

구조적 확장성

  • 좌석 추가/삭제 시 리스트만 수정
  • 블록 단위로 관리 가능

데이터 일관성

  • 좌석 키를 리스트로 관리하며, 좌석 정보는 별도 버킷으로 분리해 충돌 방지

단점 및 보완점

좌석 추가/삭제 시 리스트 동기화 필요

  • 좌석 삭제, 갱신 시 RList 도 함께 수정해줘야 함

좌석 갯수가 매우 많을 경우 RList 용량 증가

  • 블록 단위로 리스트를 나눴기 때문에 대량 데이터 문제를 완화했지만, 극단적인 케이스에서는 리스트의 용량도 모니터링 필요

결론

실시간성이 중요한 야구장 예매 서비스에서 좌석 데이터를 Redis RBucket + RList 조합으로 효율적으로 관리하여 빠른 좌석 조회 속도와 서버 부하 최소화를 모두 달성했습니다.
특히 기존 getKeysByPattern() 방식의 단점을 극복하고, 블록 단위의 좌석 리스트 관리 방식으로 확장성 있고 안정적인 예매 시스템을 구현할 수 있었습니다.

profile
Junior Backend Developer

0개의 댓글