Cache Aside Pattern in System Design
Learn the Cache Aside Pattern (Lazy Loading) from a System Design perspective. This guide explains how Cache Aside works, cache hits, cache misses, cache invalidation, TTL, Redis integration, Spring Boot implementation, and real-world examples from Amazon, Netflix, Uber, and Banking systems.
Introduction
Imagine an e-commerce application where 100,000 customers search for the same product every hour.
Every request follows this path:
Customer
↓
Spring Boot API
↓
PostgreSQL
↓
Response
Although the product rarely changes,
the database is queried for every request.
Problems:
- High database load
- Increased response time
- Expensive database scaling
- Connection pool exhaustion
The most widely used caching strategy in modern applications is the Cache Aside Pattern.
Companies like:
- Amazon
- Netflix
- Uber
- Airbnb
- Banking Platforms
all use this pattern to reduce database traffic.
Learning Objectives
After completing this article, you will understand:
- What is Cache Aside?
- Why Cache Aside?
- Request Flow
- Cache Hit
- Cache Miss
- Cache Invalidation
- TTL
- Read Flow
- Write Flow
- Spring Boot Integration
- Redis Integration
- Real-World Examples
What is Cache Aside?
Cache Aside is also known as:
Lazy Loading Cache
The application is responsible for managing the cache.
The cache is checked first.
If data is not found,
the application retrieves it from the database,
stores it in Redis,
and returns it to the client.
Why Cache Aside?
Without Cache
flowchart TD
U[Users]
APP[Spring Boot]
DB[(Database)]
U --> APP
APP --> DB
Every request reaches the database.
With Cache Aside
flowchart TD
U["Users"]
APP["Spring Boot"]
CACHE{"Redis Cache"}
DB["PostgreSQL"]
R["Return Response"]
U --> APP
APP --> CACHE
CACHE -- "Hit" --> R
CACHE -- "Miss" --> DB
DB --> CACHE
CACHE --> R
Frequently requested data comes directly from Redis.
Cache Aside Architecture
flowchart TD
USERS["Users"]
APP["Spring Boot API"]
CACHE{"Redis Cache"}
DB["PostgreSQL"]
RESP["Response"]
USERS --> APP
APP --> CACHE
CACHE -- "Cache Hit" --> RESP
CACHE -- "Cache Miss" --> DB
DB --> CACHE
CACHE --> RESP
Redis acts as a high-speed cache.
PostgreSQL remains the source of truth.
Read Request Flow
flowchart TD
CLIENT["Client"]
APP["Spring Boot"]
CACHE["Redis"]
HIT{"Cache Hit?"}
RETURN1["Return Data"]
DB["Database"]
STORE["Store in Redis"]
RETURN2["Return Response"]
CLIENT --> APP
APP --> CACHE
CACHE --> HIT
HIT -- Yes --> RETURN1
HIT -- No --> DB
DB --> STORE
STORE --> RETURN2
Step-by-Step Read Flow
Step 1
Customer requests
GET /products/1001
Step 2
Spring Boot checks Redis.
Redis
↓
Product Found?
Step 3
If found
Return immediately.
Latency
<1 ms
Step 4
If missing
Query PostgreSQL.
Step 5
Store response in Redis.
Step 6
Return response to client.
Cache Hit
flowchart LR
CLIENT["Client"]
APP["Spring Boot"]
CACHE["Redis Cache"]
RESP["Cached Response"]
CLIENT --> APP
APP --> CACHE
CACHE --> RESP
RESP --> CLIENT
Advantages
- Very fast
- No database call
- Low CPU usage
- Better scalability
Cache Miss
flowchart TD
CLIENT["Client"]
APP["Spring Boot"]
CACHE{"Redis Cache"}
DB["Database"]
STORE["Store in Redis"]
RESP["Return Response"]
CLIENT --> APP
APP --> CACHE
CACHE -- "Cache Miss" --> DB
DB --> STORE
STORE --> CACHE
CACHE --> RESP
The retrieved data is cached for future requests.
Complete Cache Aside Flow
sequenceDiagram
participant Client
participant SpringBoot
participant Redis
participant PostgreSQL
Client->>SpringBoot: GET /products/1001
SpringBoot->>Redis: Read Cache
alt Cache Hit
Redis-->>SpringBoot: Product
SpringBoot-->>Client: Response
else Cache Miss
SpringBoot->>PostgreSQL: Query Product
PostgreSQL-->>SpringBoot: Product
SpringBoot->>Redis: Save Product
SpringBoot-->>Client: Response
end
Write Operation
Updating data requires cache invalidation.
flowchart TD
CLIENT["Client"]
APP["Spring Boot API"]
DB["Update Database"]
CACHE["Invalidate Redis Cache"]
SUCCESS["Return Success"]
CLIENT --> APP
APP --> DB
DB --> CACHE
CACHE --> SUCCESS
Why Remove Cache?
Suppose
Product price changes
iPhone
↓
$999
↓
$899
Redis still stores
$999
Customers receive stale data.
Solution
Delete cache after updating database.
Cache Invalidation
flowchart TD
CLIENT["Client Update"]
APP["Spring Boot"]
DB["Update Database"]
CACHE["Invalidate Redis Cache"]
READ["Next Read Request"]
REDIS["Reload Redis Cache"]
CLIENT --> APP
APP --> DB
DB --> CACHE
CACHE --> READ
READ --> REDIS
This ensures fresh data.
Time To Live (TTL)
Redis entries should expire automatically.
Example
Product
↓
TTL
↓
30 Minutes
After expiration,
the next request reloads fresh data.
Spring Boot Architecture
flowchart TD
CLIENT["Client"]
LB["Load Balancer"]
GATEWAY["API Gateway"]
APP1["Spring Boot Instance 1"]
APP2["Spring Boot Instance 2"]
REDIS["Redis Cluster"]
DB["PostgreSQL"]
CLIENT --> LB
LB --> GATEWAY
GATEWAY --> APP1
GATEWAY --> APP2
APP1 --> REDIS
APP2 --> REDIS
REDIS --> DB
Product Search Example
Without Cache
Customer
↓
Database
↓
300 ms
With Cache
Customer
↓
Redis
↓
5 ms
Banking Example
Cache
- Branch Details
- Currency Exchange Rates
- Interest Rates
- ATM Locations
Do Not Cache
- Account Balance
- Payment Status
- Transaction History
- OTP
Amazon Example
Cache
- Product Details
- Product Images
- Reviews
- Recommendations
Do Not Cache
- Inventory
- Shopping Cart
- Real-time Pricing
Netflix Example
Redis stores
- Movie Metadata
- User Preferences
- Trending Movies
- Recommendations
Uber Example
Redis stores
- Driver Locations
- ETA
- Surge Pricing
- Frequently Used Routes
Advantages
- Very easy to implement
- Excellent read performance
- Reduces database load
- Simple cache invalidation
- Works well with Redis
- Supports distributed systems
Disadvantages
- First request is slower (Cache Miss)
- Application manages cache logic
- Cache invalidation complexity
- Possible stale data
- Additional Redis dependency
Cache Aside vs Read Through
| Cache Aside | Read Through |
|---|---|
| Application manages cache | Cache manages loading |
| Most popular | Less common |
| Flexible | Simpler application |
| Better control | Vendor dependent |
Redis Keys
Example
product:1001
customer:2001
order:3001
branch:101
Meaningful keys improve maintainability.
Monitoring
Monitor
- Cache Hit Ratio
- Cache Miss Ratio
- Redis Memory Usage
- Evictions
- TTL Expirations
- Database Queries
- API Response Time
- Redis Latency
Tools
- Redis Insight
- Datadog
- Grafana
- Prometheus
- CloudWatch
Common Mistakes
❌ Forgetting to invalidate cache
❌ Very long TTL values
❌ Caching frequently changing data
❌ Caching large objects
❌ Ignoring cache hit ratio
❌ Using Redis as the only database
Best Practices
- Use Cache Aside for read-heavy applications.
- Store only frequently accessed data.
- Invalidate cache immediately after database updates.
- Configure appropriate TTL values.
- Keep cached objects small.
- Use meaningful Redis key names.
- Monitor cache hit ratio continuously.
- Combine Redis with Spring Cache abstraction.
- Avoid caching highly volatile transactional data.
Common Interview Questions
What is the Cache Aside Pattern?
Cache Aside is a caching strategy where the application checks the cache first. If the data is missing, it retrieves it from the database, stores it in the cache, and then returns it to the client.
Why is Cache Aside called Lazy Loading?
Because data is loaded into the cache only when it is requested for the first time, rather than being preloaded.
What happens during a Cache Miss?
The application queries the database, stores the retrieved data in Redis, and then returns it to the client. Future requests for the same data become Cache Hits.
Why should cache entries be invalidated after updates?
If the cache is not invalidated after modifying the database, users may receive stale or outdated information.
When should Cache Aside be used?
Cache Aside is ideal for read-heavy applications where data changes relatively infrequently, such as product catalogs, user profiles, configuration data, and reference information.
Summary
The Cache Aside Pattern is the most widely adopted caching strategy in modern distributed systems. It provides a simple yet highly effective way to reduce database load while significantly improving application performance.
In this article, we covered:
- Cache Aside fundamentals
- Cache Hit & Cache Miss
- Read and write flows
- Cache invalidation
- TTL
- Redis integration
- Spring Boot architecture
- Banking, Amazon, Netflix, and Uber examples
- Monitoring
- Best practices
Cache Aside works exceptionally well with Redis and Spring Boot, making it the preferred caching pattern for high-performance, cloud-native applications that need to handle millions of requests efficiently.
Comments
Share a question, correction, or practical insight about this article.
Checking login status...
Loading approved comments...