Caching with Redis and Spring Data Redis in Spring Boot
Caching is one of the most effective ways to improve the performance and scalability of modern applications. Instead of querying the database for the same data repeatedly, you can temporarily store frequently accessed data in memory and serve it almost instantly.
One of the most popular technologies for implementing caching is Redis, an in-memory data store known for its speed, flexibility, and reliability.
In this article, you'll learn:
- What caching is
- Why Redis is an excellent caching solution
- How Spring Data Redis works
- How to configure Redis in Spring Boot
- How to use
@Cacheable,@CachePut, and@CacheEvict - Best practices for production applications
Let's get started.
What is Caching?
Caching is the process of storing copies of frequently accessed data in a fast storage layer (cache), so future requests can be served without querying the database.
Without caching:
Client
│
▼
Spring Boot
│
▼
Database
Every request reaches the database.
With caching:
Client
│
▼
Spring Boot
│
▼
Redis Cache
│
Cache Hit?
│ │
Yes No
│ │
▼ ▼
Return Database
│
▼
Store in Redis
The first request loads data from the database and stores it in Redis.
Subsequent requests are served directly from Redis.
This dramatically reduces latency and database load.
Why Use Redis for Caching?
Redis is one of the fastest key-value stores available because it stores data entirely in memory.
Some major advantages include:
- Extremely low latency
- High throughput
- Rich data structures
- Optional persistence
- Replication support
- Redis Cluster support
- Automatic expiration (TTL)
- Pub/Sub messaging
- Transactions
Because of these features, Redis is widely used in high-performance systems.
What is Spring Data Redis?
Spring Data Redis is part of the Spring Data ecosystem.
It provides:
- Easy Redis integration
- Repository support
- RedisTemplate API
- Spring Cache integration
- Serialization support
- Object mapping
- Connection management
Instead of writing Redis commands manually, Spring Boot handles most of the complexity for you.
Project Dependencies
Add the following dependencies.
<dependencies>
<!-- Spring Data Redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- Spring Cache -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- Jedis Client -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
</dependencies>
Note: Newer Spring Boot versions use Lettuce as the default Redis client. Unless you specifically need Jedis, Lettuce is generally recommended for production.
Running Redis
Using Docker:
docker run -d \
--name redis \
-p 6379:6379 \
redis:latest
Verify Redis is running:
docker ps
Configure Redis
Create a configuration class.
@Configuration
public class RedisConfig {
@Bean
public RedisConnectionFactory redisConnectionFactory() {
return new JedisConnectionFactory();
}
@Bean
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory());
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(
new GenericJackson2JsonRedisSerializer());
return template;
}
}
Configure application.properties
spring.data.redis.host=localhost
spring.data.redis.port=6379
If authentication is enabled:
spring.data.redis.password=your-password
Enable Caching
Simply add @EnableCaching to your Spring Boot application.
@SpringBootApplication
@EnableCaching
public class RedisCacheApplication {
public static void main(String[] args) {
SpringApplication.run(RedisCacheApplication.class, args);
}
}
Now Spring Boot can automatically manage your cache.
Create the Product Entity
@RedisHash("Product")
public class Product {
@Id
private String id;
private String name;
private double price;
// getters and setters
}
Create the Repository
public interface ProductRepository
extends CrudRepository<Product, String> {
}
Nothing else is required.
Spring Data generates the implementation automatically.
Using Cache Annotations
Spring provides three important annotations:
| Annotation | Purpose |
|---|---|
@Cacheable |
Reads from cache before executing the method |
@CachePut |
Updates both database and cache |
@CacheEvict |
Removes data from cache |
Product Service
@Service
public class ProductService {
@Autowired
private ProductRepository repository;
@Cacheable(value = "products", key = "#id")
public Product getProductById(String id) {
System.out.println("Fetching from database...");
return repository.findById(id).orElse(null);
}
@CachePut(value = "products", key = "#product.id")
public Product updateProduct(Product product) {
return repository.save(product);
}
@CacheEvict(value = "products", key = "#id")
public void deleteProduct(String id) {
repository.deleteById(id);
}
}
Understanding @Cacheable
When the following method is called:
productService.getProductById("1");
Spring checks Redis.
If the key exists
Redis
│
Found
│
Return cached object
The database is never accessed.
If the key does not exist
Database
│
Load object
│
Store in Redis
│
Return object
Future requests will use Redis.
Understanding @CachePut
@CachePut(value = "products", key = "#product.id")
Unlike @Cacheable, this annotation always executes the method.
Workflow:
Update Database
↓
Update Redis
↓
Return Updated Object
This ensures your cache remains synchronized.
Understanding @CacheEvict
@CacheEvict(value = "products", key="#id")
When deleting a product:
Delete Database Record
↓
Remove Redis Cache
↓
Done
Without cache eviction, users could still receive stale data.
Testing the Cache
@Component
public class CacheTestRunner implements CommandLineRunner {
@Autowired
private ProductService productService;
@Override
public void run(String... args) {
Product product = new Product();
product.setId("1");
product.setName("Laptop");
product.setPrice(999.99);
productService.updateProduct(product);
System.out.println(productService.getProductById("1"));
System.out.println(productService.getProductById("1"));
product.setName("Gaming Laptop");
productService.updateProduct(product);
System.out.println(productService.getProductById("1"));
productService.deleteProduct("1");
System.out.println(productService.getProductById("1"));
}
}
Expected Output
Fetching from database...
Product{id='1', name='Laptop'}
Product{id='1', name='Laptop'}
Product{id='1', name='Gaming Laptop'}
Fetching from database...
null
Notice that "Fetching from database..." appears only when the cache misses.
Cache Lifecycle
Client Request
│
▼
Check Redis
│
┌────┴────┐
│ │
Hit Miss
│ │
▼ ▼
Return Query DB
│
▼
Save in Redis
│
▼
Return Response
Production Best Practices
1. Use TTL (Time-To-Live)
Avoid keeping cached data forever.
Example:
spring.cache.redis.time-to-live=10m
2. Cache Only Frequently Accessed Data
Good candidates include:
- Product catalog
- User profiles
- Categories
- Settings
- Configuration
- Country lists
Avoid caching highly volatile data unless necessary.
3. Use Meaningful Cache Names
Instead of:
value="cache1"
Prefer:
value="products"
or
value="users"
4. Serialize JSON
Using:
GenericJackson2JsonRedisSerializer
makes cached objects easier to inspect and maintain.
5. Monitor Cache Performance
Keep an eye on:
- Cache hit rate
- Cache miss rate
- Memory usage
- Eviction count
- Response time
Monitoring helps optimize your caching strategy.
Common Cache Annotations
@Cacheable
Reads from cache.
@CachePut
Updates cache.
@CacheEvict
Deletes cache.
@Caching
Combines multiple cache operations.
@CacheConfig
Defines shared cache configuration.
Advantages of Redis Caching
- Faster response times
- Reduced database load
- Better scalability
- Improved user experience
- Lower infrastructure costs
- High availability
- Distributed caching support
When Should You Use Redis?
Redis is an excellent choice when your application needs:
- Frequently accessed data
- High read traffic
- Low latency
- Distributed caching
- Session storage
- API response caching
- Rate limiting
- Leaderboards
- Real-time analytics
Conclusion
Caching is one of the simplest yet most impactful optimizations you can make in a Spring Boot application. By integrating Redis with Spring Data Redis, you can significantly reduce database load, improve response times, and build applications that scale efficiently under heavy traffic.
Spring Boot's caching abstraction makes implementation straightforward with annotations like @Cacheable, @CachePut, and @CacheEvict, allowing you to focus on business logic rather than cache management.
Whether you're building a REST API, microservices architecture, or a high-traffic web application, Redis caching is a proven strategy to boost performance and deliver a better user experience.
If you're working with Spring Boot in production, learning Redis is a skill that will pay dividends across many real-world applications.
Happy Coding!
Top comments (0)