Stop Just Using Redis: A Complete Spring Boot + Redis + Docker Caching Guide
The article explains why naïve reliance on databases causes performance bottlenecks, introduces cache fundamentals, details Redis’s strengths, and walks through a full Spring Boot‑Redis integration deployed with Docker, including configuration, annotations, and practical API testing.
Cache fundamentals
A cache is a small, fast, short‑lived key‑value store whose sole purpose is to trade space for time, reducing accesses to the slowest resource—typically the database. Frequently accessed, rarely changed data (product details, system configuration, dictionaries, user basics) cause database connection saturation, high response times, and reduced throughput when repeatedly queried.
Cache workflow
Request arrives.
Check the cache first.
If data exists, return it (Cache Hit).
If not, query the database, write the result into the cache, then return it (Cache Miss).
Why Redis is the de‑facto standard
Redis (Remote Dictionary Server) is an in‑memory key‑value store that supports multiple data structures (String, Hash, List, Set, ZSet) and offers high performance with low latency. These characteristics make it suitable for caching, session management, leaderboards, and distributed locks.
Integrating Redis with Spring Boot
Add the starter dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>The starter automatically configures Redis connection handling, RedisTemplate, cache abstraction, and a default serialization mechanism.
Running Redis with Docker
services:
redis:
image: redis:7.4.2
container_name: redis-cache
ports:
- "6379:6379"Start with docker compose up -d.
Spring Boot configuration
Configure the data source and cache type in application.yml:
spring:
application:
name: spring-boot-redis-cache
datasource:
url: jdbc:h2:mem:testdb
driver-class-name: org.h2.Driver
username: sa
password: password
cache:
type: redis
data:
redis:
host: localhost
port: 6379Enable caching in the main class:
@SpringBootApplication
@EnableCaching
public class RedisCacheApplication {
public static void main(String[] args) {
SpringApplication.run(RedisCacheApplication.class, args);
}
}Redis cache configuration
package com.icoderoad.redis.config;
import com.icoderoad.redis.dto.ProductDto;
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.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import java.time.Duration;
@Configuration
public class RedisConfig {
@Bean
public RedisCacheManager redisCacheManager(RedisConnectionFactory factory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(10))
.disableCachingNullValues()
.serializeValuesWith(
RedisSerializationContext.SerializationPair.fromSerializer(
new Jackson2JsonRedisSerializer<>(ProductDto.class)));
return RedisCacheManager.builder(factory)
.cacheDefaults(config)
.build();
}
}This configuration sets a TTL of 10 minutes, disables caching of null values, and uses JSON serialization for readability and compatibility.
Service layer with cache annotations
package com.icoderoad.redis.service;
import com.icoderoad.redis.dto.ProductDto;
import com.icoderoad.redis.entity.Product;
import com.icoderoad.redis.repository.ProductRepository;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
@Service
public class ProductService {
private static final String CACHE_NAME = "PRODUCT_CACHE";
private final ProductRepository repository;
public ProductService(ProductRepository repository) {
this.repository = repository;
}
@CachePut(value = CACHE_NAME, key = "#result.id")
public ProductDto create(ProductDto dto) {
Product product = new Product(dto.name(), dto.price());
Product saved = repository.save(product);
return new ProductDto(saved.getId(), saved.getName(), saved.getPrice());
}
@Cacheable(value = CACHE_NAME, key = "#id")
public ProductDto findById(Long id) {
Product product = repository.findById(id)
.orElseThrow(() -> new IllegalArgumentException("Product not found: " + id));
return new ProductDto(product.getId(), product.getName(), product.getPrice());
}
@CachePut(value = CACHE_NAME, key = "#result.id")
public ProductDto update(ProductDto dto) {
Product product = repository.findById(dto.id())
.orElseThrow(() -> new IllegalArgumentException("Product not found"));
product.setName(dto.name());
product.setPrice(dto.price());
Product updated = repository.save(product);
return new ProductDto(updated.getId(), updated.getName(), updated.getPrice());
}
@CacheEvict(value = CACHE_NAME, key = "#id")
public void delete(Long id) {
repository.deleteById(id);
}
}Cache annotation summary
@Cacheable: checks the cache first; method executes only on a miss. @CachePut: forces the method result to be written into the cache after execution. @CacheEvict: removes the specified entry from the cache.
Direct CacheManager usage
Cache cache = cacheManager.getCache("PRODUCT_CACHE");
cache.put(productId, product);Using CacheManager provides more flexibility, while annotations give concise syntax.
API testing
POST http://localhost:8080/api/product
GET http://localhost:8080/api/product/{id}
PUT http://localhost:8080/api/product
DELETE http://localhost:8080/api/product/{id}The first GET request queries the database; subsequent GET requests retrieve the data directly from Redis, demonstrating a cache hit.
Result
Introducing Redis as a cache reduces database pressure dramatically, improves response times, and enables the system to serve many more users without additional hardware.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
LuTiao Programming
LuTiao Programming is a friendly community offering free programming lessons. We inspire learners to explore new ideas and technologies and quickly acquire job-ready skills.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
