What Is Caching?
Caching means temporarily storing data that the application is likely to need again.
Imagine an e-commerce website where thousands of users repeatedly open the same product page. The application could query the database every time someone opens that page. But if the product information has not changed, there is little reason to perform the same database query again and again.
Instead, the application can retrieve the data from the database once, store it in a cache such as Redis, and serve subsequent requests directly from the cache.
The basic flow looks like this:
First request: application → database → Redis → user
Next requests: application → Redis → user
The result can be fewer database queries, lower load, and faster responses.
Why the Database Isn't Always the Bottleneck
When an application becomes slower, the database is often one of the first things developers investigate. Sometimes that is exactly where the problem is. But not always.
A database query can be perfectly optimized and still become expensive when it is executed thousands of times.
For example, imagine a dashboard that calculates statistics based on a large amount of historical data. The query itself might take only 200 milliseconds. That does not sound like much. But if hundreds of users request the same calculation every minute, the database is doing the same work repeatedly.
Caching can help by allowing the application to calculate the result once and reuse it for subsequent requests.
This does not mean that caching should automatically be added whenever an application becomes slower. Before introducing another layer, it is important to understand where the actual bottleneck is.
Sometimes the right solution is a database index. Sometimes it is a better query. Sometimes the application logic itself needs to be optimized.
Redis vs. a Database
Redis and a relational database such as PostgreSQL solve different problems.
PostgreSQL is typically the source of truth. It is designed to store persistent data, handle transactions, maintain relationships between entities, and execute complex queries.
Redis is an in-memory data store. Its strength is speed, which makes it useful for data that is frequently accessed, temporary, or expensive to retrieve repeatedly.
Typical Redis use cases include:
- results of expensive database queries
- user sessions
- temporary tokens
- rate limiting
- counters
- API responses
- frequently accessed data that changes relatively rarely
A common architecture therefore looks like this:
Application → Redis → PostgreSQL
PostgreSQL remains the reliable source of data, while Redis acts as a fast layer between the application and the database.
The Cache-Aside Pattern
One of the most common caching strategies is called cache-aside.
The idea is simple: the application first checks whether the required data is already in the cache.
If it is, the application uses it immediately.
If it is not, the application retrieves the data from the database, stores it in Redis, and then returns it to the user.
The process looks like this:
- The application receives a request.
- It checks Redis for the requested data.
- If the data exists, Redis returns it.
- If it does not exist, the application queries PostgreSQL.
- The result is stored in Redis.
- The application returns the result to the user.
The next request can then be served directly from Redis.
This approach is particularly useful when the same data is requested frequently but does not change very often.
TTL: How Long Should Data Stay in the Cache?
Cached data should not necessarily remain there forever.
This is where TTL (Time To Live) comes in. TTL defines how long a cached value should remain valid before it automatically expires.
For example, you might store a product in Redis with a TTL of five minutes. After those five minutes, the cached value expires and the application can retrieve fresh data from the database.
The right TTL depends on the type of data.
For something that changes frequently, a short TTL might make sense. For data that rarely changes, the cache can live much longer.
The important question is not simply “How long can we cache this?” but rather:
“How long can this data safely be slightly out of date?”

The Hardest Part? Cache Invalidation
There is a famous saying in software development:
“There are only two hard things in Computer Science: cache invalidation and naming things.”
And cache invalidation really can be tricky.
Imagine that a product price is stored in Redis. The price changes in PostgreSQL, but the old value is still sitting in the cache. Users may continue seeing the outdated price until the cache expires.
There are several ways to handle this.
One option is to delete the cache entry whenever the underlying data changes.
Another is to update the cached value at the same time as the database value.
A third approach is to rely on a TTL, accepting that the cached value may be slightly outdated for a limited amount of time.
The right strategy depends on how important data freshness is. A few seconds of delay might be perfectly acceptable for a dashboard statistic, but clearly not for something like a payment amount.
What Can Go Wrong?
Caching improves performance, but it also introduces another layer of complexity.
One common problem is stale data. The cache may contain an older version of the information than the database.
Another is a cache stampede. If a popular cached value expires and many requests arrive at exactly the same time, they may all try to retrieve the same data from the database simultaneously.
There is also the question of memory. Redis stores data in memory, and memory is not unlimited. Caching everything is therefore rarely a good strategy.
Finally, caching adds operational complexity. There is another service to monitor, another potential point of failure, and another part of the architecture developers need to understand.
When Should You Not Use Redis?
Redis is not automatically the answer to every performance problem.
If your application is small and your database handles the current load comfortably, adding Redis may simply introduce unnecessary complexity.
Similarly, if a query is slow because it lacks an index or is poorly written, caching the result might only hide the underlying problem.
Caching can also be a poor fit when the data needs to be completely up to date at all times or changes so frequently that cached values are rarely useful.
The first step should therefore always be measurement.
Find the bottleneck first. Then decide whether caching actually addresses it.
Redis + Django + PostgreSQL
Redis fits naturally into applications built with Django and PostgreSQL.
Django already provides a caching framework that can be connected to Redis. A typical architecture might look like:
Django → Redis → PostgreSQL
The application first checks Redis for frequently requested data. If the data is not available, Django queries PostgreSQL, stores the result in Redis, and serves it to the user.
This can be particularly useful for applications where reads significantly outnumber writes.
For example, if thousands of users repeatedly request the same relatively stable information, caching can prevent PostgreSQL from performing the same work again and again.
Cache Is Not a Replacement for Good Architecture
Redis is not a magic performance button.
Before introducing caching, it is worth understanding what is actually slowing the application down. The solution might be a better database index, a more efficient query, improved application logic, or a different architectural approach.
Caching makes the most sense when there is data that is expensive to retrieve or calculate and is requested repeatedly.
The most useful question to ask is therefore:
Which data is worth loading once and using multiple times?
That is where Redis can make a real difference — not by replacing your database, but by reducing how often your application needs to ask it to do the same work.