Caching means keeping a copy of data somewhere faster or closer than its source, so repeated reads don't pay the full cost. Memory is roughly a thousand times faster than an SSD read and far cheaper than recomputing a result or calling another service, so a cache that answers most reads can cut latency and database load dramatically.
In simple words
You keep the things you use daily (phone, keys, wallet) on the table by the door instead of in the storeroom. Grabbing them is instant. The catch: if something in the storeroom changes, the copy on the table can be out of date.
1. Look in the cache. 2. On a miss, read the database. 3. Put the value in the cache for next time.
Why It Exists
Most systems are read-heavy and their access is skewed: a small fraction of items (popular products, a viral post, today's feed) gets most of the reads. Keeping that hot set in memory serves most traffic cheaply.
Where Caches Live
browser cache -> CDN edge -> reverse proxy / gateway cache
-> application in-process cache (per server, tiny, fastest)
-> distributed cache (Redis / Memcached, shared by all servers)
-> database buffer pool (the DB's own page cache)
How It Works: Common Patterns
Cache-aside (lazy loading): app reads cache; on a miss it reads the DB and writes the value into the cache. Most common; the cache only holds what's asked for.
Read-through: the cache library itself loads from the DB on a miss.
Write-through: every write goes to the cache and the DB together. Cache stays fresh; writes are slower.
Write-back (write-behind): write to the cache, flush to the DB later. Fast writes; risk of losing data if the cache dies.
Write-around: writes go to the DB only; the cache fills on later reads.
cache-aside read:
v = cache.get(k)
if v is None:
v = db.read(k)
cache.set(k, v, ttl=300)
return v
on update: db.write(k, new); cache.delete(k) # delete, don't set
The Hard Parts
Invalidation: after the source changes, stale copies must expire or be deleted. TTLs bound staleness; explicit deletes on write make it short.
Eviction: memory is finite; LRU (least recently used) or LFU (least frequently used) decides what goes.
Stampede (thundering herd): a hot key expires and thousands of requests hit the DB at once. Fixes: request coalescing (one loader, others wait), early refresh, jittered TTLs.
Hot keys: one key overloads one cache node; replicate it or keep a local copy.
Key Pointers
Cache what is read often, changes rarely, and is expensive to produce.
Cache-aside with a TTL and delete-on-write is the default answer.
Every cache adds staleness; say how much is acceptable.
Hit ratio is the key metric; a cache with a low hit ratio just adds a hop.
Follow-up Questions
Why delete the cache entry on update instead of writing the new value?
Two concurrent updates can race so the cache ends up holding the older value indefinitely. Deleting makes the next read load the current value from the DB (a small race still exists, which a TTL bounds).
The Redis cluster restarts empty. What happens?
Every read misses and hits the DB at once, which may not survive the load. Mitigate with gradual warm-up, request coalescing, rate limiting to the DB, and replicas so a single node failure doesn't empty the cache.
In-process cache or Redis?
In-process is fastest but per-server (duplicated, inconsistent across servers). Redis is shared and consistent across servers at a network hop's cost. Many systems use a tiny local cache for the hottest keys in front of Redis.