Cache Invalidation Strategies in Spring Boot with Redis
Fast recall
Quick revision
Core answer
Practical knowledge of cache consistency strategies beyond simple TTLs, especially for distributed systems where multiple app instances share a Redis cache.
Relying solely on TTL for consistency without handling write-triggered invalidation, resulting in stale data for the full TTL duration after an update.
Discuss the two hard problems of computer science joke, then seriously cover versioned cache keys as an alternative to explicit invalidation, and mention cache-tag-based invalidation used by CDNs and frameworks like Symfony.
Interview-ready guide
Interview answer
Cache invalidation is the hard part of caching — knowing when a cached value is stale and needs to be refreshed. The three main strategies are TTL-based expiry, event-driven invalidation, and versioned keys.
TTL — simplest, always slightly stale
- Set a time-to-live on every cache entry —
SET key value EX 300expires after 5 minutes regardless of whether the underlying data changed - TTL is appropriate when some staleness is acceptable: product catalog, configuration settings, reference data that changes infrequently; the application tolerate serving data that's up to 5 minutes old
- The downside: a record updated 10 seconds after caching will serve the old value for the remaining TTL; for writes that must be visible immediately, TTL alone is insufficient
Event-driven invalidation — immediate consistency
- When a record is updated, explicitly delete or update the corresponding cache key —
@CacheEvictin Spring, or a manualredisTemplate.delete(key)call in the service method that performs the write - This is the correct pattern when reads must reflect the latest write promptly — a user updates their profile and the next page load must show the change, not the cached version
- The risk: if the write succeeds but the cache eviction fails (Redis is temporarily unavailable), the cache remains stale; use cache eviction as a best-effort operation, relying on a backup TTL to eventually expire the stale entry
Versioned keys — invalidate by changing the key
- Instead of evicting a key, change the version portion of the key —
product:v2:123replacesproduct:v1:123after an update; the old key is never explicitly deleted, it just stops being referenced and expires via TTL - Useful for bulk invalidation: incrementing a global version prefix invalidates all cache entries of that type without needing to know which specific keys to evict
Cache Invalidation Strategies in Spring Boot with Redis
Direct Answer
Cache invalidation strategies include TTL-based expiry (entries expire automatically after a fixed duration), event-driven invalidation (write operations evict or update specific cache keys), pattern-based bulk eviction (delete all keys matching a prefix), and publish/subscribe invalidation (broadcast eviction commands to all application nodes sharing a distributed cache). Spring's @CacheEvict handles explicit invalidation, but complex invalidation patterns often require direct RedisTemplate usage or a Redis pub/sub broadcast.
Code Example
Event-driven invalidation using Spring's cache annotations:
@Service
public class ProductService {
// Read path: cache the result
@Cacheable(value = "products", key = "#id")
public Product findById(Long id) {
return productRepo.findById(id).orElseThrow();
}
// Write path: evict the stale entry after update
@CacheEvict(value = "products", key = "#product.id")
public Product update(Product product) {
return productRepo.save(product);
}
// Cascading invalidation: category change affects many products
@Caching(evict = {
@CacheEvict(value = "categories", key = "#id"),
@CacheEvict(value = "products", allEntries = true)
})
public void updateCategory(Long id, Category category) {
categoryRepo.save(category);
}
// Pattern-based eviction using RedisTemplate directly
public void evictProductsByPrefix(String prefix) {
Set<String> keys = redisTemplate.keys("products:" + prefix + "*");
if (keys != null && !keys.isEmpty()) {
redisTemplate.delete(keys);
}
}
}
Warning: redisTemplate.keys() uses the KEYS command, which blocks the Redis event loop on large keyspaces. For production, use SCAN via RedisTemplate.execute() with a ScanOptions cursor instead.
Common Mistakes & Pitfalls
Relying solely on TTL for consistency without handling write-triggered invalidation, resulting in stale data for the full TTL duration after an update.
Check your understanding
Answer these without looking back at the article.
- Why should you avoid using KEYS * in production and what is the alternative?
- How do versioned cache keys eliminate the need for explicit invalidation?
- How does Spring's @Caching annotation let you combine @CacheEvict and @CachePut in one method?
Related interview prep categories
1Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.