Redis Patterns for Spring Boot
Cache Consistency Checklist
ReactiveRedisTemplate Config
@Bean
public ReactiveRedisTemplate<String, Object> reactiveRedisTemplate(
ReactiveRedisConnectionFactory factory) {
var serializer = new Jackson2JsonRedisSerializer<>(objectMapper(), Object.class);
var context = RedisSerializationContext.<String, Object>newSerializationContext(
new StringRedisSerializer())
.value(serializer).hashKey(new StringRedisSerializer())
.hashValue(serializer).build();
return new ReactiveRedisTemplate<>(factory, context);
}
Cache-Aside Pattern
public Mono<UserProfile> getUserById(String userId) {
String key = "user:profile:" + userId;
return redisTemplate.opsForValue().get(key).cast(UserProfile.class)
.switchIfEmpty(Mono.defer(() -> userRepository.findById(userId)
.flatMap(u -> redisTemplate.opsForValue()
.set(key, u, Duration.ofMinutes(30)).thenReturn(u))));
}
// Invalidate on write
public Mono<UserProfile> updateUser(String userId, UserProfile updated) {
return userRepository.save(updated)
.flatMap(saved -> redisTemplate.delete("user:profile:" + userId).thenReturn(saved));
}
Distributed Locking (Redisson)
public <T> Mono<T> executeWithLock(String resourceId, Duration wait, Duration lease, Mono<T> task) {
RLockReactive lock = redissonClient.reactive().getLock("lock:" + resourceId);
return Mono.usingWhen(
lock.tryLock(wait.toMillis(), lease.toMillis(), TimeUnit.MILLISECONDS)
.flatMap(acquired -> acquired
? Mono.just(lock)
: Mono.error(new LockAcquisitionException(resourceId))),
acquired -> task,
acquired -> acquired.unlock()
);
}
Data Structure Selection
| Structure |
Use Case |
Example |
| String |
Counters, tokens |
Session tokens, flags |
| Hash |
Object storage |
User profiles |
| Set |
Unique collections |
Online users, tags |
| Sorted Set |
Rankings, rate limits |
Leaderboards |
| HyperLogLog |
Cardinality (~0.81% error) |
Unique visitors |
| Stream |
Durable event log |
Audit, event sourcing |
Key Naming & TTL
{service}:{entity}:{id} → user:profile:12345
cache:{entity}:{id} → cache:product:SKU-100
lock:{entity}:{id} → lock:order:ORD-001
| Strategy |
TTL |
Use Case |
| Fixed |
30 min |
API responses |
| Sliding |
Refresh on access |
Sessions |
| Event-driven |
Invalidate on write |
Catalog |
| Jittered |
base +/- random |
Prevents thundering herd |
Anti-Patterns
- No TTL → memory leak. Always set expiry.
KEYS * in production → use SCAN.
- Big keys (>1MB) → split into hash fields.
- Hot keys → shard with suffix, sum on read.
- No stampede protection → lock on cache miss.
References
- references/caching.md — Write-behind, Spring Cache, reactive @Cacheable AOP, eviction, bulk ops
- references/advanced-patterns.md — Rate limiter (Lua), Pub/Sub + SSE, Redis Streams, consumer groups
- references/data-structures.md — Hash/Set/SortedSet/HyperLogLog patterns, leaderboard, serialization
- references/cluster-testing.md — Sentinel/Cluster config, hash tags, session management, Testcontainers
Related Skills
- summer-ratelimit — Summer Framework rate limiting backed by Redis
- spring-webflux-patterns — WebFlux reactive chains that consume Redis caching
- database-patterns — Cache-aside pattern complements DB queries
- testing-workflow — Testcontainers for Redis integration tests
1---2name: redis-patterns3description: Redis patterns for Java Spring Boot (MVC and WebFlux) — reactive caching, distributed locking, rate limiting with Redis, Lua scripts, pub/sub, and cluster configuration. Use when implementing Redis-based caching, distributed locks, rate limiting, session storage, or any Redis data structure operations in Spring Boot applications.4---56# Redis Patterns for Spring Boot78## Cache Consistency Checklist910- [ ] TTL on all cache entries (no infinite caches)11- [ ] Cache eviction on write/update/delete12- [ ] Cache key includes version or tenant if multi-tenant13- [ ] Null values handled (`unless = "#result == null"`)14- [ ] Serializer configured explicitly (JSON, not Java serialization)15- [ ] Connection pool sized (Lettuce pool config)16- [ ] Redis Sentinel or Cluster for HA in production1718## ReactiveRedisTemplate Config1920```java21@Bean22public ReactiveRedisTemplate<String, Object> reactiveRedisTemplate(23 ReactiveRedisConnectionFactory factory) {24 var serializer = new Jackson2JsonRedisSerializer<>(objectMapper(), Object.class);25 var context = RedisSerializationContext.<String, Object>newSerializationContext(26 new StringRedisSerializer())27 .value(serializer).hashKey(new StringRedisSerializer())28 .hashValue(serializer).build();29 return new ReactiveRedisTemplate<>(factory, context);30}31```3233## Cache-Aside Pattern3435```java36public Mono<UserProfile> getUserById(String userId) {37 String key = "user:profile:" + userId;38 return redisTemplate.opsForValue().get(key).cast(UserProfile.class)39 .switchIfEmpty(Mono.defer(() -> userRepository.findById(userId)40 .flatMap(u -> redisTemplate.opsForValue()41 .set(key, u, Duration.ofMinutes(30)).thenReturn(u))));42}43// Invalidate on write44public Mono<UserProfile> updateUser(String userId, UserProfile updated) {45 return userRepository.save(updated)46 .flatMap(saved -> redisTemplate.delete("user:profile:" + userId).thenReturn(saved));47}48```4950## Distributed Locking (Redisson)5152```java53public <T> Mono<T> executeWithLock(String resourceId, Duration wait, Duration lease, Mono<T> task) {54 RLockReactive lock = redissonClient.reactive().getLock("lock:" + resourceId);55 return Mono.usingWhen(56 lock.tryLock(wait.toMillis(), lease.toMillis(), TimeUnit.MILLISECONDS)57 .flatMap(acquired -> acquired58 ? Mono.just(lock)59 : Mono.error(new LockAcquisitionException(resourceId))),60 acquired -> task,61 acquired -> acquired.unlock()62 );63}64```6566## Data Structure Selection6768| Structure | Use Case | Example |69|-----------|----------|---------|70| String | Counters, tokens | Session tokens, flags |71| Hash | Object storage | User profiles |72| Set | Unique collections | Online users, tags |73| Sorted Set | Rankings, rate limits | Leaderboards |74| HyperLogLog | Cardinality (~0.81% error) | Unique visitors |75| Stream | Durable event log | Audit, event sourcing |7677## Key Naming & TTL7879```80{service}:{entity}:{id} → user:profile:1234581cache:{entity}:{id} → cache:product:SKU-10082lock:{entity}:{id} → lock:order:ORD-00183```8485| Strategy | TTL | Use Case |86|----------|-----|----------|87| Fixed | 30 min | API responses |88| Sliding | Refresh on access | Sessions |89| Event-driven | Invalidate on write | Catalog |90| Jittered | base +/- random | Prevents thundering herd |9192## Anti-Patterns9394- No TTL → memory leak. Always set expiry.95- `KEYS *` in production → use `SCAN`.96- Big keys (>1MB) → split into hash fields.97- Hot keys → shard with suffix, sum on read.98- No stampede protection → lock on cache miss.99100## References101102- **[references/caching.md](references/caching.md)** — Write-behind, Spring Cache, reactive @Cacheable AOP, eviction, bulk ops103- **[references/advanced-patterns.md](references/advanced-patterns.md)** — Rate limiter (Lua), Pub/Sub + SSE, Redis Streams, consumer groups104- **[references/data-structures.md](references/data-structures.md)** — Hash/Set/SortedSet/HyperLogLog patterns, leaderboard, serialization105- **[references/cluster-testing.md](references/cluster-testing.md)** — Sentinel/Cluster config, hash tags, session management, Testcontainers106107## Related Skills108109- **summer-ratelimit** — Summer Framework rate limiting backed by Redis110- **spring-webflux-patterns** — WebFlux reactive chains that consume Redis caching111- **database-patterns** — Cache-aside pattern complements DB queries112- **testing-workflow** — Testcontainers for Redis integration tests