Hello! Welcome to the next lesson in our journey through backend engineering.
In our previous lesson, we successfully offloaded a time-consuming task to a Celery worker using Redis as a message broker. This dramatically improved your API's response time by moving work into the background. You saw how an in-memory data store like Redis can act as a high-speed communication channel between different parts of your system.
Today, we're going to explore another critical use case for such in-memory stores: caching. This lesson directly addresses the learning outcome: Explain common caching strategies (e.g., cache-aside, write-through) and their use cases. We will dive into the core theoretical patterns that determine how an application reads from and writes to a cache.
Your experience with performance optimization in machine learning pipelines will be helpful here. Just as you might pre-calculate and store intermediate results to speed up a complex data transformation, caching in web applications stores the results of expensive operations (like database queries) to serve future requests much faster. This lesson focuses on the system-level strategies for managing that stored data.
1. Why is Caching a Cornerstone of Backend Performance?
At its core, caching is about reducing latency. Accessing data from main memory (RAM) is orders of magnitude faster than accessing it from a disk-based database. For systems that need to handle thousands or millions of requests, this difference is monumental.
To understand the sheer scale of this performance gap and why caching is so effective, please watch the beginning of the following video from a Staff Engineer at Meta.
Caching in System Design Interviews w/ Meta Staff Engineer
This clip provides a clear, quantitative explanation of why caching is so fundamental to high-performance systems by comparing the access speeds of memory and disk.
Watch the section from 00:41 to 01:53. Focus on the comparison between accessing data from a database (disk) versus a cache (memory) and the performance implications of this difference.
The key takeaway is that a cache acts as a high-speed, temporary storage layer for frequently accessed data, saving your application from making slow, repetitive trips to the primary data source (like a PostgreSQL database).
2. Core Caching Patterns and Strategies
Now that we understand why we cache, let's explore how. A caching strategy, or pattern, defines the rules for how your application, cache, and database interact. It dictates when data is read from the cache, when it's written to the cache, and how the cache and database are kept in sync.
The image below provides a great visual summary of the most common strategies we will discuss.

The following video provides an excellent, in-depth explanation of the most important patterns. We will use this as our primary guide.
Caching in System Design Interviews w/ Meta Staff Engineer
This section of the video is the core of our lesson. It breaks down the four most essential caching architectures: Cache-Aside, Write-Through, Write-Behind, and Read-Through. Pay close attention to the trade-offs for each pattern.
Watch the segment from 08:41 to 14:51. As you watch, try to answer the following for each pattern: Who is responsible for writing to the cache (the application or the cache itself)? When is the database updated (synchronously or asynchronously)? What are the main pros and cons regarding performance, data consistency, and complexity?
Let's consolidate what you've just learned about each strategy.
A. Cache-Aside (Lazy Loading)
This is the most common caching strategy and the one you will likely implement most often.
- Read Path:
- The application tries to read data from the cache.
- Cache Miss: If the data is not in the cache, the application reads the data directly from the database.
- The application then writes the data into the cache.
- The data is returned to the client.
- Cache Hit: If the data is in the cache, it is returned directly to the client.
- Write Path: The application writes data directly to the database. The cache is not updated on a write. The old, stale data in the cache will either expire or be overwritten after the next cache miss.
- Pros:
- Resilient: The cache and database are decoupled. If the cache fails, the application can still function by falling back to the database (though more slowly).
- Lean Cache: Only data that is actually requested is cached.
- Cons:
- Stale Data: Data in the cache can become stale if it's updated in the database but not invalidated in the cache.
- Cache Miss Penalty: The first read for any piece of data will always be a cache miss, resulting in three steps (cache read, DB read, cache write), which is slower than just a DB read.
B. Write-Through
This strategy prioritizes data consistency.
- Read Path: Usually combined with Cache-Aside or Read-Through for reads.
- Write Path:
- The application writes data to the cache.
- The cache synchronously writes the data to the database.
- The operation is only considered complete after both the cache and the database have been updated.
- Pros:
- High Consistency: Data in the cache and database is always consistent after a write operation.
- Cons:
- Higher Write Latency: Every write has to go to two systems, making it slower than writing to the database alone.
- Cache Pollution: Data that is written but rarely read might fill up the cache unnecessarily.
C. Write-Behind (or Write-Back)
This strategy is optimized for high-throughput writes.
- Read Path: Usually combined with Cache-Aside or Read-Through.
- Write Path:
- The application writes data directly and only to the cache.
- The cache then asynchronously writes the data to the database in the background, often in batches.
- Pros:
- Extremely Low Write Latency: The application gets a response as soon as the data is in the fast in-memory cache.
- Resilient to DB Downtime: Writes can still be accepted even if the database is temporarily unavailable.
- Cons:
- Risk of Data Loss: If the cache fails before the data is written to the database, those writes are permanently lost. This makes it unsuitable for critical data like financial transactions.
D. Read-Through
This is a variation of Cache-Aside that simplifies application logic.
- Read Path:
- The application tries to read data from the cache.
- Cache Miss: The cache itself is responsible for fetching the data from the database, updating itself, and returning the data to the application.
- Write Path: Typically paired with a write strategy like Write-Through or Write-Behind.
- Pros:
- Simpler Application Code: The application only ever interacts with the cache, abstracting the database away.
- Cons:
- Requires Specialized Provider: Your cache (e.g., Redis) needs a plugin or library to communicate with your specific database, adding complexity to the cache layer. Standard Redis does not support this out of the box.
Test your understanding!
Imagine you are designing the backend for an e-commerce platform. Which caching strategy would you choose for each of the following scenarios and why?
- Displaying product details (name, description, price), which are read very frequently but updated rarely.
- Handling a user's shopping cart, where items are added and removed. The data must be durable, but you want the "Add to Cart" action to feel instant.
- Recording a user's payment transaction.
Show answer
-
Product Details: Cache-Aside is an excellent choice. It's great for read-heavy workloads. A cache miss is acceptable on the first view, and the potential for slightly stale data (e.g., a price update taking a few minutes to reflect) is often a reasonable trade-off for the massive performance gain on subsequent reads.
-
Shopping Cart: Write-Behind could be a good fit. Adding an item to the cart should be fast. Writing to the cache is instant. The data can then be written to the database asynchronously. While there's a small risk of data loss if the cache crashes, for a shopping cart, this is often an acceptable risk compared to the user experience benefit. A Write-Through strategy could also work if consistency is more important than the absolute lowest latency.
-
Payment Transaction: No Caching, or Write-Through with extreme care. For critical, transactional data, you cannot risk data loss. A Write-Behind strategy is out of the question. You would typically write directly to the database to ensure atomicity and durability. A Write-Through cache might be used to immediately reflect the new balance in a user's account view, but the primary operation must be a safe, synchronous write to the database.
3. Keeping the Cache Fresh: Eviction and Invalidation
A cache has limited memory. This introduces two fundamental challenges:
- What do you do when the cache is full? (Eviction)
- How do you handle data that has changed in the database? (Invalidation)
Eviction Policies
When the cache runs out of space, an eviction policy decides which items to remove. The video you watched earlier covers the most common ones.
Caching in System Design Interviews w/ Meta Staff Engineer
This short segment explains the common strategies for deciding what data to remove when the cache is full.
Watch from 14:51 to 17:16. Focus on understanding the logic behind LRU, LFU, and TTL.
In summary:
- Least Recently Used (LRU): Evicts the item that hasn't been accessed for the longest time. This is a great general-purpose default.
- Least Frequently Used (LFU): Evicts the item that has been accessed the fewest times. Useful when some items are vastly more popular than others, regardless of when they were last accessed.
- Time To Live (TTL): Each item is stored with an expiration time. Once the time is up, it's automatically removed. This is excellent for data where freshness is important and some staleness is acceptable.
Invalidation Strategies
Eviction handles a full cache, but invalidation handles stale data. If you use a Cache-Aside strategy, what happens when a user updates their profile picture? The write goes to the database, but the old picture might remain in the cache, serving stale content to other users.
There are two primary ways to solve this:
-
Time-Based Invalidation (TTL): This is the simplest approach. By setting a short TTL on cached items (e.g., 5 minutes), you guarantee that stale data will be purged automatically after a short period. This is often "good enough" for non-critical data.
-
Explicit Invalidation (Invalidate-on-Write): When data is updated in the database, the application code takes explicit action to remove the corresponding entry from the cache.
- In a Write-Through pattern, this is handled automatically.
- In a Cache-Aside pattern, your update logic would look like this:
- Update the data in the database.
- Send a command to
DELETEthe corresponding key from the cache.
- The next read for that data will be a cache miss, forcing a fetch of the fresh data from the database, which then repopulates the cache.
This article provides a good overview of these invalidation approaches.
A Deep Dive into Caching Strategies and Invalidation Patterns
This article discusses cache invalidation in depth. We will focus on the section that defines the different invalidation strategies.
Read the section 'Phase 2: Cache Invalidation Strategies'. Pay attention to the explanations and code examples for 'Strategy 1: TTL-Based Expiration' and 'Strategy 2: Write-Through Invalidation' (which is functionally similar to explicit invalidation).
4. Conclusion: Choosing Your Strategy
There is no single "best" caching strategy. The right choice is a trade-off between performance, data consistency, and implementation complexity. The article you just read provides a helpful decision matrix.
Here's a simplified way to think about it:
- Need good performance for reads and can tolerate slightly stale data? Start with Cache-Aside and a reasonable TTL. This is the most common and versatile pattern.
- Need absolute data consistency and can tolerate slower writes? Use Write-Through.
- Need blazing-fast writes and can risk losing some data on a crash? Use Write-Behind.
Key Takeaways:
- Caching improves performance by storing frequently accessed data in fast in-memory storage like Redis.
- Cache-Aside is the most common strategy, where the application manages fetching data from the database on a cache miss.
- Write-Through ensures data consistency by writing to both cache and database synchronously, at the cost of write speed.
- Write-Behind offers the fastest writes by updating the database asynchronously, but risks data loss.
- Read-Through simplifies application logic by delegating database fetching to the cache provider.
- You must have strategies for managing your cache, including eviction policies (like LRU, TTL) to handle a full cache and invalidation strategies to handle stale data.
Preview of the Next Lesson:
We have covered the essential theory. In the next lesson, "Implement caching in Django using Redis as the cache backend," we will put this knowledge into practice. You'll configure Django to use the same Redis server from our Celery setup as a cache backend and implement the Cache-Aside strategy to speed up one of our API endpoints.