SpringBoot + Redis 实现接口缓存——抗高并发的必备技能
·
查询数据库是耗时操作,如果同一接口被频繁调用,每次都查数据库会严重影响性能。引入 Redis 缓存后,数据先从缓存中取,缓存没有再去查数据库,响应速度能提升 10~100 倍。
一、为什么需要缓存
| 场景 | 无缓存 | 有 Redis 缓存 |
|---|---|---|
| 热点数据反复查询 | 每次都查数据库 | 第一次查数据库,之后走缓存 |
| 接口响应时间 | 300~500ms | 5~30ms |
| 数据库压力 | 高 | 低 |
二、准备工作
1. 安装 Redis(Windows)
下载 Redis for Windows 并启动即可,默认端口 6379。
2. 引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
3. 配置 Redis
spring:
redis:
host: localhost
port: 6379
timeout: 3000ms
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0
三、手动操作 Redis(最灵活的方式)
使用 RedisTemplate 手动读写缓存:
@Service
public class UserService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
private static final String USER_CACHE_KEY = "user:";
public User getUserById(Long id) {
String key = USER_CACHE_KEY + id;
// 1. 先从缓存中取
User user = (User) redisTemplate.opsForValue().get(key);
if (user != null) {
System.out.println("从缓存中获取用户: " + id);
return user;
}
// 2. 缓存没有,查数据库
user = userMapper.selectById(id);
if (user != null) {
// 3. 放入缓存,过期时间 30 分钟
redisTemplate.opsForValue().set(key, user, 30, TimeUnit.MINUTES);
System.out.println("从数据库查询并缓存用户: " + id);
}
return user;
}
public void updateUser(User user) {
userMapper.updateById(user);
// 更新后删除缓存,下次查询重新加载
redisTemplate.delete(USER_CACHE_KEY + user.getId());
}
}
RedisTemplate 配置
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
// 使用 Jackson 序列化(存储为 JSON)
Jackson2JsonRedisSerializer<Object> jackson = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.activateDefaultTyping(LazyValidatorFactory.ObjectMapperDefaultTyping.NON_FINAL);
jackson.setObjectMapper(mapper);
// key 用 String 序列化,value 用 JSON 序列化
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(jackson);
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(jackson);
return template;
}
}
四、用注解方式(最推荐)
Spring 提供了声明式缓存注解,一行注解搞定缓存:
@Service
public class UserService {
@Cacheable(value = "user", key = "#id")
public User getUserById(Long id) {
System.out.println("查询数据库: " + id);
return userMapper.selectById(id);
}
@CachePut(value = "user", key = "#user.id")
public User updateUser(User user) {
userMapper.updateById(user);
return user;
}
@CacheEvict(value = "user", key = "#id")
public void deleteUser(Long id) {
userMapper.deleteById(id);
}
}
常用注解说明
| 注解 | 作用 | 适用场景 |
|---|---|---|
@Cacheable |
先查缓存,有则返回,无则执行方法并缓存结果 | 查询接口 |
@CachePut |
执行方法,并将结果更新到缓存 | 更新接口 |
@CacheEvict |
执行方法,并删除缓存 | 删除接口 |
启用缓存注解
@SpringBootApplication
@EnableCaching // 开启缓存注解
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
五、防止缓存雪崩和穿透
1. 设置合理的过期时间
// 缓存过期时间加上随机值,防止同时过期(缓存雪崩)
long expireTime = 30 + new Random().nextInt(10); // 30~40分钟
redisTemplate.opsForValue().set(key, value, expireTime, TimeUnit.MINUTES);
2. 防止缓存穿透(查询不存在的数据)
public User getUserById(Long id) {
String key = "user:" + id;
// 缓存中有特殊标记,直接返回 null
if (redisTemplate.hasKey(key)) {
Object val = redisTemplate.opsForValue().get(key);
if (val == null) {
return null; // 之前查过数据库,不存在
}
return (User) val;
}
User user = userMapper.selectById(id);
if (user == null) {
// 数据不存在,也缓存一个空值,防止穿透
redisTemplate.opsForValue().set(key, null, 5, TimeUnit.MINUTES);
} else {
redisTemplate.opsForValue().set(key, user, 30, TimeUnit.MINUTES);
}
return user;
}
六、Redis 可视化工具推荐
平时开发建议装一个可视化工具,方便查看缓存数据:
- Another Redis Desktop Manager(推荐,免费)
- Redis Insight(官方出品)
总结
Redis 缓存是 Java 后端开发的核心技能。记住三条原则:
- 读多写少的数据才加缓存(比如用户信息、配置)
- 设置合理的过期时间(避免缓存雪崩)
- 缓存空值(防止缓存穿透)
如果对你有帮助,欢迎点赞、评论、关注【张老师技术栈】,持续分享 Java/Python/爬虫 实战干货。
更多推荐



所有评论(0)