skip to content

Redis as Cache & Pub/Sub

Redis can back the Spring cache abstraction through RedisCacheManager and carry pub/sub messages through a listener container. Interviewers ask about TTLs and serialization, since a shared cache outlives any one deployment.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

questions

5

How does Redis back the Spring Cache abstraction, and what is the role of RedisCacheManager?

level: juniorimportance: must knowfreq 60%

answer

  1. CacheManager SPI -> RedisCacheManager -> RedisCache
  2. key = cacheName::key
  3. distributed + survives restart + TTL
  4. builder(connectionFactory).cacheDefaults(...)
  5. default value serializer = JDK Serializable

basics

~20 s

RedisCacheManager is the CacheManager implementation that stores cache entries in Redis. When you use @Cacheable, Spring asks it for a RedisCache, which reads/writes values as Redis keys so cached data survives restarts and is shared across instances.

solid answer

~30 s

Spring's caching abstraction (@Cacheable/@CacheEvict) delegates to a CacheManager. With spring-boot-starter-data-redis on the classpath and spring.cache.type=redis (auto-selected if only Redis caching is present), Boot auto-configures a RedisCacheManager backed by your RedisConnectionFactory. It hands out RedisCache instances, one per cache name. Each cache entry becomes a Redis key like cacheName::key, holding the serialized value. Because the store is Redis (out of process), the cache is distributed — shared across all app instances — and survives application restarts, unlike an in-JVM ConcurrentMapCacheManager. You typically customize it with a RedisCacheConfiguration to set TTL, key serializer, value serializer, and key prefixing.

code

java · 18 lines
java
@Configuration
@EnableCaching
public class CacheConfig {

    @Bean
    public RedisCacheManager cacheManager(RedisConnectionFactory cf) {
        return RedisCacheManager
                .builder(cf)
                .cacheDefaults(RedisCacheConfiguration.defaultCacheConfig())
                .build();
    }
}

@Service
class BookService {
    @Cacheable("books")               // stored in Redis under books::<isbn>
    public Book findByIsbn(String isbn) { /* slow DB call */ return null; }
}

go deeper

for a junior

Know that RedisCacheManager is a CacheManager that puts cache entries in Redis, giving a shared cache that survives restarts.

for a middle

Explain the CacheManager/Cache SPI, the cacheName::key layout, and Boot auto-config from the connection factory.

for a senior

Contrast with ConcurrentMapCacheManager, discuss serialization defaults, lazy cache creation, and clear() semantics.

for a principal

Reason about failure modes (Redis outage), serialization strategy across services, and when a distributed cache is worth the round-trip cost.

**The layering.** Spring Cache is an *abstraction*: annotations like `@Cacheable`, `@CachePut`, `@CacheEvict` and `@EnableCaching` (this abstraction itself is a sibling topic) don't know anything about Redis. At the bottom sits an SPI: `org.springframework.cache.CacheManager`, which returns `org.springframework.cache.Cache` instances by name. Redis participates by supplying implementations of these two interfaces: `RedisCacheManager` (implements `CacheManager`) and `RedisCache` (implements `Cache`). Swapping the backing store is just swapping the `CacheManager` bean — application code with `@Cacheable` is untouched. **Auto-configuration.** With `spring-boot-starter-data-redis` on the classpath, Boot configures a `RedisConnectionFactory` (Lettuce by default). If caching is enabled (`@EnableCaching`) and `spring.cache.type` resolves to `redis`, `RedisCacheConfiguration`/`RedisCacheManager` beans are auto-created from that connection factory. You can inject and reuse the same factory. **What a cache entry looks like in Redis.** For cache name `books` and key `978-1`, `RedisCache` writes a Redis key `books::978-1` (the `::` separator and the `cacheName::` prefix are the default `CacheKeyPrefix`). The value is the serialized cached object. Reads do a `GET`, writes a `SET` (with `PX`/TTL if configured), evicts a `DEL`, and `@CacheEvict(allEntries=true)` does a scan-and-delete over the prefix (`clear()`), not a `FLUSHDB`. **Why Redis vs in-memory.** The default `ConcurrentMapCacheManager` keeps entries in a JVM `ConcurrentHashMap`: fast, but per-instance and lost on restart, and it grows unbounded (no TTL). A `RedisCacheManager` gives you a **distributed** cache (all app nodes share entries, so a cache-fill on node A is visible to node B), **persistence across restarts**, and native **TTL/expiry**. The cost is network round-trips and serialization. **Building one manually.** ```java RedisCacheManager cacheManager = RedisCacheManager .builder(connectionFactory) .cacheDefaults(RedisCacheConfiguration.defaultCacheConfig()) .build(); ``` `RedisCacheManager.builder(...)` (a.k.a. `RedisCacheManagerBuilder`) is the fluent entry point; `.cacheDefaults(...)` sets the default `RedisCacheConfiguration` applied to every cache without a specific override. **Gotchas.** (1) Values must be serializable by whichever `RedisSerializer` you configure — the default value serializer is JDK serialization (`SerializationPair.fromSerializer(new JdkSerializationRedisSerializer())`), so cached types must implement `Serializable` unless you switch to JSON. (2) Cache names are created lazily on first use unless you pre-declare them; enabling `disableCreateOnMissingCache()` fails fast on unknown names. (3) Because it's out-of-process, a Redis outage surfaces as exceptions unless you configure resilience.

  • Where does the RedisConnectionFactory come from?
    Spring Boot auto-configures it from spring.data.redis.* properties (host/port/password), using Lettuce by default (Jedis if Lettuce is excluded). RedisCacheManager reuses that same factory.
  • Does @Cacheable code change if you move from ConcurrentMapCacheManager to RedisCacheManager?
    No. The annotations target the CacheManager SPI; only the CacheManager bean changes. That decoupling is the whole point of the abstraction.

saying these in an interview costs you the question

  • Thinking RedisCacheManager stores entries in the JVM heap (it stores them in Redis, out of process)
  • Believing @Cacheable talks to Redis directly rather than through the CacheManager/Cache SPI
  • Assuming @CacheEvict(allEntries=true) runs FLUSHDB (it deletes only keys under the cache's prefix)

context

open as a page

How do you configure TTL, key prefixing, null handling, and serialization for a Redis-backed cache?

level: middleimportance: must knowfreq 65%

basics

~10 s

You build a RedisCacheConfiguration and set options on it: entryTtl(Duration) for expiry, a value serializer (e.g. JSON), disableCachingNullValues(), and a key prefix. You pass it to RedisCacheManager as the default and/or per cache.

open as a page

How do you give different caches different TTLs and serializers within a single RedisCacheManager?

level: middleimportance: should knowfreq 40%

basics

~10 s

Build the manager with RedisCacheManager.builder(cf), set a default via cacheDefaults(cfg), then override specific caches with withInitialCacheConfigurations(Map<name, RedisCacheConfiguration>) — each entry a config with its own TTL/serializer.

open as a page

How do you implement Redis pub/sub messaging in Spring using RedisMessageListenerContainer?

level: seniorimportance: should knowfreq 45%

basics

~10 s

Publish with redisTemplate.convertAndSend(channel, message). To receive, register a RedisMessageListenerContainer bean, give it the connection factory, and addMessageListener(listener, topic) where the listener implements MessageListener (often via a MessageListenerAdapter that delegates to a POJO).

open as a page

You run several app instances with local caches plus a shared Redis cache. How do you keep them consistent and resilient to Redis failures?

level: principalimportance: should knowfreq 30%

basics

~20 s

Use Redis as the shared cache (RedisCacheManager) so all instances see the same entries, and broadcast invalidations via pub/sub (RedisMessageListenerContainer) when local near-caches must be evicted. For failures, wrap the cache with a CacheErrorHandler or circuit breaker so Redis outages degrade to the DB instead of erroring.

open as a page