혼자공부한거

랜덤 퀴즈 데이터 추출, 어떤 방식이 가장 빠를까? (MySQL vs Redis vs Java)

컴공코딩러 2025. 4. 16. 19:19

 

 

 

 


 

 
 

운영 중인 앱: 축잘알 테스트 (구글 플레이)

🎯 랜덤 퀴즈 데이터 추출, 어떤 방식이 가장 빠를까?

MySQL vs Java vs Redis – 실전 성능 비교

내가 만든 축구 퀴즈 앱에서는
사용자가 5, 10, 15, 20문제를 랜덤으로 풀 수 있는 기능이 핵심이다.

처음에는 그냥 MySQL의 RAND() 함수 쓰면 끝 아니야? 싶었는데...
퀴즈가 쌓이고, 유저 수가 많아지다 보니 점점 느려지기 시작했다.

그래서 아래 세 가지 방식으로 직접 성능을 비교해봤다:


 

✅ 방식 1: MySQL RAND()

 

@Query(nativeQuery=true, value="SELECT * FROM quiz_info ORDER BY rand() LIMIT ?1")
List<QuizInfo> findRandomQuiz(@Param("number")Long number);

 

  • 장점: 코드가 정말 간단함
  • 단점: 전체 테이블에 난수 계산 + 정렬이 들어가서 O(n log n)
    → 인덱스도 못 쓰고, 데이터가 많아질수록 무조건 느려짐

✅ 방식 2: Java Collections.shuffle()

 

 

@Query("SELECT q.id FROM QuizInfo q") List<Long> findAllIds();
Collections.shuffle(allQuizIds); List<Long> randomIds = allQuizIds.stream().limit(10000).toList();
List<QuizInfo> quizList = quizRepository.findByIdIn(randomIds);

 

  • ✅ 메모리 안에서 처리하니까 DB 부하 적음
  • ❌ 전체 ID를 다 불러와야 해서, ID 수가 많으면 비효율적

✅ 방식 3: Redis SRANDMEMBER

 

Set<String> randomIds = redisTemplate.opsForSet().distinctRandomMembers("quiz:id:set", 10000);
List<Long> ids = randomIds.stream().map(Long::valueOf).toList();
List<QuizInfo> quizList = quizRepository.findByIdIn(ids);

 

 

  • ✅ Redis가 알아서 랜덤하게 골라주기 때문에 속도 빠름
  • ❌ 퀴즈 추가/수정 시 Redis와 DB 간 정합성 문제 생길 수 있음
    → 그래서 Redis에는 ID만 저장해두고, 실제 퀴즈 데이터는 DB에서 조회

 

⏱️ 성능 테스트 결과 (10회 반복, 각각 10,000개 퀴즈 요청)

@Test
void compareQuizLoadPerformanceMultipleTimes() {
    int iterations = 10;

    List<Double> dbRandTimes = new ArrayList<>();
    List<Double> redisTimes = new ArrayList<>();
    List<Double> javaRandTimes = new ArrayList<>();

    for (int i = 0; i < iterations; i++) {
        StopWatch stopWatch = new StopWatch();

        // 1. DB RAND()
        stopWatch.start("DB RAND()");
        List<QuizInfo> randQuiz = quizRepository.findRandomQuiz(10000L);
        stopWatch.stop();
        dbRandTimes.add((double) stopWatch.getLastTaskTimeMillis());


        // 2. Java Random + DB IN
        stopWatch.start("Java Random + DB IN");
        List<Long> allQuizIds = quizRepository.findAllIds(); // 전체 ID
        Collections.shuffle(allQuizIds);
        List<Long> randomIdsJava = allQuizIds.stream().limit(10000).toList();
        List<QuizInfo> javaRandomQuiz = quizRepository.findByIdIn(randomIdsJava);
        stopWatch.stop();
        javaRandTimes.add((double) stopWatch.getLastTaskTimeMillis());

        // 3. Redis + DB IN
        stopWatch.start("Redis + DB IN");
        Set<String> randomIds = redisTemplate.opsForSet().distinctRandomMembers("quiz:id:set", 10000);
        List<Long> idsFromRedis = randomIds.stream().map(Long::valueOf).toList();
        List<QuizInfo> redisQuiz = quizRepository.findByIdIn(idsFromRedis);
        stopWatch.stop();
        redisTimes.add((double) stopWatch.getLastTaskTimeMillis());

        System.out.printf("==== %d회차 테스트 결과 ====\n%s\n", i + 1, stopWatch.prettyPrint());
    }

    printStats("DB RAND()", dbRandTimes);
    printStats("Java Random + DB IN", javaRandTimes);
    printStats("Redis + DB IN", redisTimes);
}

 

📊 성능 비교 결과


방식 평균(ms) 최소(ms) 최대(ms) 표준편차
MySQL RAND() 102.50 60.00 352.00 83.98
Java Random 146.30 98.00 331.00 66.32
Redis SRANDMEMBER 15.00 11.00 23.00 3.6

 

  • ✅ Redis가 평균적으로 가장 빠름 심지어 편차도 낮고, 안정성도 좋음
  • 하지만 외부 서버인 Redis의 latency 이슈로 인해 간헐적으로 가장 느려지는 경우도 있음

  • ⚠️ 하지만 주의할 점도 있다실제로 몇 번은 Java 방식보다도 느려지기도 했다.
    그래서 무조건 Redis가 정답!은 아니라는 걸 느꼈다.
  • Redis는 보통 빠르지만, 서버가 idle 상태였다가 처음 요청이 들어오는 순간, latency가 확 튀는 경우도 있었다.

💡 정리하며

이번 실험을 하면서 얻은 인사이트:

평균 속도만 보면 Redis가 당연히 좋아 보이지만,
항상 ‘최악의 상황’까지 고려해야 실제 운영 환경에서 문제가 안 생긴다.
서비스 성능은 단순 수치가 아니라
데이터 특성 + 인프라 상태 까지 다 보고 판단해야 한다.


✍️ 마무리


랜덤 퀴즈처럼 자주 호출되는 기능은  
데이터를 어떤 방식으로 가져오느냐에 따라 사용자 경험이 확 달라진다.

이번 테스트를 통해, 단순한 기능에도  
성능 최적화와 구조 설계에 대한 고민이 얼마나 중요한지 느낄 수 있었다.

앞으로도 사용자 입장에서 더 나은 경험을 주기 위해  
지속적으로 성능 테스트 → 구조 개선을 반복할 예정이다.