⚠️ 캐시 스탬피드(Cache Stampede) 현상과 해결 전략
🧨 캐시 스탬피드란?
캐시 스탬피드(Cache Stampede) 또는 Thundering Herd Problem은
캐시 미스 시 다수의 요청이 동시에 원본 저장소(DB) 를 조회하여 병목이 발생하는 현상입니다.
위 그림처럼 여러 사용자가 동시에 요청을 보내고, 해당 캐시 키가 만료되어 있는 경우,
모든 요청이 동시에 DB로 몰리는 현상이 발생합니다.
결과적으로 데이터베이스에 급격한 부하가 걸리고, 장애나 지연이 발생할 수 있습니다.
🧩 원인 정리
- Cache Aside 전략 사용 중 캐시 미스
- 캐시 TTL이 만료되거나 제거됨
- 동시에 수많은 요청이 DB에 접근하여 적재
🔧 해결 방법
1️⃣ 잠금(Locking) 방식
- 최초 1개의 요청만 DB에 접근하도록 하고, 나머지는 대기
- 캐시 적재 후 나머지는 캐시에서 응답
- Redis의
SETNX
또는 분산 락 활용 가능
if (redis.setnx(key + ":lock", "1")) {
// DB 조회 및 캐시 적재
redis.set(key, value, 60);
redis.del(key + ":lock");
} else {
// 대기 또는 fallback 처리
}
단점: 락 점유 실패, 데드락, 타임아웃 고려 필요
2️⃣ 외부 재계산(Background Refill)
- 별도의 백그라운드 스레드나 크론 잡이 주기적으로 캐시를 갱신
- 사용자 요청 시 캐시가 항상 존재할 확률이 높아짐
✔️ 장점: 사용자 요청이 DB에 직접 접근하는 일 없음
❌ 단점: 사용되지 않는 데이터도 갱신 → 리소스 낭비
3️⃣ 확률적 조기 재계산 (Probabilistic Early Recompute)
- TTL이 얼마 안 남았을 때 일부 요청만 캐시를 재적재
- 캐시 만료 시간이 임박하면 확률적으로 갱신 시도
# 예시: 확률적 재적재 조건 (Python)
if (current_time > expire_time - threshold):
if (random() < probability):
refresh_cache()
➡️ 스탬피드 발생 가능성을 확률적으로 분산시킴
🔁 실무 적용 팁
- TTL + 랜덤 편차 적용해 캐시 만료 시점 분산
- Redis 기반 Lock 구현 시 Redisson, Lettuce 고려
- 외부 재계산 방식은 유사 CDN, 검색 캐시에 적합
- 가능하면 읽기 요청 분산을 위해 복제 DB 또는 CQRS 아키텍처도 활용
🧠 마무리 정리
전략 | 장점 | 단점 |
---|---|---|
Locking | 단일 요청만 DB 접근 | 락 관리 복잡 |
외부 재계산 | 안정적 응답 | 리소스 낭비 가능 |
확률적 재계산 | 실시간 대응 + 분산 | TTL 조정, 튜닝 필요 |
댓글남기기