인기 키가 만료된 바로 그 순간 수많은 요청이 동시에 미스를 겪고 한꺼번에 DB로 몰리는 현상. Thundering Herd(우르르 몰려드는 무리), Dog-pile이라고도 한다.
어떻게 무너지는가
- t=0.000 — 키 만료
- t=0.001 — 요청 1000건 도착 → 전부 캐시 미스
- t=0.002 — 1000건이 동시에 DB로 → 커넥션 풀(예: 50개) 즉시 고갈
- t=0.05 — DB 응답 지연 → 대기 큐 증가
- t=0.1 — 타임아웃 → 재시도 → 부하 가중
- 캐시는 여전히 비어 있음 → 악순환
핵심은 평소엔 DB가 초당 1건만 받다가 갑자기 1000건을 받는다는 것이다. 캐시가 있어서 용량을 작게 잡아 뒀기에 더 크게 무너진다.
해결 수단 네 가지
① TTL 지터 — 가장 값싸고 효과적
int ttl = 300 + random.nextInt(60); // 300~360초로 흩뜨림
같은 시각에 채워진 키들이 같은 시각에 만료되는 것이 원인이므로, 만료 시점을 흩으면 몰림 자체가 사라진다.
② 뮤텍스 / 분산 락
미스 발생 → 락 시도
- 획득 성공 → 나만 DB 조회 후 캐시 채움
- 획득 실패 → 잠깐 대기 후 캐시 다시 읽기 DB로 가는 요청을 1건으로 줄인다. 대신 나머지 요청은 잠시 기다린다.
③ 조기 재계산(early recomputation)
만료 시각이 다가오면 확률적으로 일부 요청이 미리 갱신한다. 만료 전에 갱신되므로 미스 자체가 안 생긴다.
④ stale-while-revalidate
만료돼도 낡은 값을 일단 돌려주고 백그라운드에서 갱신한다. 사용자는 기다리지 않고 DB는 1건만 받는다. HTTP 캐시 헤더에도 같은 개념이 있다.
비슷하지만 다른 문제들
| 이름 | 상황 | 대응 |
|---|---|---|
| 스탬피드 | 인기 키 하나가 만료 | 지터·락·조기 갱신 |
| 캐시 관통(penetration) | 존재하지 않는 키를 계속 조회(공격) | 빈 결과도 짧게 캐싱, 블룸 필터 |
| 캐시 붕괴(avalanche) | 대량의 키가 동시에 만료·캐시 서버 다운 | 지터, 다중화, 서킷 브레이커 |
면접 함정
"TTL을 길게 하면 되지 않나?" → 만료 시점이 미뤄질 뿐 동시에 만료된다는 구조는 그대로다. 해결하려면 동시성을 깨야 한다(지터·락).