이 글에서 얻는 것

  • Cache Stampede의 구조적 원인을 설명할 수 있습니다.
  • 분산 락 + 조기 만료 + 이중 캐시를 조합하는 실전 패턴을 이해합니다.
  • 실제 코드로 안전한 락 획득/해제를 구현할 수 있습니다.

1) 문제 상황: TTL 만료 순간 폭발

핫 키가 만료되는 순간, 동시에 수천 개 요청이 DB로 쏠리면 DB가 먼저 죽습니다. 이를 막기 위해 단순 TTL 외에 추가 전략이 필요합니다.

2) 기본 전략 3가지

2-1. 분산 락으로 단일 재생성 보장

한 번에 한 요청만 DB를 조회해 캐시를 다시 채우게 합니다.

public String getUserProfile(String userId) {
    String key = "user:" + userId;
    String cached = redis.get(key);
    if (cached != null) return cached;

    String lockKey = "lock:" + key;
    String token = UUID.randomUUID().toString();
    boolean locked = redis.set(lockKey, token, 5, TimeUnit.SECONDS, NX);

    if (!locked) {
        sleep(80); // 짧게 대기 후 재시도
        return redis.get(key); // 재조회
    }

    try {
        String fresh = db.loadUser(userId);
        redis.set(key, fresh, 60, TimeUnit.SECONDS);
        return fresh;
    } finally {
        // 안전한 락 해제: 토큰 검증
        releaseLock(lockKey, token);
    }
}

Lua로 안전 해제:

-- unlock.lua
if redis.call("GET", KEYS[1]) == ARGV[1] then
  return redis.call("DEL", KEYS[1])
else
  return 0
end

2-2. 조기 만료 (Probabilistic Early Expiration)

TTL이 끝나기 전에 확률적으로 갱신하여 요청을 분산시킵니다.

boolean shouldRefresh(double ttlLeftSec) {
    // 만료가 가까울수록 확률을 증가
    double p = Math.exp(-ttlLeftSec / 10.0);
    return Math.random() < p;
}
  • 10초 남았을 때 30% 갱신
  • 2초 남았을 때 80% 갱신

2-3. 이중 캐시 (L1/L2)

  • L1 (로컬 캐시, Caffeine): 초고속, 짧은 TTL
  • L2 (Redis): 공유 캐시, 긴 TTL
String get(String key) {
    String v = localCache.getIfPresent(key);
    if (v != null) return v;

    v = redis.get(key);
    if (v != null) {
        localCache.put(key, v);
        return v;
    }

    v = db.load(key);
    redis.set(key, v, 60, TimeUnit.SECONDS);
    localCache.put(key, v);
    return v;
}

3) 운영 체크리스트

  • 락 키 TTL이 너무 길면 장애 유발
  • 조기 만료 확률/시간은 트래픽 패턴에 맞게 튜닝
  • 핫 키는 별도 모니터링 (Top N 키, hit/miss율)

4) 실무 설계 팁

  1. 락 없는 캐시 재생성은 소규모 트래픽에만 허용
  2. 락 + 조기만료 조합이 가장 현실적
  3. Redis 장애 대비를 위해 fallback(DB 타임아웃/서킷브레이커)도 필요

요약

  • Cache Stampede는 TTL 만료 시점의 동시 재생성 폭발이다.
  • 분산 락, 조기 만료, 이중 캐시는 서로 보완 관계다.
  • 락 해제는 반드시 소유자 토큰 검증으로 안전하게 처리해야 한다.

연습(추천)

  • 인기 키의 TTL을 5초로 줄여 스탬피드 상황을 재현해보기
  • 조기 만료 확률을 바꿔가며 DB QPS 변화를 측정해보기
  • 로컬 캐시(L1) 유무에 따라 Redis 부하 차이를 비교해보기