从卡顿到丝滑:Spring Boot 接入 Redis 缓存的正确打开方式
Redis 本质是一个高性能的内存型 Key-Value 存储,非常适合用来做缓存层。在 Spring Boot 体系里,我们不需要手动去写复杂的缓存逻辑。借助 Spring Cache 抽象,只需要几个注解,就能让 Redis 自动接管缓存。
有没有遇到过这样的场景?接口逻辑明明不复杂,可一旦并发量上来,响应时间就会从几十毫秒飙升到几秒,用户体验瞬间“崩盘”。
免费影视、动漫、音乐、游戏、小说资源长期稳定更新! 👉 点此立即查看 👈
更棘手的是,这种慢并非持续性的,而是呈现出“第一次慢、后面快一点”的典型特征。这往往是一个明确的信号——问题的根源可能不在于SQL优化,而在于缺少一个高效的缓存层。
所以,核心问题早已不是“要不要用 Redis”,而是“如何正确使用才能避免踩坑”。
这篇文章不讨论抽象概念,直接带你从零开始,将 Spring Boot 与 Redis 缓存的整合,推进到可上线的实战水准。
别再只会“加个 Redis 依赖”:缓存接入的正确姿势
Redis 作为一个高性能的内存键值存储,天生就是构建缓存层的理想选择。
在 Spring Boot 的生态中,开发者无需手动编写复杂的缓存逻辑。通过 Spring Cache 这一抽象层,仅仅借助几个简单的注解,就能让 Redis 自动接管缓存工作。
其整体架构可以这样理解:
图片
核心思路非常清晰:优先查询缓存,若缓存未命中则查询数据库,并将结果同步写回缓存,以备后续请求使用。
别再只会默认配置:依赖引入才是第一步
首先,在项目的 /pom.xml 文件中加入以下核心依赖:
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-cache
org.springframework.boot
spring-boot-starter-data-redis
org.springframework.data
spring-data-redis
Spring Boot 的自动配置机制会基于这些依赖,完成 Redis 客户端连接的基础配置。
别再只会 localhost:连接配置决定稳定性
接下来,在 /src/main/resources/application.properties 配置文件中进行连接设置:
spring.application.name=redis-demo
# Redis 连接信息
spring.data.redis.host=localhost
spring.data.redis.port=6379
# 数据库索引
spring.data.redis.database=0
如果部署到生产环境,通常还需要配置密码、SSL连接以及连接池参数(如最大连接数、超时时间等),以确保服务的稳定性和安全性。
别再忘记开启缓存:一行注解决定一切
在主应用启动类中,启用缓存功能是至关重要的一步。
文件路径:/src/main/ja va/com/icoderoad/redisdemo/RedisDemoApplication.ja va
代码如下:
package com.icoderoad.redisdemo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
@SpringBootApplication
@EnableCaching
public class RedisDemoApplication {
public static void main(String[] args) {
SpringApplication.run(RedisDemoApplication.class, args);
}
}
请注意,缺少 @EnableCaching 注解,后续所有缓存相关的注解都将不会生效。
别再用默认序列化:缓存配置才是关键分水岭
默认情况下,Spring Data Redis 使用 JDK 序列化方式,其生成的数据可读性差,且性能并非最优。
通常建议统一改为 JSON 序列化,便于调试和跨语言兼容。
配置类路径:/src/main/ja va/com/icoderoad/redisdemo/config/CacheConfig.ja va
package com.icoderoad.redisdemo.config;
import ja va.time.Duration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
@Configuration
public class CacheConfig {
@Bean
public RedisCacheConfiguration cacheConfiguration() {
return RedisCacheConfiguration
.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(10)) // 统一设置缓存过期时间
.disableCachingNullValues() // 禁止缓存空值,防止缓存污染
.serializeValuesWith(
RedisSerializationContext.SerializationPair
.fromSerializer(new GenericJackson2JsonRedisSerializer()) // 使用 JSON 序列化
);
}
}
这段配置完成了三件关键工作:为缓存条目设置统一的过期时间(TTL);禁止缓存空值,避免无意义数据占用空间;以及采用 JSON 格式进行序列化,极大提升了数据的可读性和可调试性。
别再用数据库演示:先模拟“慢查询”更直观
为了直观展示缓存效果,我们先定义一个简单的实体类。
实体类路径:/src/main/ja va/com/icoderoad/redisdemo/product/Product.ja va
package com.icoderoad.redisdemo.product;
public class Product {
private Long id;
private String name;
private double price;
public Product() {}
public Product(Long id, String name, double price) {
this.id = id;
this.name = name;
this.price = price;
}
// getter & setter 省略
}
接着,模拟一个包含“慢查询”的数据源仓库。
仓库类路径:/src/main/ja va/com/icoderoad/redisdemo/product/ProductRepository.ja va
package com.icoderoad.redisdemo.product;
import ja va.util.Map;
import ja va.util.concurrent.ConcurrentHashMap;
import org.springframework.stereotype.Repository;
@Repository
public class ProductRepository {
private final Map store = new ConcurrentHashMap<>();
public ProductRepository() {
store.put(1L, new Product(1L, "Laptop", 80000));
store.put(2L, new Product(2L, "Phone", 40000));
}
public Product findById(Long id) {
simulateSlowCall(); // 模拟慢查询
return store.get(id);
}
public Product sa ve(Product product) {
store.put(product.getId(), product);
return product;
}
public void deleteById(Long id) {
store.remove(id);
}
private void simulateSlowCall() {
try {
Thread.sleep(2000); // 模拟2秒的慢查询
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
效果会非常明显:第一次请求耗时约2秒,而第二次请求几乎瞬间返回。这正是缓存带来的最直观性能差异。
别再手写缓存逻辑:3 个注解搞定一切
服务层是应用缓存逻辑的核心,Spring Cache 的注解让这一切变得异常简洁。
服务类路径:/src/main/ja va/com/icoderoad/redisdemo/product/ProductService.ja va
package com.icoderoad.redisdemo.product;
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 final ProductRepository repository;
public ProductService(ProductRepository repository) {
this.repository = repository;
}
@Cacheable(cacheNames = "products", key = "#id")
public Product getProduct(Long id) {
System.out.println("Loading product from repository...");
return repository.findById(id);
}
@CachePut(cacheNames = "products", key = "#result.id")
public Product updateProduct(Product product) {
System.out.println("Updating product and cache...");
return repository.sa ve(product);
}
@CacheEvict(cacheNames = "products", key = "#id")
public void deleteProduct(Long id) {
System.out.println("Removing product and cache entry...");
repository.deleteById(id);
}
}
这里用到了三大核心注解:@Cacheable(查询时缓存)、@CachePut(更新时刷新缓存)、@CacheEvict(删除时清除缓存)。
别再只写 Service:接口验证才是闭环
最后,通过一个简单的控制器对外提供 RESTful 接口,形成完整闭环。
控制器路径:/src/main/ja va/com/icoderoad/redisdemo/product/ProductController.ja va
package com.icoderoad.redisdemo.product;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/products")
public class ProductController {
private final ProductService service;
public ProductController(ProductService service) {
this.service = service;
}
@GetMapping("/{id}")
public Product getProduct(@PathVariable Long id) {
return service.getProduct(id);
}
@PutMapping("/{id}")
public Product updateProduct(@PathVariable Long id, @RequestBody Product product) {
product.setId(id);
return service.updateProduct(product);
}
@DeleteMapping("/{id}")
public void deleteProduct(@PathVariable Long id) {
service.deleteProduct(id);
}
}
接口分工明确:GET 请求用于读取并触发缓存;PUT 请求用于更新数据并同步刷新缓存;DELETE 请求则负责删除数据并清理对应的缓存条目。
别再一刀切 TTL:精细化缓存才是进阶
在实际项目中,不同数据的变更频率和重要性各不相同,采用统一的缓存过期策略往往不是最佳选择。
可以通过自定义配置,为不同的缓存名称(CacheNames)设置不同的 TTL。
进阶配置路径:/src/main/ja va/com/icoderoad/redisdemo/config/CacheTuningConfig.ja va
package com.icoderoad.redisdemo.config;
import ja va.time.Duration;
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.RedisCacheManagerBuilder;
import org.springframework.data.redis.cache.RedisCacheManagerBuilderCustomizer;
@Configuration
public class CacheTuningConfig {
@Bean
public RedisCacheManagerBuilderCustomizer redisCacheManagerBuilderCustomizer() {
return (RedisCacheManagerBuilder builder) -> builder
.withCacheConfiguration("products",
RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(5))) // 商品信息缓存5分钟
.withCacheConfiguration("users",
RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(1))); // 用户信息缓存1分钟
}
}
这实现了差异化的缓存策略:对于商品这类变化相对缓慢的数据,可以设置较长的 TTL;而对于用户信息等可能频繁变动的数据,则应设置较短的 TTL,以在性能和数据一致性之间取得平衡。
缓存从来不是“引入一个 Redis 依赖就万事大吉”的事情,它是系统性能分层设计中至关重要的一环。
真正的优化目标,不仅仅是让单个接口变快,更是要确保系统在高并发场景下依然能保持稳定和高效。
当你开始熟练运用注解来优雅地管理缓存生命周期,通过精细化的 TTL 策略控制数据新鲜度,并借助合适的序列化方案提升系统的可观测性时,你所构建的就不再仅仅是“可以运行的代码”,而是一套能够稳健支撑业务持续增长的架构基石。
相关攻略
别再手写DTO:用Record重构你的接口模型 一提到写DTO,不少开发者脑海里浮现的就是一连串的机械劳动:构造函数、getter-setter、equals、hashCode,还有toString。这些代码毫无业务价值,却实实在在地消耗着时间和精力。 好在,现代Ja va提供了一个极其优雅的解决方
Spring Boot 4 0 2 做了什么?一句话版本概览 先给一个高度概括: Spring Boot 4 0 2 是一个“专注修复、不搞花活”的稳定性版本。 它主要覆盖三大方向: 20+ Bug 修复:Kafka、WebFlux、Actuator、测试框架等核心模块均有涉及。 40+ 核心依赖升
如何借力 Claude 快速拆解复杂的 Spring Boot 业务代码 面对一个刚接手的历史遗留项目,打开代码仓库的瞬间,那种感受恐怕很多同行都经历过: Controller层像迷宫,层层嵌套,入口难寻;Service方法动辄几百行,逻辑纠缠在一起;Mapper的调用链条深不见底;更棘手的是,一个
Redis 本质是一个高性能的内存型 Key-Value 存储,非常适合用来做缓存层。在 Spring Boot 体系里,我们不需要手动去写复杂的缓存逻辑。借助 Spring Cache 抽象,只需要几个注解,就能让 Redis 自动接管缓存。 有没有遇到过这样的场景?接口逻辑明明不复杂,可一旦并发
别再只会写接口 —— 第一阶段:能跑就行 故事的开头,往往不是从框架开始的,而是从一团混乱开始的。想象一下,一个名叫 QuickBite 的小团队,只提了一个看似简单的需求:“做一个能在线点餐的系统。”没有架构图,没有缓存,没有消息队列,更没有安全体系。只有紧迫的时间和“先做出来”的压力。 于是,最
热门专题
热门推荐
腾讯生态整合新动向:QQ全面接入微信小程序 7月1日,腾讯QQ小程序开发者平台发布了一项重要更新。核心内容是,为了帮助开发者降低双端开发与维护成本,QQ将全面接入微信小程序体系。这意味着,未来用户可以直接在QQ内搜索并打开微信小程序。 对于现有的存量QQ小程序,此次调整并未“一刀切”。它们目前仍可正
下半年芯片市场巅峰对决提前揭幕 今年下半年,全球芯片市场的战火将空前炽热。两位重量级选手——联发科与高通,已经准备好亮出各自的王牌。天玑9600系列与骁龙8E6系列,这两大迭代旗舰平台的正面交锋,注定会成为今年科技行业最值得关注的戏码。 双芯策略:精准卡位旗舰市场 有意思的是,联发科这次玩了个新花样
在当今数字化社交的时代,微信已成为人们日常沟通交流的重要工具。不少人都发现,微信好友申请居然可以通过搜索 qq 号来添加,这背后有着诸多有趣的原因和便利之处。 一、社交关系的延续与拓展 要知道,微信与QQ同属腾讯旗下,两者之间存在着千丝万缕的联系。很多用户的社交关系其实根植于QQ时代,那些好友列表里
高德地图如何更改定位?三种方法详解及注意事项 无论是日常通勤、外出旅行还是朋友相聚,高德地图已经成了我们依赖的“导航神器”,精准定位和路线规划是其核心功能。不过,现实场景有时会有点特殊——比如,你可能需要模拟一个位置来测试应用,或者在某个游戏中“签到”,又或者只是想和朋友开个无伤大雅的玩笑。这个时候
巧学宝App绑定手机号全程指南 在巧学宝App上完成手机号绑定,是解锁其完整功能的关键一步。这个看似简单的操作,能为你后续的学习之旅带来不少实实在在的便利。那么,该如何快速搞定呢?下面这张流程图,能帮你一眼看清完整的操作路径。 第一步:进入个人中心 首先,打开你的巧学宝App。进入主界面后,注意力可





