학습 목표
캐시가 읽기 지연·DB 부하를 줄이는 아키텍처 패턴임을 설명할 수 있다.
Cache-aside·write-through·write-behind 차이와 선택 기준을 설명할 수 있다.
TTL·invalidation·스탬피드 방어를 설계할 수 있다.
Redis 등 분산 캐시를 MSA·polyglot 맥락에 배치할 수 있다.
문제 상황
- 상품 상세 API p99 420ms — PostgreSQL 동일 쿼리 매 요청
- 프로모션 시작 동시 3만 req — DB connection 고갈, 캐시 미스만 연쇄
- Redis TTL 5분인데 가격 변경 후 4분간 구 가격 노출
- 캐시 무효화 없이 DB만 update — 영원히 stale
- write-through 도입 후 쓰기 p99 2배 — 읽기만 캐시하면 됐는데
- MSA 5서비스가 같은 Redis key prefix 충돌 — catalog가 order key 덮어씀
저장소 선택까지 봤다. 읽기를 빠르게·저렴하게 만드는 캐시 계층을 의도적으로 설계하는 편이다.
1. 왜 캐시인가
캐시 — 자주 읽고 변경 적은 데이터를 빠른 저장소에 복사.
| 없을 때 | 캐시 후 |
|---|---|
| DB·API 매번 hit | hit 시 sub-ms |
| read replica lag 체감 | 앱 근처 응답 |
| hot key DB 집중 | 분산 메모리 흡수 |
- Source of truth는 DB — 캐시는 파생, 버릴 수 있음
- OS page cache·DB buffer pool — 시스템 캐시; 여기서는 애플리케이션·분산 캐시
- 17편 Redis — session·lookup; 이번 편은 패턴·일관성
2. Cache-aside (lazy loading)
Cache-aside — 앱이 직접 캐시 read/write. 가장 흔함.

Read
1. GET cache key
2. hit → return
3. miss → read DB → SET cache (TTL) → return
Write (일반)
1. UPDATE DB (source of truth)
2. DELETE cache key (or update — 팀 규칙)
| 장점 | 단점 |
|---|---|
| 앱 제어·단순 | miss 시 지연·DB spike |
| 캐시 장애 시 DB fallback | stale·invalidation 직접 |
| 읽기 heavy에 적합 | write-heavy면 다른 패턴 검토 |
# conceptual cache-aside read
get_product:
key: "catalog:product:{id}"
ttl: 300s
on_miss: load_from_postgres
on_write: invalidate_key
3. 다른 쓰기·읽기 패턴

| 패턴 | 흐름 | 언제 |
|---|---|---|
| Cache-aside | 앱이 cache·DB 각각 | 기본, read heavy |
| Read-through | cache 미스 시 cache lib가 DB 로드 | 라이브러리 일원화 |
| Write-through | write 동시 cache + DB | 강한 read-after-write |
| Write-behind | cache 먼저, DB 비동기 flush | 쓰기 burst·내구성 트레이드오프 |
- Write-through — 쓰기 지연↑, 일관 read ↑
- Write-behind — 손실·순서 리스크, 고성능 ingest
- 대부분 cache-aside + invalidate로 충분
4. TTL과 키 설계
TTL(Time To Live) — 캐시 자동 만료. invalidation 없어도 eventually 갱신.
| 전략 | 예 |
|---|---|
| 고정 TTL | product 5m, session 30m |
| Jitter | TTL ± random — 동시 만료 방지 |
| 계층 | L1 local + L2 Redis |
| 키 네임스페이스 | {service}:{entity}:{id} |

catalog:product:42
order:cart:user-9f3a
- TTL만 믿기 — 가격·재고는 짧은 TTL + 명시 invalidation
- 무한 TTL — 메모리 leak·영구 stale
- hot key — 단일 key에 전체 트래픽 → shard·local cache
5. Invalidation
Invalidation — DB 변경 시 캐시를 맞춤.
| 방식 | 설명 |
|---|---|
| Delete on write | update 후 DEL key — 단순 |
| Version in key | product:42:v17 — immutable cache entry |
| Pub/Sub broadcast | catalog update → 모든 pod DEL |
| CDC / event | outbox·CDC → consumer invalidates |
- Delete vs update cache — delete가 안전 (partial update 실수 방지)
- Multi-key — 주문 변경이 product·list·search 캐시 동시 영향 → 태그 invalidation
- Race — read miss가 옛 DB 값을 다시 cache — TTL·version·lock 검토
6. 스탬피드·내고장성
| 문제 | 증상 | 완화 |
|---|---|---|
| Cache stampede | TTL 동시 만료 → DB 폭주 | jitter·singleflight·probabilistic early refresh |
| Thundering herd | miss 연쇄 | mutex per key·prewarm |
| Redis down | 전체 느림 | fail open (DB direct)·breaker |
| Big value | 네트워크·GC | 압축·필드 분리 |
- 16편 bulkhead — cache pool·DB pool 분리
- Negative cache — 없는 id도 짧은 TTL로 cache (DB 반복 방지)
7. Redis 실무
| 용도 | 구조 |
|---|---|
| String JSON | product blob |
| Hash | 필드별 partial |
| Set / ZSet | ranking·feed |
| Pub/Sub | invalidation fan-out |
| 체크 | 내용 |
|---|---|
| Eviction | maxmemory-policy (allkeys-lru 등) |
| Persistence | cache는 대부분 RDB/AOF 선택 |
| Cluster | slot·hot key 모니터 |
| 보안 | TLS·ACL·민감 데이터 금지 |
- Session·rate limit·cache 역할 분리 — DB index 다르게
- 관측 — hit rate·miss latency·memory·evicted keys
8. 실무 체크리스트
| 질문 | 좋은 답 |
|---|---|
| 진실은? | DB only, cache 재구축 가능 |
| 패턴? | read heavy → cache-aside |
| TTL? | 도메인별 문서화 + jitter |
| Write 후? | invalidate 규칙 |
| 스탬피드? | singleflight·early refresh |
| 장애? | DB fallback·degraded OK? |
| 조합 | 효과 |
|---|---|
| cache-aside + TTL + delete-on-write | 실무 기본 |
| Redis + CDN | 정적·API edge |
| event invalidation | multi-instance 일관 |
9. 정리
- Cache-aside — 앱이 cache·DB 오케스트레이션, 기본
- TTL — 만료 + jitter, stale 허용 범위 명시
- Invalidation — write 시 delete, multi-key·event 고려
- 스탬피드 — 동시 만료·miss 방어
- Redis — 분산 캐시·session, namespace·eviction
- 다음 — 이벤트 스트리밍과 CDC
다음에 다룰 것
- 이벤트 스트리밍과 CDC
- Kafka, outbox, eventual consistency 실무
해당 내용은 Fundamentals of Software Architecture (Mark Richards, Neal Ford) 및 Designing Data-Intensive Applications (Martin Kleppmann) 의 내용을 기반으로 합니다.
'소프트웨어 아키텍처' 카테고리의 다른 글
| 아키텍처 문서화 (0) | 2026.07.07 |
|---|---|
| 이벤트 스트리밍과 CDC (0) | 2026.07.06 |
| 데이터 저장소 선택 (0) | 2026.07.04 |
| 내고장성과 복원력 (0) | 2026.07.03 |
| 동기 vs 비동기 통신 (0) | 2026.07.02 |