> ## Documentation Index
> Fetch the complete documentation index at: https://notes.kodekloud.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Caching

> Explains caching concepts, patterns, trade-offs, and best practices for using Redis to speed reads, manage consistency, and handle invalidation in distributed systems.

When the same item is requested repeatedly, reading it from the database each time is wasteful. Consider a public figure on your app—like Lionel Messi or Cristiano Ronaldo—whose post is viewed millions of times. The database can be hammered with identical queries for the same photo. A cache placed in front of the database solves this by serving popular items from a small, extremely fast store.

<Frame>
  <img src="https://mintcdn.com/kodekloud-c4ac6d9a/6ZQa3NbKoYZJ4kpq/images/System-Design-For-Beginners/Data-Reads-and-Writes/Caching/system-architecture-flow-cache-app-database.jpg?fit=max&auto=format&n=6ZQa3NbKoYZJ4kpq&q=85&s=b32593099dc28cc6f0679bf280bc6147" alt="The image illustrates a system architecture flow, depicting an app accessing data with an added cache layer between the app and the database to improve performance." width="1920" height="1080" data-path="images/System-Design-For-Beginners/Data-Reads-and-Writes/Caching/system-architecture-flow-cache-app-database.jpg" />
</Frame>

Why use a cache?

* Reads from an in-memory cache often complete in under 1 ms; the same read from a typical database may take 20–30 ms or more.
* Under heavy load, those milliseconds multiply into significantly higher throughput and lower cost.

Typical request flow with a cache (using Redis as an example):

1. A request arrives for a trending photo.
2. The application checks Redis first:
   * If Redis contains the item → cache hit: return value from Redis; skip the database.
   * If Redis does not contain the item → cache miss: query the database, write the result back into Redis (often with a TTL), then return the response. Future requests will hit the cache.

<Frame>
  <img src="https://mintcdn.com/kodekloud-c4ac6d9a/6ZQa3NbKoYZJ4kpq/images/System-Design-For-Beginners/Data-Reads-and-Writes/Caching/redis-cache-system-mobile-app-database.jpg?fit=max&auto=format&n=6ZQa3NbKoYZJ4kpq&q=85&s=b43b6798fc7d193033874db1f9406ab0" alt="The image illustrates how a cache system using Redis works between a mobile application and a database. It shows the process of requesting photos, with Redis checking if the data exists before querying the database." width="1920" height="1080" data-path="images/System-Design-For-Beginners/Data-Reads-and-Writes/Caching/redis-cache-system-mobile-app-database.jpg" />
</Frame>

Example: cache-aside (lazy population) in Python

```python theme={null}
# python
def get_trending_photo(photo_id):
    # 1) Try cache first
    photo = redis.get(photo_id)
    if photo is not None:
        return photo  # cache hit

    # 2) Cache miss -> load from DB
    photo = db.load_photo(photo_id)

    # 3) Populate cache for next time (with TTL)
    redis.set(photo_id, photo, ex=300)  # expire after 5 minutes
    return photo
```

What to cache

* Cache items that are:
  * Small in size (so the cache stays memory-efficient)
  * Read frequently (high read-to-write ratio)
  * Updated rarely (reduces invalidation complexity)

| Item type        | Why cache it                  | Example                                          |
| ---------------- | ----------------------------- | ------------------------------------------------ |
| Trending content | High read volume, low updates | `photo:{id}` – trending photo record             |
| Metadata         | Small, frequently read        | `post_meta:{id}` – likes, caption summary        |
| Profile cards    | Reused across many pages      | `profile:{id}` – `username`, `avatar_url`, `bio` |

<Frame>
  <img src="https://mintcdn.com/kodekloud-c4ac6d9a/6ZQa3NbKoYZJ4kpq/images/System-Design-For-Beginners/Data-Reads-and-Writes/Caching/caching-process-redis-app-trending-data.jpg?fit=max&auto=format&n=6ZQa3NbKoYZJ4kpq&q=85&s=84af604e4c81bc2a1e8fcf1e6f61c7ab" alt="The image illustrates a caching process using an app and Redis to store a trending photo record and a popular profile card. It highlights what data is stored in the cache for quick access." width="1920" height="1080" data-path="images/System-Design-For-Beginners/Data-Reads-and-Writes/Caching/caching-process-redis-app-trending-data.jpg" />
</Frame>

Sessions and caches

* Login sessions must be stored in a shared store accessible to all app servers. Redis is often used for both sessions and caching because session data is small, read on nearly every request, and requires low latency.
* Keep session keys and cached keys properly namespaced to avoid collisions (for example: `session:{user_id}` vs `profile:{user_id}`).

<Callout icon="lightbulb" color="#1CB2FE">
  Redis is commonly used for both caching hot application data and as a session store. Use clear namespaces for session keys and cache keys to avoid accidental collisions.
</Callout>

Cache coherence: the trade-offs
Caching introduces a second copy of data (database + cache). Inconsistency can arise when one copy changes and the other does not. Example problem: a photo deleted from the database still exists in Redis and continues to be served to users until it expires or is invalidated.

<Frame>
  <img src="https://mintcdn.com/kodekloud-c4ac6d9a/6ZQa3NbKoYZJ4kpq/images/System-Design-For-Beginners/Data-Reads-and-Writes/Caching/data-flow-mobile-app-redis-database.jpg?fit=max&auto=format&n=6ZQa3NbKoYZJ4kpq&q=85&s=dff9628365d5e30c86342c9532d8f9e4" alt="The image illustrates the flow of data between a mobile app, Redis cache, and a database, highlighting an issue where data is still served from the cache after being deleted from the database." width="1920" height="1080" data-path="images/System-Design-For-Beginners/Data-Reads-and-Writes/Caching/data-flow-mobile-app-redis-database.jpg" />
</Frame>

Common caching strategies and trade-offs

|                     Strategy | How it works                                            | Pros                               | Cons                                             |
| ---------------------------: | ------------------------------------------------------- | ---------------------------------- | ------------------------------------------------ |
|           Cache-aside (lazy) | App checks cache → DB on miss → populate cache          | Simple, common, low read latency   | Requires explicit invalidation on writes/deletes |
| Read-through / Write-through | Cache handles reads/writes automatically                | Simplifies client code             | Can increase write latency                       |
|    Write-behind (write-back) | Cache acknowledges writes; flushes to DB asynchronously | Low write latency                  | Risk of data loss if cache fails before flush    |
|        Explicit invalidation | App deletes/updates cache keys on changes               | Stronger control over staleness    | Requires careful, consistent invalidation logic  |
|           TTL (time-to-live) | Entries auto-expire after set duration                  | Simple fallback to limit staleness | Not sufficient for strict consistency needs      |
|            Eviction policies | LRU/LFU/TTL to free memory                              | Keeps cache size bounded           | May evict hot data unexpectedly if misconfigured |

Key practices

* Use clear key naming: `photo:{id}`, `profile:{id}` to make invalidation and debugging easier.
* Apply sensible TTLs for items where eventual consistency is acceptable.
* Combine explicit invalidation on writes/deletes with TTLs to limit inconsistency windows.
* Test failure modes: cache node loss, partial updates, and race conditions (e.g., double writes).
* Monitor cache hit/miss rates, memory usage, and eviction count to tune policies.

<Callout icon="warning" color="#FF6B6B">
  Cache invalidation is one of the hardest problems in distributed systems. Choose a strategy (cache-aside vs write-through vs write-behind) based on your consistency, latency, and complexity requirements, and test failure scenarios (cache node failure, incomplete invalidation).
</Callout>

Summary

* Caching (e.g., Redis) dramatically accelerates reads for small, hot, infrequently changing data.
* The cache-aside pattern is simple and widely used: check cache → on miss read DB → populate cache.
* Design for cache consistency using TTLs, explicit invalidation, or read/write caching strategies that align with your correctness and latency needs.

Links and references

* [Redis](https://redis.io/)
* Martin Kleppmann, “Designing Data-Intensive Applications” – section on caching and consistency
* Patterns: Cache-aside, Read-through, Write-through, Write-behind — search for implementation guides in your language or framework of choice

<CardGroup>
  <Card title="Watch Video" icon="video" cta="Learn more" href="https://learn.kodekloud.com/user/courses/system-design-for-beginners/module/3ee9409c-c2ff-4102-9a76-af9840dc6e23/lesson/e772337e-1b5d-46bc-98b2-32bb446cbf74" />
</CardGroup>
