Chaturmind
LearnDSASystem DesignBlogPremium
Sign inGet started
Chaturmind

Structured learning paths for engineers who want to go deep. Written by practitioners.

Learn

  • Java
  • DSA
  • System Design
  • Spring Boot
  • AI / ML

Company

  • Blog
  • Premium
  • Contact

Legal

  • Privacy Policy
  • Terms of Service

© 2026 Chaturmind. All rights reserved.

Built for engineers who want to go deep.


← System Design Fundamentals

Scalability Fundamentals

  • Horizontal vs Vertical Scaling
  • Load Balancing
  • Caching Strategies

Databases at Scale

  • CAP Theorem
  • Database Sharding
  • Replication & Consistency
  • Consistent Hashing
Chaturmind
← System Design Fundamentals

Scalability Fundamentals

  • Horizontal vs Vertical Scaling
  • Load Balancing
  • Caching Strategies

Databases at Scale

  • CAP Theorem
  • Database Sharding
  • Replication & Consistency
  • Consistent Hashing
HomeLearnSystem DesignSystem Design FundamentalsCaching
✓ FreeIntermediate· 13 min read

Caching Strategies

Implement cache-aside, write-through, write-behind, and read-through patterns with eviction policies.

Published April 14, 2025


Caching Strategies

Caching stores frequently accessed data in fast storage (memory) to reduce latency and database load. Choosing the right caching strategy determines correctness and consistency.

Cache-Aside (Lazy Loading) — most common

Application manages the cache explicitly.

public User getUser(String userId) {
    // 1. Check cache
    User cached = redis.get("user:" + userId);
    if (cached != null) return cached; // cache hit

    // 2. Cache miss: load from DB
    User user = userRepository.findById(userId);

    // 3. Populate cache
    redis.setex("user:" + userId, 3600, user); // TTL 1 hour
    return user;
}

public void updateUser(String userId, User user) {
    userRepository.save(user);
    redis.del("user:" + userId); // invalidate cache
    // Or: redis.set("user:" + userId, user); // update cache
}

Pros: Only caches data that's actually read; works great for read-heavy workloads. Cons: First request always has a cache miss (cold start). Stale data possible if invalidation is missed.

Write-Through — write to cache AND DB together

public void updateUser(User user) {
    userRepository.save(user);          // write to DB
    redis.set("user:" + user.getId(), user); // write to cache
}

Pros: Cache is always consistent with DB. Cons: Every write pays the DB penalty. Unused cache entries waste space.

Write-Behind (Write-Back) — write to cache, async DB write

Client → Cache (immediate) → Background worker → DB (async)

Pros: Fastest writes; batch DB updates. Cons: Risk of data loss if cache node dies before the async write completes.

Read-Through — cache sits between app and DB

Client → Cache → DB (on miss)
         ↓ auto-populates on miss

Pros: Transparent to the application. Cons: Cache layer must know how to talk to the DB.

Cache Eviction Policies

PolicyDescriptionUse Case
LRURemove Least Recently UsedGeneral purpose
LFURemove Least Frequently UsedFrequency-based access
FIFORemove oldest entrySimple queues
TTLExpire after fixed timeData with known staleness
No evictionReturn error when fullCritical data

Redis default: no eviction (return error). Common config: maxmemory-policy allkeys-lru.

Cache Stampede (Thundering Herd)

When many requests hit the same expired cache key simultaneously, all miss and bombard the database.

// Solution 1: Mutex lock (only one request rebuilds the cache)
public User getUserWithLock(String userId) {
    User cached = redis.get("user:" + userId);
    if (cached != null) return cached;

    String lockKey = "lock:user:" + userId;
    if (redis.setnx(lockKey, "1") == 1) { // acquired lock
        redis.expire(lockKey, 30);
        try {
            User user = userRepository.findById(userId);
            redis.setex("user:" + userId, 3600, user);
            return user;
        } finally {
            redis.del(lockKey);
        }
    }
    // Solution 2: Return stale value while rebuilding
    return redis.get("user:stale:" + userId);
}

Cache Aside with Spring Boot

@Service
public class UserService {

    @Cacheable(value = "users", key = "#userId")
    public User getUser(String userId) {
        return userRepository.findById(userId).orElseThrow();
    }

    @CacheEvict(value = "users", key = "#user.id")
    public User updateUser(User user) {
        return userRepository.save(user);
    }

    @CachePut(value = "users", key = "#user.id") // update cache without evicting
    public User updateUserAndCache(User user) {
        return userRepository.save(user);
    }
}

Interview Tips

  1. Cache-aside is the answer for 90% of interview scenarios — explain it clearly.
  2. Always address cache invalidation — it's the hard part of caching.
  3. Mention TTL as a simple consistency mechanism — even if the cache isn't invalidated perfectly, stale data expires.

Previous

Load Balancing

Next

CAP Theorem

AI Tutor

Lesson: Caching Strategies

Quick actions

AI responses can be inaccurate. Verify critical information.