상황

위 3가지 조회 API는 모두 DB에서 좌석 정보를 조회한 뒤, 각 좌석이 Redis에서 점유(occupy) 중인지를 다시 확인하는 방식이었다.

image.png

DB: 특정 회차의 좌석 N건 조회
     ↓
Redis: 좌석 N건 각각의 occupy Key를 MGET으로 한 번에 조회
     ↓
두 결과를 합쳐 AVAILABLE / OCCUPIED 판별

콘서트 하나의 좌석 수는 3000석으로 설정하였다. 이 3000건을 매 요청마다 Redis에서 점유 여부로 대조해야

처음에는 redisTemplate.opsForValue().multiGet(occupyKeys) 을 적용하여 좌석마다 흩어진 요청을 왕복 1번으로 묶는 MGET을 사용했다. 그러나, 이것은 RTT만 줄인 것이지 O(N)의 연산으로, 조회량 자체를 줄인 게 아니었다.

// occupy Key 목록 획득
List<String> occupyKeys = scheduleSeatEntities.stream()
    .map(scheduleSeat -> ScheduleSeatRedisKeys.occupyKey(scheduleSeat.getId()))
    .toList();
    
// 한 번의 MGET으로 일괄 조회
List<Object> occupyValues = redisUtil.multiGet(occupyKeys);
avg p90 p95 p99
remaining_seats_duration 144ms 1s 101ms 11ms 307ms 417ms 673ms
seats_duration 209ms 1s 159ms 27ms 420ms 537ms 812ms
my_occupy_duration 196ms 1s 150ms 22ms 397ms 496ms 772ms

즉, RTT가 병목이 아니라, 매번 3천건을 훑는 조회 자체가 병목이라고 판단하였다.