Caching improves performance, but it introduces a challenge:
cache consistency. When data changes in the database, the cached version must also be updated or removed. Otherwise, users may receive outdated data.
This problem is known as
cache invalidation, and it is one of the most important topics when using Redis with Spring Boot.
This article explains common cache invalidation patterns and how to implement them.
1. Why Cache Invalidation Matters
Example scenario:
User updates product price → Database updated
↓
Redis cache still stores old price
If the cache is not updated or removed, applications return incorrect data.
Cache invalidation ensures that cached data always reflects the latest state.
2. Cache Eviction Pattern
The
cache eviction pattern removes cached data when it becomes invalid.
Spring Boot provides
@CacheEvict for this purpose.
Example:
@CacheEvict(value = "products", key = "#id")
public void deleteProduct(Long id) {
productRepository.deleteById(id);
}
How it works:
- Product is deleted from the database.
- Redis cache entry is removed.
- Future requests fetch fresh data from the database.
3. Cache Update Pattern
Instead of removing the cache, the application updates it.
Example using
@CachePut:
@CachePut(value = "products", key = "#product.id")
public Product updateProduct(Product product) {
return productRepository.save(product);
}
This ensures Redis always contains the newest data.
Advantages:
- No cache misses after update
- Faster response times
4. Time-Based Cache Expiration (TTL)
Another simple strategy is using
time-based expiration.
Example Redis configuration:
@Bean
public RedisCacheConfiguration cacheConfiguration() {
return RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(10));
}
After 10 minutes, cached data automatically expires.
Pros:
- Easy to implement
- Prevents stale data
Cons:
- Data may remain outdated until expiration.
5. Event-Based Cache Invalidation
In distributed systems, multiple services may use the same cache.
When data changes, all services must clear their cache.
Redis
Pub/Sub can notify services to invalidate cache entries.
Example concept:
Service A updates product
↓
Publish event to Redis
↓
All services clear local cache
This ensures consistency across multiple instances.
6. Bulk Cache Eviction
Sometimes many cache entries must be cleared.
Example:
@CacheEvict(value = "products", allEntries = true)
public void refreshCache() {
}
Use cases:
- Large catalog updates
- Data migrations
- System resets
7. Preventing Cache Inconsistency
Common best practices:
- Always update cache after database writes
- Use short TTL values for dynamic data
- Combine eviction and update strategies
- Monitor cache hit and miss rates
These practices reduce stale data problems.
8. Real Production Example
Typical e-commerce flow:
Product updated
↓
Update database
↓
Evict Redis cache
↓
Next request rebuilds cache
This is the
cache-aside invalidation strategy, widely used in production systems.
image quote pre code