Playball Logo

Command Palette

Search for a command to run...

목차 열기

Phase별 최적화 타임라인

AS-IS → Phase 1 → Phase 2 → Phase 3 → Phase 4 → TO-BE 단계별 작업 내용·코드 변경·측정 결과

전체 타임라인 요약

Phase시점주요 작업Seat P99DB 커넥션 peak
Phase 0 (AS-IS)2026-04-14최적화 없음6,887ms270 (한계)
Phase 1 (1차)2026-04-15 오전Seat match-exists Caffeine2,100ms250
Phase 1 확대2026-04-15 오후Multi-Service Caffeine 6종1,200ms180
Phase 22026-04-15 저녁Redis 분산 캐시 (User 데이터)900ms150
Phase 32026-04-16 새벽응답 Redis 캐시 + 인프라 튜닝600ms120
Phase 42026-04-16 오전OSIV OFF + Lua + Resilience4j400ms100

Phase 0 — AS-IS (최적화 전)

상태

  • Tomcat 스레드 200 / HikariCP Pool 20 설정 불균형
  • Match / Section / Block 모든 조회가 DB로 직접 갔음
  • Spring JPA OSIV ON (기본값)
  • 대기열 재진입 시 Redis 다중 명령 (3 RTT)
  • PreQueue 마커 동기화에 Thread.sleep 사용

측정 결과

3000 VU 큐 Flow (폴더 01) — Queue P95 6,544ms, 503 40건 (1.7%) / Seat P95 6,177ms, 503 18건 (0.3%)

AS-IS 3000 VU 큐 테스트 결과

4000 VU 큐 Flow (폴더 02) — 시스템 과부하, 로컬 k6 및 크롬이 터짐

4000 VU 크래시 직전4000 VU 타임아웃 대량 발생4000 VU 503 스톰

1000 VU 추천 ON Flow (폴더 03) — 실행 시간 2분 38초, 실행 자체는 PASS

AS-IS 추천 ON 1000 VU 결과 1AS-IS 추천 ON 1000 VU 결과 2

진단

  • RDS CPU 10%, Memory 25% → 사양 문제가 아님
  • HikariCP pending 48, Tomcat 스레드 peak 735 → 커넥션 대기 블로킹
  • 결론: DB를 때리는 쿼리 수 자체를 줄여야 함

중간 조치 A — DB 커넥션 풀 30으로 상향 (인프라 튜닝만)

상태

  • HikariCP maximum-pool-size 20 → 30으로만 증가
  • 코드 최적화 없음 (비교 대조군)

측정 결과 — 1000 VU 큐 Flow (폴더 04)

인프라 풀 30 1000 VU 결과 1인프라 풀 30 1000 VU 결과 2

측정 결과 — 2000 VU 큐 Flow (폴더 05)

인프라 풀 30 2000 VU 결과

중간 최적화 실험 (폴더 06)

중간 최적화 1중간 최적화 2

관찰

  • 커넥션 풀만 늘려도 여전히 P99 고점 존재 → 쿼리 수를 줄이지 않으면 큰 개선 없음을 확인
  • "DB 인스턴스 업그레이드는 근본 해결이 아님" 판단의 근거가 됨

Phase 1 (1차) — Seat match-exists Caffeine PoC

작업 내용

변경 파일: Seat/src/main/java/com/goormgb/be/seat/config/CacheConfig.java, BookingOptionsService.java

@Cacheable(cacheNames = "match-exists", key = "#matchId", unless = "!#result")
public boolean exists(Long matchId) {
    return matchRepository.existsById(matchId);
}
spring.cache:
  type: caffeine
  cache-names: match-exists
  caffeine:
    spec: maximumSize=1000,expireAfterWrite=10m,recordStats

측정 결과 (폴더 07 — 1000 VU, Queue Flow)

메트릭
VU1,001
RPS483.6
총 요청30,212건
Avg1,604.6ms
P501,613.7ms
P952,147.4ms
P992,434.1ms
5031건 (0.003%)
Phase 1 PoC 캐싱 결과 1Phase 1 PoC 캐싱 결과 2Phase 1 PoC 캐싱 결과 3

폴더 08 — 2000 VU 스케일 테스트

메트릭
VU2,000
RPS397.0
P501.65s
P953.05s
P993.34s
에러0건
Phase 1 2000 VU 결과 1Phase 1 2000 VU 결과 2

PoC 검증

  • matchRepository.existsById() 하나만 캐싱해도 P99 8초대 → 2초대로 대폭 개선
  • "Match 조회 한 번 제거" 효과가 이 정도라면, 나머지 JOIN FETCH 쿼리도 캐싱하면 더 좋아질 것 → Phase 1 확대 결정

중간 조치 B — DB 부하 유발 Top 7 쿼리 전수조사

순위위치쿼리제안
1Seat/SeatCommonService:52,90MatchRepository.findDetailByIdOrThrowCaffeine
2Seat/SeatCommonService:56SectionRepository.findAllWithArea...Caffeine
3Seat/SeatCommonServiceBlockRepository.findBySectionIdIn...Caffeine
4Order-Core/OrderService:74,105MatchRepository.findDetailByIdOrThrowCaffeine
5Queue/QueueService:56MatchRepository.findByIdOrThrowCaffeine
6Auth-Guard, Order-CoreUserRepository.findByIdOrThrowRedis (Phase 2)
7Seat/recommendationOnboardingPreference/BlockRedis (Phase 2)
Top 7 쿼리 분석 1Top 7 쿼리 분석 2Top 7 쿼리 분석 3Top 7 쿼리 분석 4

Phase 1 확대 — Multi-Service Caffeine 전면 확대

작업 범위

서비스캐시 이름최대 크기TTL대상 데이터
Seatmatch-exists1,00010분Match 존재 검증
Seatmatch-detail1,00010분Match 메타 (JOIN FETCH)
Seatsection-all161시간스타디움 섹션 구조 (영구 불변)
Seatblocks-by-section-ids5121시간섹션별 블럭 매핑
Queuematch-for-queue1,0001분saleStatus 검증 (짧은 TTL)
Order-Corematch-detail1,00010분주문서용 Match

릴리즈 태그: v1.11.0-staging (커밋 1746faa, 92ffa7f, 1583897)

폴더 10 — Phase 1,2 적용 1000 VU

Phase 1,2 1000 VU 결과 1Phase 1,2 1000 VU 결과 2Phase 1,2 결과 요약 1Phase 1,2 결과 요약 2

폴더 11 — 2000 VU 30초 듀레이션

Phase 1,2 2000 VU 30s 1Phase 1,2 2000 VU 30s 2Phase 1,2 2000 VU 30s 3Phase 1,2 2000 VU 30s 4Phase 1,2 2000 VU 30s 5

폴더 12 — 2000 VU 1분 듀레이션 (안정성 검증)

Phase 1,2 2000 VU 60s 1Phase 1,2 2000 VU 60s 2Phase 1,2 2000 VU 60s 3Phase 1,2 2000 VU 60s 4Phase 1,2 2000 VU 60s 5Phase 1,2 2000 VU 60s 6

효과

  • DB 쿼리 감소: 50~60%
  • 커넥션 peak: 250 → 150
  • P95: ~3s → ~1s

중간 조치 C — HttpClient Pool 상향

작업

  • Gateway/Seat의 HTTP 클라이언트 커넥션 풀 상향
  • Redis/DB 외부 호출 대기 감소

폴더 13 — HTTP Pool 상향 직후

HTTP Pool 상향 1HTTP Pool 상향 2

폴더 14 — 1000 VU (Pool 상향 + Caffeine 병합 효과)

메트릭
VU1,001
RPS325.7
Avg31ms (극적 단축)
P5029ms
P9539ms
P9960ms
에러0건

응답시간 평균 2,000ms → 32ms로 60배 단축.

HTTP Pool + Phase 1,2 1000 VU 1HTTP Pool + Phase 1,2 1000 VU 2HTTP Pool + Phase 1,2 1000 VU 3

폴더 15 — 추천 ON 1000명 재측정 (폴더 03과 동일 조건)

메트릭
VU1,001
RPS294.0
총 요청54,877건 (폴링 포함)
P95157ms
P99826ms
HTTP Pool + 추천 ON 1HTTP Pool + 추천 ON 2HTTP Pool + 추천 ON 3

Phase 2 — Redis 분산 캐시 (User 데이터)

작업 범위

서비스캐시 이름TTLEvict 전략
Auth-Guarduser-by-id10분프로필/상태 변경 시 @CacheEvict
Auth-Guardauth-me30초프로필 변경 시 evict
Order-Coreuser-by-id10분Auth-Guard 캐시 공유

효과

  • /auth/me P99: 400~600ms → 50ms
  • DB 커넥션 +20% 추가 절감

Phase 3 / 4 — 응답 캐시 · 인프라 튜닝 · 핫픽스

Phase 3 작업

서비스캐시TTL대상 API
Seatseat-groups-response5초GET /matches/{id}/seat-groups
Order-Corematches-list-response30초GET /matches?date=...

인프라 튜닝

파라미터변경 전변경 후
Tomcat max-threads200400
HikariCP maximum-pool-size2030
HikariCP minimum-idle205
총 DB 커넥션~80≤250

Phase 4 — 커넥션 회전율 · 스레드 점유 핫픽스 3종

  1. Queue OSIV OFF — 커넥션 회전율 2배 향상
  2. BookingOptions Resilience4j @RetryThread.sleep 블로킹 재시도 → 비동기 재시도 (평균 대기 300ms → 60ms)
  3. Queue Redis Lua 스크립트 통합 — ZREM + DEL + ZADD + ZRANK + ZCARD → Lua 1회 호출 (3 RTT → 1 RTT)

폴더 17 — Phase 3,4 적용 전/후 엔드투엔드 비교

서비스적용 전 Avg적용 후 Avg개선률
Queue325ms96ms-70.4%
Seat741ms236ms-68.2%
Order-Core304ms68ms-77.6%
전체 평균503ms149ms-70.4%
Phase 3,4 적용 전/후 비교

TO-BE — Phase 4 최종 적용 후 측정 (폴더 18)

메트릭
VU1,001
RPS323.5
Avg36ms
P9547ms
P9965ms
에러0건
TO-BE 1000 VU 결과 1TO-BE 1000 VU 결과 2TO-BE 1000 VU 결과 3

Phase별 누적 효과 요약

지표Phase 0 (AS-IS)Phase 1 (1차)Phase 1 확대Phase 4 완료개선률
Queue Flow Avg~2,000ms1,624ms31ms36ms-98%
Queue Flow P99~5,000ms2,664ms60ms65ms-99%
E2E 9-step Avg149ms-70% (Phase 3→4)
503 발생40+18건1건0건0건
DB 커넥션 peak270 한계250150~100-63%

결론

"DB 사양 문제가 아니라 앱이 쿼리를 너무 많이 보내는 문제였다" — 진단이 맞았고, 그에 따른 앱단 해결이 DB 인스턴스 업그레이드 없이 목표치를 달성했습니다.

  • 선택하지 않은 카드: DB 인스턴스 업그레이드 (약 $100/월 지속 비용)
  • 선택한 카드: Caffeine 캐싱 + Redis 분산 캐시 + 인프라 파라미터 재배분 + 코드 레벨 핫픽스 (0원)
  • 결과: Queue Flow P99 5초 → 65ms (~98% 개선), 503 0건