What to cache with Redis
Redis is an in-memory data store, and reading a key usually takes a fraction of a millisecond, while a heavy SQL query may take hundreds. But speed is not the only criterion; the data must be cacheable.
- Good candidates: product lists, heavy report results, third-party API responses, site settings, expensive computations.
- Poor candidates: live stock counts, account balances, anything where a stale read costs money.
- Rule of thumb: read-to-write ratio; at ten to one or more, caching usually pays off.
Before adding a cache, fix the index and the query first; a cache should not be a coat of paint over a bad query. This also appears in scalable API design.
The cache-aside pattern and its alternatives
In cache-aside the application manages the cache itself: it reads the cache first, and on a miss fetches from the database, stores the result and returns it.
// Node.js with node-redis
async function getProduct(id) {
const key = "product:" + id;
const hit = await redis.get(key);
if (hit) return JSON.parse(hit);
const row = await db.query("SELECT * FROM products WHERE id = $1", [id]);
if (!row) return null;
// 10 min base TTL + up to 60 s jitter
const ttl = 600 + Math.floor(Math.random() * 60);
await redis.set(key, JSON.stringify(row), { EX: ttl });
return row;
}| Pattern | How it works | Risk |
|---|---|---|
| Cache-aside | App fills the cache on a miss | First request is slow |
| Write-through | Every write updates the cache too | Slower writes; cache fills with cold data |
| Write-behind | Write to cache first, batch to the DB later | Data loss if Redis fails |
Start with cache-aside; it has a simple mental model and a Redis failure only makes things slower rather than losing data.

TTL, key naming and memory
TTL and eviction
Every key should have a TTL; otherwise the cache slowly fills memory. The TTL balances freshness against database load: site settings might be an hour, product lists one to five minutes. To stop many keys expiring at the same instant, add a few seconds of random jitter.
To cap memory, set maxmemory with the allkeys-lru policy so Redis evicts the least recently used keys when full.
# redis.conf
maxmemory 512mb
maxmemory-policy allkeys-lruNaming keys
Use a type:id:version structure such as product:42:v3. The version part lets you invalidate every old key by changing a single number in code. Avoid KEYS * in production because it blocks Redis; use SCAN instead.
Invalidation: the hard part
There are three main strategies and a mix of them usually works best:
- Time-based expiry: rely on TTL alone. It is simple and enough when a few minutes of staleness is harmless.
- Delete on write: after updating a record,
DELits key so the next request rebuilds it. - Versioned keys: keep a group version number in its own key and bump it to invalidate every key in the group at once.
Preventing cache stampedes
When a hot key expires, hundreds of simultaneous requests miss and all run the same heavy query. The database buckles, responses slow down and the problem feeds itself. The simplest defence is a short lock: only one request rebuilds the cache while the others wait briefly.
// SET NX = only if absent; EX = auto-release after 10 s
const got = await redis.set("lock:" + key, "1", { NX: true, EX: 10 });
if (got) {
try { /* rebuild value and SET it */ }
finally { await redis.del("lock:" + key); }
} else {
await new Promise(r => setTimeout(r, 50)); // wait, then re-read cache
}Complementary defences are the TTL jitter mentioned earlier and refreshing very hot keys shortly before they expire.
Operations: persistence, security and monitoring
- Persistence: if Redis is only a cache you can disable RDB/AOF; if it also holds queues or counters, keep it on and back it up like a database.
- Security: never expose Redis directly to the internet; use a password plus a firewall or private network.
- Monitoring: watch the hit rate (
keyspace_hitsandkeyspace_missesinINFO stats), memory use and evictions. A hit rate well below about 80% usually means poor keys or TTLs.
The official command and eviction docs live at redis.io/docs. For dashboards of these metrics see professional monitoring and logging, and if slowness is hurting your search results and user experience, read page load speed optimisation for the client side.
If your system has slowed under load and you are unsure where to start, our web development and optimisation service includes reviewing the caching layer.
Frequently asked questions
Is Redis better than Memcached for caching?
Both work for simple caching. Redis offers richer data structures (hashes, sets, sorted sets), flexible TTLs and optional persistence, which is why it is the default choice in most projects.
What TTL should I use?
There is no fixed number. It depends on how much staleness the data tolerates: seconds for dynamic data, minutes for lists, hours for settings. Always add a few seconds of random jitter.
What happens if Redis goes down?
With cache-aside the app should fall back to the database and merely get slower. To avoid a full outage, set short timeouts on Redis connections and treat cache errors as misses.
Why is my cache hit rate low?
Usually because of very short TTLs, keys that include changing parameters such as timestamps, or caching data that is rarely read twice. Review your keys and access pattern.

Comments
No comments yet. Be the first to share your thoughts.