Improving Cache Hit Ratios: A Guide to Cache-Aside Logic
Master the cache-aside pattern to improve cache hit ratios. Learn to implement cache-miss logic, populate Redis dynamically, and handle data updates effectively.

Previously in this course, we covered Building a Simple API Response Cache with Node.js and Redis, where we established the basic flow of checking a key and returning its value. In this lesson, we level up that implementation by focusing on Improving Cache Hit Ratios through the cache-aside pattern—the industry standard for maintaining a performant and consistent data layer.
The Cache-Aside Pattern from First Principles
A "cache hit" happens when your application requests data and finds it in Redis, avoiding a trip to your primary database. A "cache miss" occurs when the data isn't there, forcing the application to fetch it from the source of truth (like PostgreSQL or a third-party API) and then update Redis.
If your cache hit ratio is low, your application suffers from high latency and unnecessary load on your primary database. To solve this, we implement the cache-aside logic:
- Check Cache: Attempt to
GETthe data from Redis. - Handle Miss: If the key is missing, fetch the data from the database.
- Populate:
SETthe result into Redis so the next request hits the cache. - Return: Send the data back to the client.
Implementing Cache-Miss Logic
Let's refine our API project. We’ll create a function that handles the logic cleanly. If you haven't set up your project yet, refer back to Setting Up the Backend Project Baseline with Node.js and Redis.
JAVASCRIPTconst getCachedData = async (key, fetchFunction) => { // 1. Check Redis const cached = await redis.get(key); if (cached) { console.log("Cache Hit!"); return JSON.parse(cached); } // 2. Cache Miss: Fetch from source console.log("Cache Miss. Fetching from DB..."); const data = await fetchFunction(); // 3. Populate Redis // We use EX to set a TTL, preventing stale data from living forever await redis.set(key, JSON.stringify(data), CE9178">'EX', 3600); return data; };
Handling Data Updates (Invalidation)
One of the biggest pitfalls in distributed systems is serving "stale" data—where the database has changed, but the cache still holds the old value. To maintain high hit ratios without sacrificing correctness, you must invalidate or update the cache when the underlying data changes.
When performing a write operation (e.g., UPDATE users SET...), you have two choices:
- Delete the key: The next read will trigger a cache miss and fetch the fresh data. This is safer and simpler.
- Update the key: Overwrite the existing Redis key with the new data.
JAVASCRIPTconst updateUser = async (userId, newData) => { // 1. Update the database await db.updateUser(userId, newData); // 2. Invalidate the cache(Remove the key) // This forces the next request to fetch the fresh, updated data await redis.del(CE9178">`user:${userId}`); };
Hands-on Exercise
Using the project structure we've developed, modify your /api/user/:id endpoint:
- Implement the
getCachedDatawrapper shown above. - Create a "dummy" database fetch function that simulates a 2-second delay.
- Observe the logs: The first request should show "Cache Miss," and subsequent requests to the same ID should show "Cache Hit."
- Write a simple
PUTendpoint that updates a user and callsredis.delto invalidate the cache.
Common Pitfalls
- Cache Stampede: If a popular key expires, multiple concurrent requests might see a cache miss and all try to fetch from the DB at once. Use locking or probabilistic early expiration to mitigate this.
- Infinite TTLs: Never set a key without an expiration unless you have a robust invalidation strategy. You will eventually run out of memory.
- Serialization Errors: Always ensure your
JSON.parseis wrapped in atry/catchblock, as malformed data in Redis can crash your node process.
FAQ
Q: Should I cache everything? A: No. Only cache data that is "read-heavy" and relatively static. Caching frequently changing data leads to high invalidation overhead.
Q: What is the ideal cache hit ratio? A: It depends on your workload, but 80%–95% is a healthy target for most API response caches.
Q: Is redis.del slow?
A: DEL is an O(1) operation for most keys and is extremely fast. It is the preferred way to handle data updates.
Recap
Improving your cache hit ratio relies on a disciplined cache-aside strategy. By implementing graceful cache-miss handling and proactive cache invalidation (like we discussed in Managing Cache via Dashboard and API: A Developer's Guide), you drastically reduce backend latency and database pressure. Remember: it is better to have a cache miss than to serve stale, incorrect data to your users.
Up next: We'll dive into Managing Hash Operations, which allow us to store structured objects in Redis more efficiently than simple strings.
Work with me

Laravel REST API Development
Clean, secure, well-documented Laravel REST APIs — the backend engine for your app, mobile client, or SaaS. Built by an API specialist.

FilamentPHP Admin Panel & Dashboard Development
A powerful admin panel for your Laravel app — built with FilamentPHP so you can manage everything without touching the database.
