Skip to main content

🗄️ Caching

Keeping snacks on your desk

The Refrigerator Analogy

Without a fridge:

  • Go to store every time you need milk
  • Takes a long time each trip!

With a fridge:

  • Go to store once
  • Keep milk nearby
  • Takes only moments to grab it

Caching is a refrigerator for data. Store frequently used data nearby for faster access.


What Is Caching?

Cache = Temporary storage for frequently accessed data

First request:
  Client → Server → Database → Response (slow)
  Store in cache

Second request:
  Client → Cache → Response (fast!)
  Skip database entirely

The Trade-off

Speed vs Freshness

Cache hit: Data is there, instant response
Cache miss: Data not there, fetch from source

Stale data: Cache has old version
Fresh data: Cache has current version

Balance: Fast enough, fresh enough

Where Caching Happens

Multiple levels of caching:

┌─────────────────────────────────────────────────┐
│ Browser                                          │
│   └─ Browser cache (HTML, CSS, images)          │
├─────────────────────────────────────────────────┤
│ CDN (Edge)                                       │
│   └─ Geographic distribution                    │
├─────────────────────────────────────────────────┤
│ API Gateway                                      │
│   └─ Response caching                           │
├─────────────────────────────────────────────────┤
│ Application                                      │
│   └─ In-memory cache (Redis, Memcached)         │
├─────────────────────────────────────────────────┤
│ Database                                         │
│   └─ Query cache, buffer pool                   │
└─────────────────────────────────────────────────┘

Cache Strategies

Cache-Aside (Most Common)

1. Check cache
2. Cache miss? Fetch from database
3. Store in cache
4. Return response

App controls caching explicitly.

Write-Through

Write to cache AND database together.

Data is kept in sync (when all writes go through this path).
Slower writes (two writes).

Write-Behind (Write-Back)

Write to cache immediately.
Asynchronously sync to database.

Fast writes.
Risk: data loss if cache fails before sync.

Read-Through

Cache handles loading from database.
App talks to the cache, and the cache fetches from the database on misses.

Cache as the single point of access.

Cache Invalidation

The Hardest Problem

"A famous joke: there are two hard things in computer science:
cache invalidation and naming things."

When underlying data changes,
cache needs to know!

Strategies

Time-based (TTL):
  Entry expires after a set time
  Simple but may serve stale data

Event-based:
  Database updated → Invalidate cache
  Immediate but more complex

Version-based:
  Cache key includes version number
  New version = new key = fresh data

TTL (Time To Live)

Short TTL (very short):
  Fresh data
  More cache misses
  More database load

Long TTL (long):
  Stale data risk
  Fewer cache misses
  Less database load

Balance based on your needs.

Cache Keys

Key design matters!

Simple: "user:123"
With params: "user:123:profile:v2"
Hash: md5(request_url + params)

Good keys:
✓ Unique per data item
✓ Consistent generation
✓ Efficient lookup

ToolTypeGood For
RedisIn-memory storeMost use cases
MemcachedIn-memory cacheSimple key-value
VarnishHTTP cacheWeb acceleration
Browser cacheClient-sideStatic assets
CDNDistributedGlobal content

Cache Hit Ratio

Hit ratio = cache hits / total requests

90% hit ratio:
  90 requests served from cache
  10 requests go to database

Higher is better!
Monitor and optimize.

Improving Hit Ratio

✓ Longer TTL (if freshness allows)
✓ Preload popular items (cache warming)
✓ Better key design
✓ Larger cache size

Common Patterns

Cache Warming

Preload cache before traffic hits:

App starts → Load popular items into cache

Avoids cold start cache misses.

Distributed Cache

Multiple app servers share one cache:

App 1 ─┐
App 2 ─┼──→ Redis Cluster ──→ Database
App 3 ─┘

All servers see same cached data.

Local + Remote Cache

Two levels:

Request → Local cache (very fast)
        ↓ miss
        → Remote cache (fast)
        ↓ miss
        → Database (slow)

Local cache for hot data.
Remote cache for shared data.

Common Mistakes

1. Caching Everything

Cache what's:
✓ Expensive to compute
✓ Frequently accessed
✓ Stable enough

Don't cache:
✗ Rarely accessed data
✗ Constantly changing data
✗ User-specific infrequently accessed data

2. Ignoring Staleness

User changes profile.
Cache shows old profile.
User confused!

Plan for invalidation from the start.

3. No Cache Eviction Policy

Cache keeps growing forever.
Memory exhausted!

Set max size + eviction policy (LRU, LFU).

4. Cache Stampede

TTL expires.
1000 requests simultaneously:
  All miss cache.
  All hit database.
  Database overwhelmed!

Solution: Locking, early expiration, staggered TTL.

FAQ

Q: When NOT to cache?

Rapidly changing data, sensitive data, or low-frequency requests.

Q: Redis vs Memcached?

Redis: More features (data structures, persistence) Memcached: Simpler, multi-threaded

Q: How do I know if caching is working?

Monitor hit ratio, latency improvements, database load reduction.

Q: Can I cache database queries?

Yes! Many ORMs support query caching. Be careful with invalidation.


Summary

Caching stores frequently accessed data for faster retrieval, trading memory for speed.

Key Takeaways:

  • Cache speeds up repeated data access
  • Multiple levels (browser, CDN, app, database)
  • Strategies: cache-aside, write-through, etc.
  • Invalidation is the hard part
  • TTL for time-based expiration
  • Monitor hit ratio
  • Avoid cache stampede

Caching: the closest thing to free performance!

Related Concepts

Leave a Comment

Comments (0)

Be the first to comment on this concept.

Comments are approved automatically.