When an endpoint gets slow, adding Redis is often the first idea in the room. Sometimes it is exactly right and response times drop from 400 ms to 5 ms. Other times it adds a second source of truth, stale data bugs, and an extra piece of infrastructure to babysit, while the real problem was a missing index. Redis caching is a powerful tool, but only when you use it for the right reasons.
What Redis Caching Actually Gives You
Redis is an in-memory key-value store. Reads take well under a millisecond, it supports expirations (TTL) natively, and it offers useful data structures such as hashes, sorted sets, and counters. As a cache, it lets you skip expensive work, whether that is a heavy SQL query, a slow third-party API, or a costly computation, by storing the result and serving it again.
When You Should Use Redis Caching
- Read-heavy data that changes rarely. Product catalogs, configuration, category trees, pricing tables, and public profiles are read thousands of times for every write.
- Expensive aggregations. Dashboards, leaderboards, and report totals that take seconds to compute but can be a minute old.
- Slow or rate-limited external APIs. Currency rates, geocoding, or LLM responses for identical prompts. Caching protects both latency and your quota.
- Sessions and short-lived tokens. Fast lookups with automatic expiry are a natural fit.
- Rate limiting and counters. Atomic
INCRwith a TTL is simpler and faster than doing the same in a relational database. - Protecting the database under spikes. A cache absorbs traffic bursts so your primary database stays healthy.
When You Shouldn't Use It
- The query is slow because it is badly written. Run
EXPLAIN ANALYZEfirst. An index or a rewritten query fixes the root cause; a cache only hides it until the next cache miss. - Data must always be fresh. Account balances, stock levels during checkout, and permissions are dangerous to serve stale.
- Every request is unique. If keys are almost never reused, your hit rate will be near zero and you pay the overhead for nothing.
- Your traffic is small. A well-indexed PostgreSQL or MySQL database handles a lot. Don't add infrastructure before you have a measured problem.
- As your only copy of data. A cache can be evicted or flushed at any time. If losing it means losing data, it is not a cache.
The Cache-Aside Pattern
The most common and safest approach is cache-aside, also called lazy loading. The application checks Redis first, falls back to the database on a miss, and stores the result with a TTL.
async function getProduct(id) {
const key = `product:v1:${id}`;
const cached = await redis.get(key);
if (cached) return JSON.parse(cached);
const product = await db.product.findUnique({ where: { id } });
await redis.set(key, JSON.stringify(product), 'EX', 300);
return product;
}On writes, update the database first and then delete the key, instead of trying to update the cache in place. The next read will repopulate it with fresh data.
Common Redis Caching Mistakes
No TTL
Keys without expiry live forever, grow memory without limit, and keep serving data long after it is wrong. Give every cache key a TTL, even a long one.
Forgetting invalidation
Most cache bugs are invalidation bugs. List every write path that changes the data behind a key, and make sure each one deletes it. Versioned keys such as product:v2:42 let you invalidate everything after a schema or logic change.
Cache stampede
When a popular key expires, hundreds of requests can hit the database at once. Use a short lock so only one request rebuilds the value, add random jitter to TTLs, or refresh hot keys in the background before they expire.
Caching huge objects
Storing whole pages or multi-megabyte blobs eats memory and network bandwidth. Cache the smallest useful piece, and set a maxmemory policy such as allkeys-lru.
Not measuring
Track hit rate, latency, memory, and evictions. A cache with a 20 percent hit rate is mostly overhead and may need different keys or longer TTLs.
A Quick Decision Checklist
- Have you measured the slow path and fixed obvious query problems?
- Is the data read far more often than it is written?
- Can users tolerate it being seconds or minutes old?
- Will the same keys be requested repeatedly?
- Do you know every write path that must invalidate the key?
- Can the system still work, more slowly, if Redis is down?
If you answer yes to all six, Redis caching is likely a strong win. If not, fix the underlying issue first.
Conclusion
Redis caching shines for read-heavy, repeatable, slightly stale-tolerant data and for protecting slow dependencies. It hurts when it is used to hide bad queries, cache data that must be exact, or act as a database. Measure first, use cache-aside with TTLs, plan invalidation up front, and treat Redis as an accelerator, never as the source of truth.
