Hello! Welcome back to our series on backend development.
In our last lesson, we took our first step into improving application performance by implementing method-level caching. We used a simple in-memory cache, which is great for a single application instance. However, as you look toward building enterprise-level applications, you'll often have multiple instances of your service running simultaneously for scalability and reliability. This is where our in-memory approach falls short—each instance would have its own separate, inconsistent cache.
Today, we'll solve this problem by integrating a distributed cache. This lesson will teach you how to configure and integrate Redis, a powerful and popular in-memory data store, into your Spring Boot application. This ensures that all instances of your application share a single, consistent cache, a crucial step for building scalable and robust systems.
1. Why a Distributed Cache? The Role of Redis
A distributed cache is an external service that is shared by all instances of your application. When one instance caches a piece of data, all other instances can immediately benefit from it. This eliminates data inconsistency and redundant work.

Redis is a popular choice for a distributed cache because it's an extremely fast, in-memory key-value store. It's often referred to as a "data structure server" because its values can be more than just simple strings—they can be lists, sets, hashes, and more. For our purposes, we'll use it to store the results of our service method calls.
2. Setting Up a Local Redis Instance with Docker
Before our application can connect to Redis, we need a Redis server running. The simplest way to get one for local development is by using Docker. As you move towards building microservices and containerized applications, becoming comfortable with Docker is an essential skill.
Spring Boot Redis Multi-Cache: A Complete Guide
The following resource provides a docker-compose.yml file to easily run a Redis container. We will use this to set up our local development environment.
Please review the section titled "7️⃣ Docker Setup". You don't need to understand every detail of the docker-compose.yml file yet. The key is to understand that this file defines a service named redis using the official Redis image. Then, save this file as docker-compose.yml in your project's root directory and run the command docker-compose up -d in your terminal from that directory. This will start a Redis server in the background, listening on port 6379.
After running the command, you'll have a Redis instance ready and waiting for your application to connect.
3. Configuring Spring Boot to Use Redis
Now let's configure our Spring Boot application to use this Redis server for caching.
Step 1: Add Dependencies
First, we need to add the necessary dependencies to our pom.xml. This includes the caching starter we used before, plus a new one for Redis data access.
The Baeldung article, "Spring Boot Cache with Redis," clearly lists the required Maven dependencies.
In the section titled "2. Dependencies", find the two <dependency> blocks for spring-boot-starter-cache and spring-boot-starter-data-redis and add them to your pom.xml file.
After adding these, your IDE should prompt you to reload your Maven project to download the new libraries.
Step 2: Set Application Properties
Next, we need to tell Spring Boot that we want to use Redis for caching and where to find our Redis server. This is done in your application.properties (or application.yml) file.
Spring Boot Redis Multi-Cache: A Complete Guide
The "Spring Boot Redis Multi-Cache" guide provides the necessary properties to configure the Redis connection.
Look at the section "6️⃣ Application Configuration". Find the Redis-related properties and add them to your application.properties file. The most important one is spring.cache.type=redis. The host and port should match our Docker setup (localhost and 6379).
Your properties should look like this:
# Tell Spring Boot to use Redis for caching
spring.cache.type=redis
# Connection details for our local Redis server
spring.redis.host=localhost
spring.redis.port=6379
With just these dependencies and properties, Spring Boot's auto-configuration is smart enough to create a RedisCacheManager for you. Your existing @Cacheable annotations will now automatically use Redis!
4. Fine-Grained Control with Java Configuration
While Spring's auto-configuration is convenient, you'll often want more control over caching behavior, such as:
- Serialization: How Java objects are stored in Redis. The default is binary, which isn't human-readable. Storing as JSON is often better for debugging.
- Time-To-Live (TTL): How long a cache entry should live before it expires. You might want different TTLs for different types of data.
To customize this, we can define our own CacheManager bean, just as we did in the previous lesson, but this time for Redis.
Spring Boot Redis Multi-Cache: A Complete Guide
This is the core of our lesson. The following RedisConfig class from the guide is a perfect example of a production-ready cache configuration. Let's study it in detail.
Read the code in the section "5️⃣ Redis Configuration with TTL" carefully. We will break it down below. The goal is to understand how it sets a default TTL, configures JSON serialization, and defines custom TTLs for specific caches named users and profiles.
Let's dissect this configuration class. You would add this class to your project (e.g., in a config package).
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
import java.util.Map;
@Configuration
@EnableCaching // Don't forget this! It enables Spring's cache-handling proxy mechanism.
public class RedisConfig {
@Bean
public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
// 1. Configure a default behavior for all caches
RedisCacheConfiguration defaultConfig = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(30)) // Default TTL is 30 minutes
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())); // Serialize values as JSON
// 2. Configure specific behaviors for individual caches
Map<String, RedisCacheConfiguration> cacheConfigurations = Map.of(
"users", defaultConfig.entryTtl(Duration.ofMinutes(5)), // "users" cache has a 5-minute TTL
"profiles", defaultConfig.entryTtl(Duration.ofMinutes(10)) // "profiles" cache has a 10-minute TTL
);
// 3. Build the CacheManager with the defined configurations
return RedisCacheManager.builder(connectionFactory)
.cacheDefaults(defaultConfig) // Apply the default config
.withInitialCacheConfigurations(cacheConfigurations) // Apply the per-cache configs
.build();
}
}
Key Points from this Configuration:
- Serialization: We configure keys to be stored as simple strings and values to be stored as JSON. This is incredibly helpful for debugging, as you'll see in the next section.
- Default TTL: Any cache that isn't specifically configured (e.g.,
@Cacheable("orders")) will have its entries expire after 30 minutes. - Per-Cache TTL: We explicitly set shorter TTLs for
usersandprofiles. This level of control is essential in real-world applications where some data changes more frequently than other data.
Test your understanding!
Based on the RedisConfig class we just discussed, if you cache a method result in a new cache named orders (e.g., by annotating a method with @Cacheable("orders")), what will its Time-To-Live (TTL) be? Why?
Show answer
The TTL for the orders cache will be 30 minutes. This is because no specific configuration was provided for the "orders" cache in the cacheConfigurations map. Therefore, it falls back to using the defaultConfig, which has an entryTtl of 30 minutes.
5. Verifying the Redis Integration
How do we know it's working? With our previous in-memory cache, we could only rely on log messages and timing. With Redis, we can directly inspect the cache.
Let's assume you have a UserService with a method like this:
@Service
public class UserService {
@Cacheable(value = "users", key = "#id")
public User getUserById(Long id) {
log.info("Fetching user with id: {} from database", id);
// ... database logic to fetch the user
}
}
- Call the endpoint that triggers this method for the first time (e.g.,
GET /users/1). You will see the log message "Fetching user...". - Call the same endpoint again. The log message will not appear, and the response will be much faster. This confirms caching is active.
- Now, let's prove the data is in Redis.
Spring Boot Redis Multi-Cache: A Complete Guide
The final step is to look inside Redis itself. The guide shows us how to use the Redis Command Line Interface (CLI) to inspect our cache.
Follow the instructions in the section "9️⃣ Redis Cache Inspection". First, connect to the Redis CLI using the docker exec command. Then, try the commands keys *, get <key>, and ttl <key> to see your cached data, its value (in JSON format!), and its remaining time-to-live.
When you run keys * in the Redis CLI, you should see a key like users::1. The key is constructed from the cache name (users) and the key you provided (#id). When you run get "users::1", you will see the full User object serialized as a JSON string. Finally, ttl "users::1" will show you the remaining lifespan of that cache entry in seconds, which will be less than 300 (5 minutes).
Conclusion
Congratulations! You have successfully leveled up your caching strategy from a simple, single-instance solution to a robust, distributed one suitable for scalable, enterprise-grade applications.
Key Takeaways:
- A distributed cache like Redis solves the cache consistency problem when running multiple instances of an application.
- Integrating Redis with Spring Boot requires adding dependencies (
spring-boot-starter-data-redis) and setting properties (spring.cache.type=redis). - For fine-grained control over TTL and serialization, you can create a custom
RedisCacheManagerbean. Using JSON serialization is highly recommended for easier debugging. - The same
@Cacheableannotation works seamlessly, but now the cache is shared and persistent outside your application. - You can and should verify your cache's state directly using the Redis CLI.
What's Next?
With robust caching in place, your application is now more performant and scalable. In our next lesson, we will explore another critical pattern for building responsive systems: asynchronous processing. You'll learn how to use Spring's @Async annotation to run long-running tasks in the background, preventing them from blocking critical API requests and improving the user experience.