Full Stack • Java • System Design • Cloud • AI Engineering

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.


Loading likes...

Comments

Share a question, correction, or practical insight about this article.

Loading approved comments...