Home / Databases / Redis

DatabasesHow-toIntermediate

Advisory: Redis caching patterns - cache-aside, write-through, TTLs, invalidation and stampede protection

A cache helps only if it stays correct and survives failures gracefully. Choose cache-aside, read-through or write-through per data type, set TTLs and invalidation rules, protect the database from stampedes when hot keys expire, and design the application to keep working when Redis is slow or down.

Deepak Bhardwaj3 min read

Applies toRedis and Valkey (self-managed), Azure Managed Redis / Azure Cache for Redis, Amazon ElastiCache, Redis Cloud

Patterns

Pattern How Fits
Cache-aside (lazy loading) App reads cache; on miss reads DB and writes cache Most read-heavy data; simple
Read-through Cache layer/library loads from DB on miss Same as cache-aside, logic centralised
Write-through App writes DB and cache together Data read right after writing
Write-behind App writes cache; cache flushed to DB later Very high write rates; risk of loss, use with care
Refresh-ahead Refresh popular keys before they expire Hot keys with expensive loads

Cache-aside example

def get_product(pid):
    key = f"product:{pid}"
    cached = r.get(key)
    if cached:
        return json.loads(cached)
    product = db.fetch_product(pid)
    r.set(key, json.dumps(product), ex=3600 + random.randint(0, 300))  # TTL with jitter
    return product

def update_product(pid, data):
    db.update_product(pid, data)
    r.delete(f"product:{pid}")   # invalidate after the DB commit

TTL and invalidation rules

Data Approach
Reference data (catalog, config) Longer TTL + delete on change
User sessions TTL = session timeout; sliding expiry
Computed aggregates Short TTL or refresh-ahead
Data that must never be stale Don't cache, or write-through with strict invalidation

Always set a TTL, even with explicit invalidation, so missed invalidations heal themselves.

Stampede protection

When a hot key expires, many requests hit the database at once.

Technique How
TTL jitter Randomise expiry so keys don't expire together
Single-flight lock First request sets SET lock:key 1 NX EX 10 and reloads; others wait briefly or serve stale
Stale-while-revalidate Store a soft expiry inside the value; serve stale while one worker refreshes
Refresh-ahead Background refresh for known hot keys

Failure behaviour

  • Timeouts on cache calls short (milliseconds), with fallback to the database.
  • Circuit breaker so a Redis outage doesn't exhaust app threads.
  • Database sized (or rate-limited) to survive a cold cache, or warm the cache on start.
  • Don't store the only copy of important data in a cache-configured Redis (eviction).

Key design

  • Namespaced keys: service:entity:id.
  • Version in key for format changes: product:v2:123.
  • Avoid huge values and huge collections (see the memory optimisation post).

Tip

Track cache hit ratio, database load and p95 latency together. A falling hit ratio after a release usually means a key format or TTL change, and it shows up as database load first.

Checklist

  • Pattern chosen per data type
  • TTLs with jitter on every cache key
  • Invalidation after DB commits
  • Stampede protection for hot keys
  • Timeouts, fallbacks and circuit breakers
  • Hit ratio and DB load monitored