소프트웨어 아키텍처

캐싱 전략

meellon 2026. 7. 5. 05:20

학습 목표

캐시읽기 지연·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 hitsub-ms
read replica lag 체감 앱 근처 응답
hot key DB 집중 분산 메모리 흡수
  • Source of truthDB — 캐시는 파생, 버릴 수 있음
  • 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:v17immutable 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