ayushsalampuriya.xyz Revise

System Design · Core

Caching

Put hot data closer to the CPU: layers, read/write strategies, eviction, stampede, hot keys, and Redis vs Memcached.

1. Why cache

A cache stores a subset of data in a faster medium (usually RAM) so repeated reads avoid the database or a remote service. You trade freshness and operational complexity for latency and QPS.

Without cache: Client → App → DB (every time) With cache: Client → App → Cache (hit) → done → DB (miss) → fill cache

2. Cache layers

LayerWhereTypical data
Client / browserUser deviceStatic assets, HTTP cache
CDN edgePoP near userImages, JS, video segments
App localProcess memoryConfig, tiny hot maps (careful: inconsistency)
DistributedRedis / MemcachedSessions, profiles, feed pages
DB buffer poolInside DBPages / blocks (transparent)

In interviews, draw at least CDN + distributed cache + DB. Mention local in-process cache only if you discuss invalidation across instances.

3. Read/write strategies

Cache-aside (lazy loading)

read(key): v = cache.get(key) if v: return v v = db.read(key) cache.set(key, v, ttl) return v write(key, v): db.write(key, v) cache.delete(key) # or set; delete is safer with races

Most common interview default. App owns the logic. Risk: stampede on miss.

Write-through

Write hits cache and DB together (cache library or proxy). Reads always see warm data for written keys. Higher write latency; good when read path must stay simple.

Write-back (write-behind)

Write to cache; flush to DB asynchronously. Fast writes, risk of data loss on crash unless you have WAL/replication on the cache tier. Rare for user-critical data; more common for counters/metrics buffers.

Read-through

Cache itself loads from DB on miss (app only talks to cache). Similar to cache-aside with the loader living in the cache layer.

Refresh-ahead

Before TTL expires, asynchronously refresh hot keys so users rarely see a cold miss. Needs a predictor of “hot” keys (access counters, known catalogs). Extra write/load traffic; great for predictable popular items.

StrategyRead pathWrite pathWhen to pick
Cache-asideApp loads on missDB then invalidate/updateDefault interview answer
Write-throughUsually warm after writesCache + DB togetherNeed simple reads after every write
Write-behindFrom cacheCache first; async DBWrite speed > durability risk OK
Refresh-aheadProactive reload before expirySame as aside/throughPredictable hot keys, avoid stampede

4. Eviction: LRU, LFU, TTL

PolicyEvictsGood for
LRULeast recently usedGeneral working sets with temporal locality
LFULeast frequently usedStable popularity (catalog bestsellers)
TTLExpired keysBounding staleness; combine with LRU
Random / noevictionTiny caches or fail-on-full (Redis noeviction)

Always set a TTL even with LRU so deleted DB rows do not live forever under a rare key. Size the cache for your hot set, not the full dataset.

Data typeTTL guidelineWhy
Session / auth token metaMinutes to session lengthSecurity + logout correctness
User profile / product pageMinutes to hoursRead-heavy; short stale OK
Feed page / ranked listSeconds to few minutesFreshness matters more
Config / feature flagsSeconds to minutesFast rollout; small objects
CDN static hashed assetsDays / immutableContent-addressed filenames

5. Cache stampede (thundering herd)

Popular key expires; thousands of requests miss and hammer the DB.

Mitigations: 1) Soft TTL + background refresh (serve stale while one worker reloads) 2) Request coalescing / singleflight (one loader per key) 3) Probabilistic early expiration (XFetch) 4) Lock around miss: only lock holder loads DB

6. Hot keys and large values

7. Cache penetration

Attackers (or bugs) request keys that never exist in the DB. Every miss falls through → DB overload. Different from stampede (which hits a real hot key after expiry).

Mitigations: 1) Bloom filter in front: "definitely not present" → 404 without DB 2) Cache negative results (null / empty) with short TTL 3) Auth + rate limit abusive key patterns 4) Validate key space (UUIDs, signed ids) before lookup

8. Local vs distributed cache

Local (in-process)Distributed (Redis/Memcached)
LatencyMicrosecondsSub-ms to few ms network
ConsistencyPer instance; hard to invalidate fleet-wideShared view across apps
CapacityLimited by one process RAMCluster RAM; scalable
Use forConfig, tiny hot maps, L1 in front of RedisSessions, shared hot objects

9. Consistency pitfalls

  1. Update DB then fail before invalidating cache → stale forever until TTL.
  2. Invalidate then write DB; concurrent reader refills old value → use versioning or short TTL.
  3. Multi-key invariants (account + balance) → do not cache partial aggregates without care.

10. Cache technologies

TechRoleNotes
RedisDistributed cache / data structuresDefault interview pick; Cluster, persistence optional
MemcachedSimple distributed blob cacheVery fast; client-side sharding typical
VarnishHTTP reverse-proxy cacheEdge/origin HTTP caching; VCL rules
CDN (CloudFront, etc.)Geo edge cacheStatic + some dynamic; see CDN notes
Caffeine / GuavaIn-process local cacheL1 in front of Redis

Default interview answer for “distributed cache”: Redis, unless the problem is purely “dumb” object cache at massive QPS. Mention Varnish when the cache key is an HTTP URL at the reverse-proxy layer.

11. Performance impact (why bother)

PathBallpark latencyEffect
DB primary read1–10+ ms (disk/lock)Limits QPS; expensive under load
Redis GET (same AZ)~0.2–1 ms10×–100× cheaper than cold DB
Local L1 hit<0.01 msRemoves network hop for ultra-hot keys
CDN edge hittens of ms RTT to userOffloads origin entirely for static

12. What to say in a design

“Cache-aside Redis in front of the primary store for read-heavy keys, TTL plus delete-on-write, singleflight against stampede, negative cache or bloom filter against penetration, and CDN for static. Measure hit rate and p99; size RAM for the hot set.”

Quick revision

  • Layers: browser → CDN → app → Redis → DB buffer.
  • Strategies: aside / through / behind / refresh-ahead — pick with a table.
  • TTL by data type; LRU/LFU for capacity.
  • Stampede ≠ penetration; blooms + null cache for missing keys.
  • Local L1 vs distributed: latency vs shared invalidation.
  • Redis / Memcached / Varnish / CDN — name the layer.
  • Invalidation races are the hard part — name them.