Caching in System Design: The Fundamentals
Table of Contents
System Design - This article is part of a series.
Caching in System Design: The Fundamentals #
Caching is one of the most important techniques used to improve the performance and scalability of distributed systems.
Almost every large-scale system uses caching somewhere.
A typical system might look like:
Client
↓
Browser Cache
↓
CDN
↓
Load Balancer
↓
Application
↓
Local Cache
↓
Distributed Cache
↓
Database
↓
Storage
The fundamental idea is simple:
Store frequently accessed data closer to where it is needed so that future requests can be served faster and more cheaply.
But caching is much more than adding Redis to an architecture.
A proper caching design requires us to answer questions such as:
- What should we cache?
- Where should we cache it?
- How long should we cache it?
- How do we know when cached data is stale?
- What happens when the cache is full?
- What happens when the cache goes down?
- How do we prevent millions of requests from hitting the database simultaneously?
- How do we distribute cached data across multiple machines?
- What consistency guarantees do we need?
- How much memory do we need?
This article builds the mental model required to answer those questions.
1. Why Do We Need Caching? #
Consider a simple application:
Client
↓
Application Server
↓
Database
Suppose the database takes 20 ms to execute a query.
Now imagine, 10,000 requests / second. If every request goes directly to the database - 10,000 database queries / second.
The database becomes the bottleneck.
But suppose 90% of those requests ask for the same data.
For example:
GET /products/123
Instead of querying the database every time, we can store the result in a cache:
+-------+
| Cache |
+-------+
↑
|
Client → Application
|
↓
Database
Now:
90% → Cache
10% → Database
The database might only need to handle around 1,000 requests / second, instead of 10,000 requests / second.
Caching provides three major benefits.
Lower latency #
Memory-based access is generally much faster than querying a database or making a remote service call.
Reduced load #
Requests served from the cache do not reach the underlying database.
Better scalability #
We can handle significantly more requests without scaling the database proportionally.
But caching also introduces a new problem:
We now have another copy of our data that must be managed correctly.
2. What Is a Cache? #
A cache is a temporary storage layer that stores data that can be retrieved or reconstructed from another source.
For example:
Application
↓
Redis
↓
PostgreSQL
PostgreSQL is the source of truth.
Redis contains a derived copy.
This distinction is extremely important.
Database
↓
Source of Truth
Cache
↓
Derived State
If Redis disappears, we should ideally be able to reconstruct its contents from PostgreSQL.
Therefore:
A cache should generally be disposable.
If losing the cache means permanently losing critical data, then the system is no longer simply using a cache. We are effectively treating the cache as a database.
3. The Core Caching Model #
A useful mental model is:
Request
↓
Cache
/ \
Hit Miss
↓ ↓
Return Source
↓
Cache
↓
Return
A cache hit occurs when the requested data is already available in the cache.
A cache miss occurs when it is not.
For example:
GET user:123
Cache hit #
Application
↓
Cache
↓
User data
Cache miss #
Application
↓
Cache
↓
MISS
↓
Database
↓
User data
↓
Cache
This simple flow forms the basis of many caching architectures.
4. Cache Hit and Cache Miss #
One of the most important metrics in caching is the cache hit rate.
Suppose we have:
900 cache hits
100 cache misses
Total requests:
1,000
Therefore:
Cache Hit Rate = 900 / 1000 = 90%
The cache hit rate tells us how often the cache successfully avoids accessing the underlying system.
The opposite is the cache miss rate:
Cache Miss Rate = 1 - Cache Hit Rate
So if:
Hit Rate = 90%
then:
Miss Rate = 10%
A high hit rate is generally desirable, but it should not be treated as the only measure of cache effectiveness.
For example:
99% hit rate
sounds excellent.
But if the remaining 1% represents extremely expensive database queries, the system may still have a serious performance problem.
We therefore need to look at:
- hit rate
- miss rate
- cache latency
- database load
- eviction rate
- memory usage
- request distribution
5. The Working Set #
A database might contain billions of records.
But users may only access a small percentage of them frequently.
For example:
Database
1 billion records
But perhaps:
10 million records
receive 95% of all requests.
Those frequently accessed records represent the system’s working set.
Database
│
├── Rarely accessed data
│
├── Rarely accessed data
│
├── Rarely accessed data
│
└── Frequently accessed data
↑
|
Working Set
A cache does not necessarily need to contain the entire database.
The goal is often to keep the most valuable part of the working set in memory.
This is one of the most important ideas in cache design.
You don’t necessarily need to cache everything. You need to cache the right things.
6. What Makes Data a Good Candidate for Caching? #
A good caching candidate usually has some combination of these characteristics:
Frequently Read
+
Expensive to Obtain
+
Relatively Stable
For example:
Product metadata #
Product ID
Name
Description
Price
Images
Frequently accessed and relatively stable.
Configuration #
Feature flags
Application configuration
Service configuration
Often read many times but changed infrequently.
Expensive computation #
Recommendation generation
Analytics aggregation
Machine learning prediction
If computing the same result repeatedly is expensive, caching can be extremely valuable.
7. What Is a Bad Candidate for Caching? #
Caching is not automatically beneficial.
Data may be a poor candidate when:
- it changes constantly
- it is accessed only once
- it is extremely large
- it has extremely high cardinality
- it requires strong consistency
- the cache entry costs more to maintain than the computation itself
For example: Current bank balance may have very different consistency requirements from Popular news article. The news article might tolerate 5 seconds of staleness, while a financial transaction may not.
Therefore:
The decision to cache is fundamentally a business and consistency decision, not just a performance decision.
8. Freshness vs Performance #
Caching creates a fundamental trade-off:
Freshness
↑
|
|
+----------------→ Performance
The more aggressively we cache, the less frequently we may contact the source of truth.
That improves performance.
But the cached value may become stale.
For example:
Database
Price = ₹100
Cache:
Price = ₹100
Then the database changes:
Database
Price = ₹120
But the cache still contains:
Price = ₹100
Now the cache is stale.
The system designer has to decide:
How much stale data can the business tolerate?
Possible answers might be:
0 seconds
1 second
10 seconds
5 minutes
1 hour
Eventually consistent
This requirement strongly influences the caching strategy.
9. Cache Location Matters #
Caching can happen at many different layers.
A complete request path could look like:
User
↓
Browser Cache
↓
CDN
↓
Load Balancer
↓
Application
↓
Local Cache
↓
Distributed Cache
↓
Database
↓
Disk
Each layer has different properties.
The closer the cache is to the consumer:
Lower latency
but generally:
Smaller capacity
For example:
CPU Cache
↓
Extremely fast
Very small
RAM
↓
Fast
Larger
SSD
↓
Slower
Much larger
Database
↓
Even more expensive
Persistent
This gives us a general principle:
Fast storage tends to be smaller and more expensive per byte. Slow storage tends to be larger and cheaper per byte.
Caching allows us to keep the most valuable data in the faster layer.
10. Local Cache vs Distributed Cache #
One of the first architectural decisions is where the cache should live.
Local Cache #
The cache lives inside the application process.
Application Server
|
v
Memory Cache
For example:
Map<String, User> cache;
Advantages:
- extremely low latency
- no network call
- simple
- no external infrastructure
But suppose we have three application servers:
+---------+ +---------+ +---------+
| App 1 | | App 2 | | App 3 |
| Cache | | Cache | | Cache |
+---------+ +---------+ +---------+
Each server has its own copy.
This creates several problems:
- duplicated memory
- inconsistent values
- independent cache warming
- difficult invalidation
- different hit rates
11. Distributed Cache #
A distributed cache is shared by multiple application instances.
+----------------+
| Redis Cluster |
+----------------+
↑ ↑ ↑
| | |
App1 App2 App3
All application instances can access the same cache.
Advantages:
- shared state
- larger aggregate capacity
- consistent cache population
- easier coordination
But now every cache access requires a network call.
Therefore:
Local Cache
↓
Fastest
Distributed Cache
↓
Fast, but network dependent
Database
↓
More expensive
12. Multi-Level Caching #
Large systems often use both local and distributed caches.
For example:
Request
↓
Local Cache
|
| HIT
↓
Return
|
| MISS
↓
Distributed Cache
|
| HIT
↓
Return
|
| MISS
↓
Database
This creates multiple layers:
L1 → Local Cache
L2 → Distributed Cache
L3 → Database
The hottest data can be kept in the local cache.
The broader working set can live in the distributed cache.
The database remains the source of truth.
13. Caching Is Not Just Redis #
When engineers hear “cache”, they often immediately think:
Redis
But caching is a much broader system-design concept.
Examples include:
CPU Cache
OS Page Cache
Browser Cache
CDN
Application Cache
Distributed Cache
Database Buffer Cache
DNS Cache
API Response Cache
Query Cache
Computation Cache
Semantic Cache
Redis is simply one technology used to implement a particular kind of cache.
The more important question is:
Where is the expensive operation happening, and can we avoid repeating it?
14. Caching as Memoization at Scale #
At a programming level, memoization means:
Input
↓
Expensive Function
↓
Result
Instead of recomputing:
Input
↓
Check previous result
↓
If available → Return
If unavailable → Compute → Store
Caching is essentially the same idea applied at system scale.
For example:
calculateRecommendation(user123)
might take:
500 ms
If the result is valid for five minutes, we can store:
recommendation:user123
and reuse it.
Therefore:
Caching is often system-level memoization.
15. Caching Reduces More Than Latency #
Caching is usually introduced because something is slow.
But the benefits go beyond latency.
Reduce database CPU #
Fewer queries need to be executed.
Reduce database connections #
Fewer requests need database connections.
Reduce network traffic #
Less data travels between services.
Reduce infrastructure cost #
Fewer database resources may be required.
Improve availability #
The application may continue serving cached data even when the source is temporarily unhealthy.
For example:
Database temporarily slow
↓
Cache still healthy
↓
Popular data continues to be served
However, this comes with the possibility of serving stale data.
16. The Cache as a Shock Absorber #
A useful way to think about caching is as a shock absorber between the application and expensive dependencies.
Without caching:
100,000 requests/sec
↓
100,000 database requests/sec
With caching:
100,000 requests/sec
↓
Cache
↓
95,000 requests served
↓
5,000 database requests
The cache absorbs a large portion of the traffic.
This protects the database from traffic spikes.
But there is an important caveat:
The cache itself can become a bottleneck if it is not designed for the traffic pattern.
This becomes especially important with distributed caches, hot keys, and cache failures.
17. Cache Latency #
When evaluating a cache, we should measure its latency.
For example:
Local Cache
↓
~nanoseconds / microseconds
Distributed Cache
↓
~sub-millisecond to milliseconds
Database
↓
milliseconds to tens/hundreds of milliseconds
The exact numbers depend heavily on:
- hardware
- network
- deployment topology
- payload size
- serialization
- database workload
The important concept is the relative difference.
If the cache is:
5 ms
and the database is:
10 ms
the benefit may be modest.
But if the cache is:
0.5 ms
and the database is:
50 ms
the benefit can be substantial.
18. Cache Hit Rate Is Not Everything #
Consider two systems.
System A #
Hit Rate = 99%
Cache Latency = 10 ms
System B #
Hit Rate = 95%
Cache Latency = 0.5 ms
System B might still provide excellent overall performance.
Similarly, consider:
99% hit rate
where the remaining 1% generates enormous database queries.
Therefore, when evaluating a cache, look at the complete picture:
Cache
|
+-----------+-----------+
| | |
Hit Rate Latency Miss Cost
| | |
+-----------+-----------+
|
Database Load
19. Cache Cost #
Caching is not free.
A cache introduces:
- infrastructure
- memory cost
- network traffic
- serialization
- operational complexity
- invalidation complexity
- monitoring
- failure scenarios
Therefore the real question is:
Does the performance and scalability benefit justify the additional complexity and cost?
Sometimes the answer is no.
For example, if a database query takes:
2 ms
and is executed only:
100 times/sec
introducing a distributed cache might not be worth it.
But if the query takes:
100 ms
and receives:
50,000 requests/sec
caching may be transformative.
20. The Fundamental Caching Trade-offs #
Most caching decisions can be reduced to a few fundamental trade-offs.
Freshness vs Performance #
Long TTL
↓
Better performance
Potentially stale data
Short TTL
↓
Fresher data
More source requests
Local vs Distributed #
Local
↓
Faster
Harder consistency
Distributed
↓
Shared
Network overhead
Cache Size vs Cost #
Larger Cache
↓
Higher hit rate
Higher memory cost
Complexity vs Optimization #
Simple Cache
↓
Easy to operate
Less optimization
Sophisticated Cache
↓
Better optimization
Higher operational complexity
21. The Most Important Questions in Cache Design #
Whenever you introduce a cache into a system, ask these questions:
1. What are we caching? #
Object?
Query?
API response?
Computation?
Static asset?
2. Why are we caching it? #
Reduce latency?
Reduce DB load?
Reduce cost?
Improve availability?
3. Where should it live? #
Browser?
CDN?
Local memory?
Distributed cache?
4. How is it populated? #
Lazy?
Proactive?
On write?
On read?
5. How long is it valid? #
Seconds?
Minutes?
Hours?
Forever?
6. How is it invalidated? #
TTL?
Explicit delete?
Update?
Event?
Version?
7. What happens on a miss? #
Database?
Another service?
Recompute?
8. What happens when the cache is full? #
LRU?
LFU?
TTL?
Random?
9. What happens when the cache fails? #
Fallback?
Fail open?
Fail closed?
Circuit breaker?
10. What happens during a traffic spike? #
Hot key?
Stampede?
Avalanche?
Database overload?
These questions form the foundation of almost every caching discussion in system design.
22. A Simple Mental Model #
Whenever you see a slow dependency in a system, ask:
Can I avoid calling it?
↓
Can I reuse a previous result?
↓
Can I store that result somewhere faster?
↓
How long can I safely reuse it?
↓
How do I know it is stale?
↓
What happens when it expires?
↓
What happens if millions of requests discover that it expired simultaneously?
↓
What happens if the cache itself fails?
This sequence takes us from:
"Let's add Redis."
to:
Actual System Design.
Conclusion #
Caching is one of the most powerful techniques for improving the performance, scalability, and resilience of distributed systems.
But caching is not simply:
Application → Redis → Database
A good caching design requires us to understand:
What to cache
Where to cache
When to cache
How to populate it
How long to keep it
How to invalidate it
How to evict it
How to scale it
How to handle failures
How to measure it
The most important idea to remember is:
Caching is about avoiding expensive work by safely reusing a previous result.
Once we understand that principle, the same idea appears everywhere:
CPU Cache
OS Cache
Browser Cache
CDN
Application Cache
Distributed Cache
Database Cache
API Cache
Query Cache
Computation Cache
Semantic Cache
The technology changes.
The fundamental idea remains the same.
What’s Next? #
In Part 2, we will look at the different types of caches in detail — from CPU caches and browser caches to CDNs, application caches, distributed caches, and database caches — and understand why each exists, where it sits in the request path, and what trade-offs it introduces.
Next → Types of Caches: From CPU to CDN