혼자공부한거

Redis로 유저 랭킹을 불러오면 정말 더 빠를까?

컴공코딩러 2025. 4. 17. 20:13

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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

🚀 Redis로 유저 랭킹 조회, 정말 더 빠를까?

게임이나 퀴즈 서비스에서 랭킹 기능은 빠질 수 없다.
근데 어떻게 불러오느냐에 따라 체감 속도는 완전히 달라진다.

그래서 직접 MySQL vs Redis 성능을 비교해봤다.
과연 Redis는 정말 빠를까?


✅ 기존 방식: MySQL로 랭킹 조회

 

처음엔 이렇게 했다:

  • 10분마다 랭킹 데이터를 UserRankInfo 테이블에 저장
  • 요청이 올 때마다 MySQL에서 다시 조회
@Query(nativeQuery = true, value =
        "SELECT * FROM front_user_info WHERE total_quiz_count > 30 
        ORDER BY percentage_of_answer DESC LIMIT 100")
        List<FrontUserInfo> getPlayerTop100();

이 방식은 데이터가 많아질수록 점점 느려졌다.


 

✅ 개선 방식: Redis 캐싱 (Sorted Set 활용)

🔄 랭킹 업데이트 방식

  • 10분마다 Redis에 Top 100 저장
  • ZSet으로 정렬 자동 처리
  • 유저 정보를 JSON으로 직렬화해서 저장
redisTemplate.opsForZSet().add("quiz:rank", jsonUserRankInfo, score);

score는 정답률이고, ZSet에서 이걸 기준으로 정렬해준다.


🔍 캐싱 조회 방식

  • Redis에서 정렬된 TOP 100 데이터를 조회
  • JSON을 객체로 변환하여 반환
Set<String> redisSet = redisTemplate.opsForZSet().reverseRange("quiz:rank", 0, 99);
List<UserRankInfo> redisResult = redisSet.stream()
        .map(json -> objectMapper.readValue(json, UserRankInfo.class))
        .toList();

✅ 테스트 코드

@Test
@DisplayName("✅ MySQL vs Redis 랭킹 조회 성능 비교")
void compareMysqlAndRedisRankLoadPerformance() throws JsonProcessingException {
    List<Double> mysqlTimes = new ArrayList<>();
    List<Double> redisTimes = new ArrayList<>();

    for (int i = 0; i < TEST_REPEAT; i++) {
        // MySQL 성능 측정
        long mysqlStart = System.nanoTime();
        List<UserRankInfo> mysqlResult = userRankRepository.findAll();
        long mysqlEnd = System.nanoTime();
        mysqlTimes.add((mysqlEnd - mysqlStart) / 1_000_000.0);

        // Redis 성능 측정
        long redisStart = System.nanoTime();
        Set<String> redisSet = redisTemplate.opsForZSet().reverseRange("quiz:rank", 0, 99);
        List<UserRankInfo> redisResult = redisSet.stream()
                .map(json -> {
                    try {
                        return objectMapper.readValue(json, UserRankInfo.class);
                    } catch (JsonProcessingException e) {
                        throw new RuntimeException(e);
                    }
                })
                .collect(Collectors.toList());
        long redisEnd = System.nanoTime();
        redisTimes.add((redisEnd - redisStart) / 1_000_000.0);
    }

    printStats("MySQL", mysqlTimes);
    printStats("Redis", redisTimes);
}

📊 성능 테스트 결과 (평균 100회 반복 측정)

100번 반복 테스트로 평균 시간 비교해봤다:

 

항목 평균 시간 최소 최대 표준 편차
MySQL 6.38 ms 3.20 ms 89.31 ms 8.47 ms
Redis 2.11 ms 0.91 ms 22.13 ms 2.24 ms

 


📌 결론: 자주 조회되는 랭킹이라면 Redis가 답

✅ 평균 속도 기준으로 Redis는 MySQL보다 약 3배 빠름
✅ 최대 응답 시간도 Redis가 훨씬 더 안정적
✅ 응답 시간 편차도 낮아서 일관된 UX 제공 가능

 

💡 자주 조회되고, 자주 안 바뀌는 데이터는 무조건 캐싱이 답이다.
ZSet 덕분에 정렬도 따로 할 필요 없이 깔끔하게 처리된다.


🧩 보너스: Redis가 비어 있다면?

Redis는 TTL이나 서버 재시작으로 데이터가 비어 있을 수 있다.

이를 대비한 나의 방법은:

Redis가 비어 있으면 MySQL에서 다시 데이터를 불러와 Redis를 다시 채워 넣는 방식

 

(이후 찾아보니 Cache-Aside Pattern 패턴이라고 함)

 

📌 이 방법으로 항상 빠른 랭킹 응답을 보장할 수 있다.


🧠 정리하며

이번 테스트를 통해
랭킹 기능 하나에도 성능, 안정성, 사용자 경험 모두 고려해야 한다는 걸 다시 느꼈다.
빠르고 일관된 응답 속도 = 사용자 만족도라는 걸 실제로 체감했다.