100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Redis

Caching Strategies with Redis

A practical guide to the core caching patterns — cache-aside, write-through, and write-behind — used to speed up applications with Redis, plus how to pick sane TTLs and eviction policies.

Performance & CachingIntermediate9 min readJul 10, 2026
Analogies

Why Cache with Redis

Redis stores data in RAM rather than on disk, so reads and writes complete in sub-millisecond time compared to the tens or hundreds of milliseconds a relational database or a downstream REST API might take under load. Caching means placing a copy of frequently requested data in Redis so repeat requests are served from memory instead of re-computing or re-fetching the same result from a slower system of record.

🏏

Cricket analogy: Think of Virat Kohli's career stats sitting on a broadcaster's in-studio graphics tablet during a live match instead of an analyst re-querying a decade of scorecards every time the commentator asks for his average against pace bowling.

Cache-Aside (Lazy Loading)

In the cache-aside pattern, the application code is responsible for checking Redis first with a GET; on a cache hit it returns the value immediately, and on a cache miss it queries the primary database, then populates Redis with a SET (usually with a TTL) before returning the result to the caller. This is the most widely used pattern because it only caches data that is actually requested, keeping memory usage proportional to real traffic, but it does mean the first request after a miss pays the full database latency.

🏏

Cricket analogy: It resembles a stadium announcer who checks a whiteboard for a batter's strike rate first; if it's not written up yet, he asks the scorers, gets the number, and then chalks it onto the board for the next time someone asks.

python
import redis, json, time

r = redis.Redis(host="localhost", port=6379, decode_responses=True)

def get_user_profile(user_id: str) -> dict:
    cache_key = f"user:profile:{user_id}"
    cached = r.get(cache_key)
    if cached is not None:
        return json.loads(cached)  # cache hit

    # cache miss: fall back to the database
    profile = db.query_user_profile(user_id)
    r.set(cache_key, json.dumps(profile), ex=300)  # TTL = 5 minutes
    return profile

Write-Through Caching

With write-through caching, every write from the application goes to Redis first, and Redis (or the application layer wrapping it) synchronously propagates that same write to the backing database before acknowledging success. This guarantees the cache is never stale relative to the database because the two are updated in the same logical operation, but it adds the database's write latency to every single write path, which can hurt throughput on write-heavy workloads like order processing.

🏏

Cricket analogy: It is like a scorer updating both the giant stadium scoreboard and the official BCCI digital scorecard in the same motion the instant a boundary is hit, so neither ever shows a different total than the other.

Write-Behind (Write-Back) Caching

Write-behind caching flips the write-through trade-off: the application writes only to Redis and returns immediately, while a background process (often reading from a Redis Stream or a queue like Kafka) asynchronously batches and flushes those writes to the database moments later. This gives very low write latency and lets many writes to the same key be coalesced into a single database update, but it introduces a durability gap where a Redis crash before the flush means the write is lost unless Redis persistence is also enabled.

🏏

Cricket analogy: It resembles a scorer quickly jotting runs in a personal notebook during a fast T20 over and only transcribing the full over's tally into the official ICC records once the over actually finishes, batching six deliveries into one update.

Write-behind caching is often paired with Redis persistence (RDB snapshots or AOF logging) to reduce, but not eliminate, the data-loss window. Redis persistence protects the cache itself from restarts — it does not replace a durable primary database as the ultimate source of truth.

Choosing TTLs and Eviction Policies

A cache's usefulness depends heavily on its expiration strategy: a TTL that is too short causes excessive cache misses and defeats the purpose of caching, while a TTL that is too long risks serving stale data long after the source has changed. Redis also needs a maxmemory-policy such as allkeys-lru (evict the least recently used key across the whole keyspace) or volatile-ttl (evict keys with the nearest expiry first among keys that have a TTL set) so that when memory fills up, Redis degrades gracefully instead of running out of memory and rejecting writes.

🏏

Cricket analogy: It is like deciding how long a live win-probability percentage stays valid on a cricket broadcast graphic — refresh it every ball and it's accurate but noisy, refresh it only once an innings and it's stale by the time Jos Buttler smashes a six.

When thousands of keys share the same TTL — for example, if an entire product catalog is cached with ex=3600 in one bulk job — they can all expire at nearly the same moment, causing a 'cache stampede' where every request floods the database simultaneously. Mitigate this by adding random jitter to TTLs (e.g. ex=3600 + random.randint(0, 300)) or using a locking/early-recomputation strategy so only one request repopulates the cache while others wait or serve a slightly stale value.

  • Cache-aside (lazy loading) is the most common pattern: the app checks Redis, falls back to the database on a miss, and populates the cache afterward.
  • Write-through keeps the cache always consistent with the database by writing to both synchronously, at the cost of higher write latency.
  • Write-behind minimizes write latency by writing to Redis first and flushing to the database asynchronously, but introduces a durability gap.
  • TTLs balance freshness against cache-hit rate: too short wastes the cache, too long serves stale data.
  • maxmemory-policy (e.g. allkeys-lru, volatile-ttl) determines what Redis evicts once it hits its memory limit.
  • Add jitter to bulk-set TTLs to prevent cache stampedes where many keys expire at once and overwhelm the database.

Practice what you learned

Was this page helpful?

Topics covered

#Redis#RedisStudyNotes#Database#CachingStrategiesWithRedis#Caching#Strategies#Cache#Aside#StudyNotes#SkillVeris