Redis 캐시 전략과 실패 모드
Cache-aside, TTL, 무효화와 Stampede 대응을 정합성 요구에 맞게 선택한다.
이 문서의 목차
Overview
캐시 전략은 “어디서 읽고 언제 쓰며 누가 지우는가”를 정하는 규칙이다. Redis 도입보다 중요한 것은 원본 데이터와 캐시가 잠시 달라도 되는 시간, Miss 때 원본이 감당할 부하, 실패 시 우회 경로를 합의하는 일이다.
한 문장 설명: Cache-aside는 애플리케이션이 캐시를 먼저 보고 Miss일 때 DB에서 읽어 TTL과 함께 채우는 방식이다.
핵심 용어
| 용어 | 설명 |
|---|---|
| Hit / Miss | Cache에서 즉시 찾음 / 원본 조회 필요 |
| Invalidation | 원본 변경 뒤 오래된 Key 제거 |
| TTL | 자동 만료 안전망 |
| Stale Window | 오래된 값이 노출될 수 있는 시간 |
왜 필요한가
같은 조회를 반복 실행하면 DB CPU와 I/O가 낭비된다. 반대로 무효화 규칙 없이 캐시하면 오래된 가격이나 권한이 노출된다. 전략은 성능과 최신성 사이의 허용 범위를 코드와 운영 지표로 만든다.
핵심 원리
Cache-aside에서는 읽기 Miss 시 DB 조회 후 캐시에 저장하고, 쓰기 성공 뒤 관련 Key를 삭제한다. Write-through는 쓰기 경로에서 캐시와 저장소를 함께 갱신하고, Write-behind는 캐시에 먼저 쓴 뒤 비동기로 원본에 반영한다. 후자로 갈수록 쓰기 응답은 빨라지지만 유실과 순서 제어가 어려워진다.
내부 동작
sequenceDiagram; participant A as App; participant R as Redis; participant D as DB; A->>R: GET key; alt Hit; R-->>A: value; else Miss; R-->>A: null; A->>D: SELECT; D-->>A: row; A->>R: SET key EX ttl; end
TTL은 안전망이지 정확한 무효화 시점이 아니다. 쓰기 후 삭제가 실패할 수 있으므로 재시도나 이벤트 기반 무효화가 필요하다. 동일 Key Miss가 몰리면 하나의 요청만 원본을 조회하도록 짧은 Lock 또는 Request Coalescing을 적용한다.
쉬운 비유
캐시는 식당의 오늘 메뉴판과 같다. 메뉴판을 빨리 보는 것은 좋지만 주방의 실제 재고가 바뀌었을 때 누가 언제 고칠지 정하지 않으면 잘못된 주문을 받는다.
Example
const cached = await redis.get(key)
if (cached) return JSON.parse(cached)
const value = await repository.find(id)
await redis.set(key, JSON.stringify(value), { EX: 300 })
return value실제 구현에서는 300초에 작은 무작위 값을 더해 동시 만료를 분산하고, Not-found도 짧게 Negative Cache해 반복 공격을 막는다.
실무에서 발생하는 문제
Stampede는 인기 Key 만료 순간 DB 요청이 폭증하는 현상이다. Penetration은 존재하지 않는 Key가 캐시를 통과하는 현상이고, Avalanche는 많은 Key 또는 Redis 장애가 한꺼번에 DB 부하로 전환되는 상황이다. Hot Key는 한 Node의 Network와 CPU를 집중시킨다.
Trade-off
긴 TTL은 Hit Ratio를 높이지만 Stale 시간이 길어진다. 짧은 TTL은 최신성을 높이지만 DB 부하가 늘어난다. 분산 Lock은 중복 조회를 줄이지만 Lock 대기와 장애 처리가 추가된다. 데이터 중요도별로 다른 정책을 쓴다.
흔한 오해
- TTL이 짧으면 항상 안전한 것이 아니다. 인기 Key 동시 만료는 더 자주 발생한다.
- 쓰기 후 캐시 갱신보다 삭제가 단순하지만 삭제 실패는 여전히 처리해야 한다.
- Cache Miss는 예외가 아니라 정상 경로이므로 원본이 감당 가능해야 한다.
Production Considerations
Key별 Hit/Miss, Miss 시 DB Latency, Eviction, Timeout, Fallback 비율을 측정한다. TTL Jitter, 요청 합치기, 사전 갱신 중 필요한 것만 적용한다. Redis 전체 장애와 Cold Cache 재기동 상황을 부하 테스트하고 Circuit Breaker로 연쇄 장애를 제한한다.
다른 사람에게 설명한다면
30초: “Cache-aside는 Redis를 먼저 보고 Miss면 DB에서 읽어 채웁니다. 쓰기는 DB Commit 후 Cache를 지우며, 삭제 실패와 동시 Miss 때문에 TTL·재시도·Single Flight가 필요합니다.”
2분: Hit/Miss와 Commit→Evict 순서를 그리고, 오래된 값이 다시 채워지는 Race, Stampede, Cold Cache 보호까지 설명한다.
Interview Questions / Follow-up Questions
- Cache-aside에서 쓰기 후 갱신 대신 삭제를 택하는 이유는?
- Stampede, Penetration, Avalanche의 차이는?
- 후속: 캐시 삭제가 실패했을 때 정합성을 어떻게 회복할 것인가?
- 후속: 권한 정보와 상품 목록의 TTL을 다르게 정해야 하는 이유는?
Related Topics
Redis 운영 지도, Latency와 Throughput, 부하 테스트와 함께 읽는다.
SOURCE REFERENCES
이 문서의 근거
본문은 Dev Atlas 안에서 완결되며, 검증이 필요할 때만 원문을 확인할 수 있습니다.