Spring Boot Cache Example
Last updated: Apr 3, 2026Table of Contents
- โ 1. Design Considerations
- ๐ง General Design
- ๐งฑ Common Caching Patterns
- โ Recommended: Cache-Aside (Spring Boot default pattern)
- โ 2. Ensuring Consistency (DB <-> Cache)
- ๐ Sync Strategies
- ๐ Spring Boot Code Example: Cache Eviction on Update
- โ ๏ธ 3. Bottlenecks & Edge Cases
- ๐ฅ Cache Stampede
- ๐ Cache Inconsistency
- ๐ง Cold Start
- ๐ Serialization Format
- ๐งช Example Caching Flow in Spring Boot (Cache-Aside):
- ๐ง Summary Checklist
Implementing caching via Redis in a Spring Boot + MySQL application can massively boost performance and reduce DB load, but it requires a careful design to avoid stale data, inconsistency, and cache stampede.
โ 1. Design Considerations
๐ง General Design
-
Read-heavy queries โ cacheable in Redis
-
Cache key-value pairs, typically:
- Key:
user:123orproduct:sku:abc - Value: JSON-serialized object (or Hash/String)
- Key:
๐งฑ Common Caching Patterns
| Pattern | Description |
|---|---|
| Read-Through | App checks cache first โ DB fallback on miss โ populate cache |
| Write-Through | Writes to cache and DB at the same time |
| Write-Behind (Async) | Writes to cache, flushes to DB later (riskier) |
| Cache-Aside (Lazy Load) | App reads cache, falls back to DB, populates cache if missed (most common) |
โ Recommended: Cache-Aside (Spring Boot default pattern)
@Cacheable(value = "user", key = "#userId")
public User getUserById(Long userId) {
return userRepository.findById(userId).orElseThrow();
}
โ 2. Ensuring Consistency (DB <-> Cache)
The key challenge: When DB changes, how do we keep Redis cache in sync?
๐ Sync Strategies
| Strategy | How It Works | Pros | Cons |
|---|---|---|---|
Evict on Write (@CacheEvict) |
After DB write, remove the cache entry | Simple, consistent | Requires cache reload on next read |
Update Cache on Write (@CachePut) |
Write to DB and cache together | Fast reads | Risk of partial failure (DB updated but cache not) |
| Message Queue (e.g. Kafka) | Async sync via event stream | Decoupled, scalable | Complexity, potential lag |
| Double Write Transaction | Transactionally update both | Reliable if done right | Harder to manage rollback |
๐ Spring Boot Code Example: Cache Eviction on Update
@CacheEvict(value = "user", key = "#user.id")
public void updateUser(User user) {
userRepository.save(user); // DB write
}
โ ๏ธ 3. Bottlenecks & Edge Cases
๐ฅ Cache Stampede
Many requests hit the DB when cache expires
๐ Cache Inconsistency
DB updated, cache not updated
- Use
@CacheEvict,@CachePut, or transactional messaging - Avoid parallel updates โ lock or debounce as needed
๐ง Cold Start
Cache empty after restart / deployment
- โ Pre-warm cache during boot (optional batch loaders)
๐ Serialization Format
- Redis needs fast serialization
- Prefer JSON (e.g.
Jackson2JsonRedisSerializer) or String over Java native serialization
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
template.setValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class));
return template;
}
๐งช Example Caching Flow in Spring Boot (Cache-Aside):
@GetMapping("/user/{id}")
@Cacheable(value = "user", key = "#id")
public User getUser(@PathVariable Long id) {
return userService.findById(id); // Calls DB only if cache miss
}
@PutMapping("/user")
@CacheEvict(value = "user", key = "#user.id")
public void updateUser(@RequestBody User user) {
userService.update(user); // Removes stale cache
}
๐ง Summary Checklist
| Goal | Technique |
|---|---|
| Improve read performance | Cache-Aside w/ Redis |
| Keep cache & DB in sync | Use @CacheEvict on write |
| Avoid stampede / cold start | Pre-warm or use lock/mutex |
| Handle failures | Use retry logic, fallbacks |
| Choose right TTL | Add random jitter to avoid stampede |
| Fast serialization | Use JSON or String serializers |