一、前言
在分布式系统开发中,缓存是提升接口响应速度、减轻数据库压力的关键手段。Redis 作为高性能的键值对数据库,凭借其支持多种数据结构、读写速度快、可持久化等特性,成为 Spring Boot 项目的首选缓存方案。本文将从环境准备、依赖配置、代码实现、缓存策略优化四个维度,带大家从零实现 Spring Boot 与 Redis 的无缝集成,附完整代码示例和常见问题解决方案。
二、环境准备
1. 基础环境
- JDK 1.8+
- Spring Boot 2.7.x(兼容 2.x 版本,3.x 版本配置略有差异)
- Redis 6.2.x(本地或云服务器部署均可)
- Maven 3.6+
2. Redis 环境验证
确保 Redis 服务正常启动,可通过以下命令测试连接:
# 本地 Redis 连接
redis-cli -h 127.0.0.1 -p 6379
# 输入 ping,返回 PONG 则表示连接正常
127.0.0.1:6379> ping
PONG
三、核心配置步骤
1. 引入 Maven 依赖
在 pom.xml 中添加 Spring Boot 整合 Redis 的核心依赖,无需额外引入 Redis 客户端(Spring Boot 自动适配 Lettuce 客户端):
Redis starter -->
<dependency>
>org.springframework.boot
-boot-starter-data-redis</artifactId>
>
依赖(Lettuce 底层依赖 commons-pool2) -->
>
.apache.commons ool2</dependency>
2. 配置 application.yml
在配置文件中添加 Redis 连接信息、连接池配置和缓存通用配置:
spring:
# Redis 配置
redis:
host: 127.0.0.1 # Redis 服务器地址
port: 6379 # 端口号
password: "" # 若未设置密码则留空
database: 0 # 操作的数据库索引(默认 0)
# Lettuce 连接池配置
lettuce:
pool:
max-active: 8 # 最大连接数
max-idle: 8 # 最大空闲连接数
min-idle: 2 # 最小空闲连接数
max-wait: -1ms # 最大等待时间(-1 表示无限制)
# 缓存通用配置
cache:
type: redis # 缓存类型为 Redis
redis:
time-to-live: 3600000ms # 缓存默认过期时间(1 小时)
cache-null-values: false # 是否缓存 null 值(避免缓存穿透)
use-key-prefix: true # 是否使用缓存前缀(默认前缀为 cache:)
3. 自定义 Redis 配置类
Spring Boot 自动配置的 RedisTemplate 序列化方式为 JDK 序列化,存在可读性差、占用空间大的问题。我们通过自定义配置类,将序列化方式改为 JSON 格式:
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.core.RedisTemplate;
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;
@Configuration
@EnableCaching // 开启缓存注解支持
public class RedisConfig {
/**
* 自定义 RedisTemplate:解决默认序列化问题
*/
@Bean
public RedisTemplate, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate, Object> template = new RedisTemplate template.setConnectionFactory(factory);
// 字符串序列化器(key 采用 String 序列化)
StringRedisSerializer stringSerializer = new StringRedisSerializer();
template.setKeySerializer(stringSerializer);
template.setHashKeySerializer(stringSerializer);
// JSON 序列化器(value 采用 JSON 序列化)
GenericJackson2JsonRedisSerializer jsonSerializer = new GenericJackson2JsonRedisSerializer();
template.setValueSerializer(jsonSerializer);
template.setHashValueSerializer(jsonSerializer);
// 初始化模板
template.afterPropertiesSet();
return template;
}
/**
* 自定义 RedisCacheManager:统一缓存配置
*/
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory factory) {
// 缓存配置:JSON 序列化 + 过期时间
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.hours(1)) // 默认过期时间 1 小时
.serializeKeysWith(RedisSerializationContext.SerializationPair
.fromSerializer(new StringRedisSerializer())) // key 序列化
.serializeValuesWith(RedisSerializationContext.SerializationPair
.fromSerializer(new GenericJackson2JsonRedisSerializer())) // value 序列化
.disableCachingNullValues(); // 不缓存 null 值
// 构建缓存管理器
return RedisCacheManager.builder(factory)
.cacheDefaults(config)
.build();
}
}
四、实战:缓存注解的使用
1. 实体类准备
创建用户实体类(需实现序列化接口,或使用 JSON 序列化时无需显式实现):
import lombok.Data;
@Data
public class User {
private Long id;
private String username;
private String phone;
private Integer age;
}
2. Service 层缓存实现
通过 @Cacheable、@CachePut、@CacheEvict 三个核心注解实现缓存的增删改查:
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
@Service
public class UserService {
// 模拟数据库存储
private static final Map> USER_DB = new HashMap<>();
static {
// 初始化测试数据
USER_DB.put(1L, new User(1L, "张三", "13800138000", 25));
USER_DB.put(2L, new User(2L, "李四", "13900139000", 30));
}
/**
* 查询用户:缓存存在则返回缓存数据,不存在则执行方法并缓存结果
* key:缓存键(默认使用方法参数作为 key,这里显式指定为 user::id)
*/
@Cacheable(value = "user", key = "#id", unless = "#result == null")
public User getUserById(Long id) {
System.out.println("===== 执行数据库查询,id:" + id + " =====");
return USER_DB.get(id);
}
/**
* 更新用户:执行方法后,更新缓存(覆盖原缓存)
*/
@CachePut(value = "user", key = "#user.id")
public User updateUser(User user) {
System.out.println("===== 执行数据库更新,id:" + user.getId() + " =====");
USER_DB.put(user.getId(), user);
return user;
}
/**
* 删除用户:执行方法后,删除对应缓存
*/
@CacheEvict(value = "user", key = "#id")
public void deleteUser(Long id) {
System.out.println("===== 执行数据库删除,id:" + id + " =====");
USER_DB.remove(id);
}
}
3. 测试接口(Controller 层)
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/{id}")
public User getUser(@PathVariable Long id) {
return userService.getUserById(id);
}
@PutMapping
public User updateUser(@RequestBody User user) {
return userService.updateUser(user);
}
@DeleteMapping("/{id}")
public String deleteUser(@PathVariable Long id) {
userService.deleteUser(id);
return "删除成功";
}
}
五、缓存优化与避坑指南
1. 缓存穿透问题解决
- 原因:查询不存在的 key 时,缓存未命中,导致频繁访问数据库。
- 解决方案:
- 配置 cache-null-values: true(缓存 null 值,过期时间设为 5-10 分钟);
- 接口层增加参数校验,过滤无效 id(如小于 1 的 id)。
2. 缓存击穿问题解决
- 原因:热点 key 过期瞬间,大量请求同时访问数据库。
- 解决方案:
- 热点 key 设置永不过期(或超长过期时间);
- 使用互斥锁(如 Redis 的 SETNX),确保同一时间只有一个请求更新缓存。
3. 缓存雪崩问题解决
- 原因:大量缓存 key 同时过期,导致数据库压力骤增。
- 解决方案:
- 缓存过期时间添加随机值(如 time-to-live: ${random.int[3600000, 7200000]}ms);
- 核心业务缓存集群部署,避免单点故障。
4. 序列化问题注意事项
- 避免使用 JDK 序列化:可读性差、占用空间大,优先使用 JSON 序列化;
- 实体类若使用 Lombok,需确保有无参构造函数(JSON 反序列化依赖);
- 若实体类属性有新增 / 删除,建议清理旧缓存,避免反序列化失败。
六、总结
本文详细介绍了 Spring Boot 集成 Redis 的完整流程,从环境搭建、配置优化到实战应用,覆盖了缓存注解的核心使用场景和常见问题解决方案。通过 Redis 缓存,可显著提升接口响应速度(毫秒级响应),尤其适用于高频查询、数据变更较少的场景(如用户信息、商品详情等)。
后续可进一步探索 Redis 的高级特性,如分布式锁、消息队列、地理位置存储等,欢迎在评论区交流探讨!