[Redis] multiGet 조회 최적화

jihyeon·2026년 8월 18일

1. 문제 상황 (Problem)

  • 공연을 선택했을 때 좌석 상태가 사람이 눈으로 느낄 수 있을 정도로 늦게 올라오는 현상 발견, 원래는 페이지 진입과 거의 동시에 좌석 상태가 바로 렌더링 되어야 하는데 그보다 눈에 띄게 지연됨

2. 원인 분석 (Cause)

  • 좌석 조회 로직 코드 확인 중 Redis에서 좌석 하나하나마다 개별 GET 요청을 보내고 있는 구조임을 발견 (좌석 360개 = 요청 360번)
  • 요청마다 네트워크 왕복이 발생하고 누적되어 응답 지연으로 이어진다는 것을 파악
  • 단건 조회의 비효율성을 개선하고자 다건을 일괄 처리하여 통신 횟수를 줄일 수 있는 방안 리서치
  • 리서치 결과 Redis의 multiGet을 확인, 여러 키를 한 번의 요청으로 조회할 수 있어 반복 조회가 많은 상황에 효과적이라는 것을 파악

3. 해결 방법 (Solution)

  • 기존 단건 GET 요청 로직을 Redis multiGet으로 변경
  • 360번의 개별 네트워크 통신을 단 1번의 통신으로 병합하여 처리


4. 결과 (Result)

  • 동일 로직을 재현한 환경에서 측정한 결과, 응답 시간이 64.19ms → 2.06ms로 단축
  • 단순 조회 속도뿐만 아니라 인프라 간 네트워크 통신 최소화가 성능 최적화의 핵심임을 체감

# 재현 코드 (테스트)

package com.example.ticketing;

import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import java.util.*;

public class RedisBenchmark {
    public static void main(String[] args) {
        LettuceConnectionFactory factory = new LettuceConnectionFactory(
            new RedisStandaloneConfiguration("localhost", 6379));
        factory.afterPropertiesSet();

        StringRedisTemplate redisTemplate = new StringRedisTemplate();
        redisTemplate.setConnectionFactory(factory);
        redisTemplate.afterPropertiesSet();

        List<String> keys = new ArrayList<>();
        for (long i = 1; i <= 360; i++) {
            String key = "seat:" + i;
            redisTemplate.opsForValue().set(key, "userA");
            keys.add(key);
        }

        int rounds = 10;
        long totalIndividual = 0;
        long totalMulti = 0;

        for (int r = 0; r < rounds; r++) {
            long start = System.nanoTime();
            for (String key : keys) {
                redisTemplate.opsForValue().get(key);
            }
            totalIndividual += (System.nanoTime() - start);

            start = System.nanoTime();
            redisTemplate.opsForValue().multiGet(keys);
            totalMulti += (System.nanoTime() - start);
        }

        double avgIndividualMs = (totalIndividual / rounds) / 1_000_000.0;
        double avgMultiMs = (totalMulti / rounds) / 1_000_000.0;

        System.out.printf("360회 개별 조회 평균: %.2f ms%n", avgIndividualMs);
        System.out.printf("multiGet 1회 평균: %.2f ms%n", avgMultiMs);
        System.out.printf("개선율: %.1f%% 단축%n", (1 - avgMultiMs / avgIndividualMs) * 100);

        factory.destroy();
    }
}

0개의 댓글