Spring Boot Cache Example

Last updated: Apr 3, 2026

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:123 or product:sku:abc
    • Value: JSON-serialized object (or Hash/String)

๐Ÿงฑ 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)
@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

  • โœ… Solution: Use randomized TTL + mutex locks (SETNX)
  • Libraries: Caffeine (local), Redisson

๐Ÿ” 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