Skip to content

07 — Caching Strategies

Caching stores frequently accessed data in a fast, temporary storage layer so future requests can be served faster. It’s one of the most effective ways to improve system performance and reduce database load.

Analogy: Caching is like keeping your most-used tools on your workbench instead of walking to the tool shed every time. The workbench is fast (cache), the shed is slow (database).


Without caching:

  • Every request hits the database, increasing latency and load
  • Repeated queries for the same data waste resources
  • Database becomes bottleneck under high traffic
  • Response times degrade as data grows

flowchart LR
User["User Request"] --> App["Application Server"]
App --> Cache["Cache Layer<br/>Redis / Memcached / CDN"]
Cache -->|"Cache Hit ✅"| App
Cache -->|"Cache Miss ❌"| DB["Database"]
DB --> App
App --> User
App -->|"Update cache"| Cache
style User fill:#f59e0b,color:#fff
style App fill:#3b82f6,color:#fff
style Cache fill:#7c3aed,color:#fff
style DB fill:#059669,color:#fff

Cache TypeStorageSpeedPersistenceUse Case
In-Memory CacheRAMNanosecondsNo (volatile)Redis, Memcached
CDN CacheEdge serversMillisecondsYes (distributed)Static assets, media
HTTP CacheBrowser/CDNVariesConfigurableBrowser caching headers
Database CacheDB buffer poolMillisecondsYesQuery results, indexes
Application CacheApp memoryMicrosecondsNoLocal hot data

flowchart TB
Strategies["Caching Strategies"] --> CacheAside["Cache-Aside<br/>App checks cache first,<br/>then database"]
Strategies --> ReadThrough["Read-Through<br/>Cache layer handles DB queries<br/>transparently to app"]
Strategies --> WriteAround["Write-Around<br/>Write to DB, invalidate cache<br/>Read next time"]
Strategies --> WriteThrough["Write-Through<br/>Write to cache AND DB<br/>Always consistent"]
Strategies --> WriteBack["Write-Behind<br/>Write to cache first,<br/>async write to DB later"]
Strategies --> RefreshAhead["Refresh-Ahead<br/>Cache auto-refreshes<br/>before expiration"]
style Strategies fill:#7c3aed,color:#fff
style CacheAside fill:#3b82f6,color:#fff
style ReadThrough fill:#059669,color:#fff
style WriteAround fill:#f59e0b,color:#fff
style WriteThrough fill:#ef4444,color:#fff
style WriteBack fill:#6366f1,color:#fff
style RefreshAhead fill:#10b981,color:#fff
StrategyRead SpeedWrite SpeedConsistencyComplexity
Cache-AsideFastModerateEventualLow
Read-ThroughFastSame as DBEventualMedium
Write-ThroughFastSlower (write to both)StrongLow
Write-BehindFastVery fast (async)Eventual (risk of loss)High
Refresh-AheadFastSame as strategyEventualHigh

sequenceDiagram
participant App as Application
participant Cache as Redis Cache
participant DB as Database
App->>Cache: GET user:123
Cache-->>App: MISS
App->>DB: SELECT * FROM users WHERE id=123
DB-->>App: { name: "Alice" }
App->>Cache: SET user:123 { name: "Alice" } TTL 3600
App-->>User: Response
Note over App,DB: Next request for same user...
App->>Cache: GET user:123
Cache-->>App: { name: "Alice" } ✅ HIT
App-->>User: Fast response

PolicyDescriptionBest For
LRU (Least Recently Used)Remove oldest accessed itemsGeneral purpose
LFU (Least Frequently Used)Remove least accessed itemsStable access patterns
FIFO (First In First Out)Remove oldest written itemsStreaming, logs
TTL (Time To Live)Remove after fixed timeTime-sensitive data
RandomRandom evictionSimple caching
MaxmemoryEvict when memory fullAlways defined as fallback

The hardest problem in caching — when to update or remove cached data.

ApproachHow It WorksRisk
TTL-basedExpire after fixed timeStale data until TTL expires
Write-invalidateDelete cache when DB updatesTemporary extra DB load
Write-updateUpdate cache when DB updatesCache and DB must be updated together
Version-basedCache key includes version (e.g., user:123:v2)Old cache lingers until TTL

flowchart TB
User["🌍 User"] --> Edge["CDN Edge Server<br/>Closest to user"]
Edge -->|"Cache MISS"| Origin["Origin Server<br/>Primary data source"]
Origin --> Edge
Edge --> User
subgraph EdgeStorage["Edge Cache"]
Static["Static assets<br/>Images, CSS, JS<br/>Long TTL"]
Dynamic["Dynamic content<br/>HTML pages, API<br/>Short TTL"]
end
style User fill:#f59e0b,color:#fff
style Edge fill:#7c3aed,color:#fff
style Origin fill:#3b82f6,color:#fff
CDN Cache LevelTTLExample
Browser cache1 year (versioned assets)Cache-Control: max-age=31536000
CDN edge cache1 day (static)CloudFront, Cloudflare
CDN edge cache60 seconds (dynamic)API responses, HTML
Origin cacheVariesApplication cache layer

DecisionProsCons
Cache everythingFastest possible responsesStale data risk, memory cost
Cache nothingAlways fresh dataSlow, high DB load
Long TTLHigh hit ratePotentially stale
Short TTLFresher dataHigher cache miss rate
Local cache (app memory)Fastest (no network)Not shared across servers

StrategyDescription
Redis ClusterDistributed caching across nodes
Multi-layer cacheL1 (app memory) → L2 (Redis) → L3 (CDN)
Cache warmingPre-populate cache on deploy or after invalidation
Read replicas for cache missesUse read replicas for cache misses to reduce primary DB load
Geo-distributed cacheRegional Redis clusters for global apps

  1. What’s the difference between cache-aside and read-through caching?
  2. How would you invalidate a cache when data is updated?
  3. What eviction policy would you choose for a social media feed?
  4. How do you handle cache stampede (thundering herd) when cache expires?
  5. Design the caching strategy for a product catalog with millions of products.

SystemCaching Strategy
TwitterRedis for timeline, Memcached for user data, CDN for media
FacebookTAO (graph cache), Memcached distributed across regions
YouTubeCDN-first (Google Global Cache), edge caching for popular videos
AmazonMulti-layer: browser → CDN → ElastiCache → DB read replicas

  • Cache = store frequent data in fast memory — reduces latency by 10-100x
  • Cache-Aside is the most common pattern — app checks cache first, falls back to DB
  • TTL is your simplest invalidation strategy — data auto-expires after a fixed time
  • CDN caches at the network edge — critical for fast global content delivery
  • Cache stampede happens when many requests miss simultaneously — use locking or refresh-ahead
  • Don’t cache everything — cache data that’s read frequently and written infrequently