문제 발생

Redis 캐시를 쓰면서도 서비스 초반에 첫 요청이 느려지는 문제가 반복됐다. 특정 모듈이나 배너 데이터가 Redis에 올라가지 않은 상태에서 요청이 들어오면:

  • 최초 호출이 DB/API를 직접 타면서 지연 발생
  • 심하면 여러 사용자가 동시에 접근해 DB 부하 급증
  • 캐시가 차더라도 TTL이 들쑥날쑥해 warmup 효과가 반감

결국 “캐시가 있음에도 초반 응답이 느린” 아이러니가 발생했다.


원인 파악

  • 캐시 미스 시점에만 DB/API를 타도록 설계되어 있었다.
  • TTL 관리가 단순해서 모든 데이터가 같은 시점에 만료되고, 특정 시점에 집중 미스가 발생했다.
  • WarmUp 로직은 있었지만 옵션 TTL, 빠른 만료(fastExpire) 정책 등이 반영되지 않아 균형 잡힌 캐시 적재가 이뤄지지 않았다.

해결

WarmUp 로직을 개선해 데이터를 미리 Redis에 적재하고, TTL을 더 세분화했다.

@Override
public void warmUp(RedisCacheDataVO redisCacheDataVO) {
    if (!redisCacheDataVO.isUseCache()) {
        return;
    }

    String redisKey = redisCacheDataVO.getRedisKey();
    String type = redisCacheDataVO.getType();
    Map<String, Object> setMap = buildCachePayload(redisCacheDataVO);

    CompletableFuture.runAsync(() ->
        redisTemplate.exchange(getRedisApiUrl() + RedisInfo.RedisEndpoint.SET_WARMUP.path(type),
            HttpMethod.POST, new HttpEntity<>(setMap),
            new ParameterizedTypeReference<Map<String, Object>>() {})
    ).exceptionally(e -> {
        recordFailure("warmUp", redisKey, e);
        return null;
    });
}

private Map<String, Object> buildCachePayload(RedisCacheDataVO vo) {
    Map<String, Object> copyMap = deepCopy(vo.getData());
    int expireTTL = vo.getExpireTTL();

    // expireTTL의 1/10로 optionTTL 설정, 최대 5분
    int optionTTL = Math.min(expireTTL / 10, 300);
    copyMap.put("optionTTL", optionTTL);

    return copyMap;
}

개선 포인트:

  • CompletableFuture.runAsync로 WarmUp을 비동기 실행 → 응답 경로 방해 최소화
  • TTL을 expireTTL / 10으로 나눈 optionTTL을 추가하되, 최대 5분으로 제한
  • 빠른 만료 플래그(fastExpire)와 연계해 일부 데이터는 더 짧게 캐싱

효과 / 깨달음

  • 초반 요청 지연이 크게 줄어들었다 (DB 직접 호출 감소).
  • 모든 데이터가 동시에 만료되지 않아 만료 밀도가 완화됐다.
  • WarmUp이 실제로 운영에 의미 있게 동작하기 시작했다.
  • TTL은 단순 숫자가 아니라, 데이터 특성에 따라 다층적으로 관리해야 한다는 걸 체감했다.