Spring Boot与MySQL接口结合Redis和Caffeine多级缓存实践
> 文章案例具体代码链接:https://github.com/whltaoin/hututu > 提交版本:e67453a26f0bbe78ce075a550bafb01ba82aa9ff 引言 在后端开发中,API 接口是服务间通信的核心载体,而数据存储与缓存策略则直接决定了接口的性能与稳定性。Spring 框架凭借其强大的生态成为 API 开发的首选,MySQL 作为关系型数据库提供了可靠的…

> 文章案例具体代码链接:https://github.com/whltaoin/hututu > 提交版本:e67453a26f0bbe78ce075a550bafb01ba82aa9ff 引言 在后端开发中,API 接口是服务间通信的核心载体,而数据存储与缓存策略则直接决定了接口的性能与稳定性。Spring 框架凭借其强大的生态成为 API 开发的首选,MySQL 作为关系型数据库提供了可靠的…

文章案例具体代码链接:https://github.com/whltaoin/hututu
提交版本:e67453a26f0bbe78ce075a550bafb01ba82aa9ff
在后端开发中,API 接口是服务间通信的核心载体,而数据存储与缓存策略则直接决定了接口的性能与稳定性。Spring 框架凭借其强大的生态成为 API 开发的首选,MySQL 作为关系型数据库提供了可靠的数据持久化支持。当系统并发量提升时,单一数据库架构易出现性能瓶颈,此时引入 Redis(分布式缓存)与 Caffeine(本地缓存)构建多级缓存体系,成为优化性能的关键方案。本文将从基础实现出发,深入探讨多级缓存的优势、劣势、适用场景及实践注意点。
Spring 生态中,Spring Boot 结合 MyBatis/MyBatis-Plus 可快速搭建基于 MySQL 的 API 接口服务。其核心逻辑是通过 Spring MVC 接收前端请求,Service 层处理业务逻辑,Mapper 层通过 MyBatis 操作 MySQL 数据库,最终将数据返回给调用方。
实现该架构需引入以下核心依赖(以 Maven 为例):
<!-- Spring Boot Web 核心 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- MyBatis-Plus 启动器 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
<!-- MySQL 驱动 -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.16</version>
</dependency>
application.yml 中配置 MySQL 地址、用户名、密码及连接池参数;BaseMapper 实现 CRUD 操作,无需手动编写 SQL;@RestController、@RequestMapping 等注解定义 API 路径,接收请求并返回结果。该架构的优势是开发效率高、数据一致性强,但当并发请求量超过 MySQL 承载能力时,会出现查询延迟、连接池耗尽等问题,此时缓存的引入至关重要。
多级缓存的核心思路是'近缓存优先',将访问频率高的数据存储在距离应用更近的缓存中,减少网络开销和数据库压力。Caffeine 作为本地缓存(进程内缓存),Redis 作为分布式缓存(独立服务),二者结合可兼顾性能与分布式一致性。
<!-- 分布式缓存:Redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 本地缓存:Caffeine(注意:3.x 版本要求 Java 11 及以上) -->
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>3.1.8</version>
</dependency>
| 特性 | Caffeine | Redis |
|---|---|---|
| 存储位置 | 应用进程内 | 独立服务器/集群 |
| 访问延迟 | 微秒级(极快) | 毫秒级(较快) |
| 分布式支持 | 不支持(进程隔离) | 支持(集群部署) |
| 存储容量 | 受 JVM 内存限制 | 支持大容量(GB/TB 级) |
| 持久化 | 不支持(进程重启丢失) | 支持(RDB/AOF) |
基于 Spring Cache 抽象,可快速整合 Caffeine 与 Redis,核心流程如下:

弧图图项目中的一个分页列表接口示例:
@Resource
private ValueOperations<String, String> stringValueWithRedisTemplate;
@Resource
private Cache<String, String> localCacheWithCaffeine;
@ApiOperation(value = "分页图片列表VO多级缓存")
@PostMapping("/list/page/vo/multi-level")
public BaseResponse<Page<PictureVo>> listPictureVOByPageWithMultiLevel(
@RequestBody PictureQueryRequest pictureQueryRequest,
HttpServletRequest request) {
int current = pictureQueryRequest.getCurrent();
int pageSize = pictureQueryRequest.getPageSize();
ThrowUtil.throwIf(pageSize > 20, ResponseCode.PARAMS_ERROR);
// 只有审核过的图片可以在主页显示
pictureQueryRequest.setReviewStatus(ReviewStatusEnum.PASS.getValue());
Page<Picture> picturePage = pictureService.page(new Page<>(current, pageSize),
pictureService.getQueryWrapper(pictureQueryRequest));
// 1. 构造 key
String queryCondition = JSONUtil.toJsonStr(pictureQueryRequest);
// 查询条件加密
queryCondition = DigestUtils.md5DigestAsHex(queryCondition.getBytes());
String cacheKey = String.format("hututu:listPictureVOByPageWithCache:%s", queryCondition);
// 2. 先查询本地缓存
String cacheValue = localCacheWithCaffeine.getIfPresent(cacheKey);
Page<PictureVo> pictureVOPage = null;
if (cacheValue != null) {
pictureVOPage = JSONUtil.toBean(cacheValue, Page.class);
return ResponseUtil.success(pictureVOPage);
}
// 3. 查询 Redis 缓存
cacheValue = stringValueWithRedisTemplate.get(cacheKey);
(cacheValue != ) {
pictureVOPage = JSONUtil.toBean(cacheValue, Page.class);
ResponseUtil.success(pictureVOPage);
}
pictureVOPage = pictureService.getPictureVOPage(picturePage, request);
JSONUtil.toJsonStr(pictureVOPage);
localCacheWithCaffeine.put(cacheKey, jsonStr);
stringValueWithRedisTemplate.set(cacheKey, jsonStr, + RandomUtil.randomInt(, ), TimeUnit.SECONDS);
ResponseUtil.success(pictureVOPage);
}
实现前端效果:

任何技术方案都有其适用场景,多级缓存的优劣需结合业务场景客观评估。
Caffeine 的微秒级访问延迟可覆盖大部分高频查询场景,减少对 Redis 的网络调用;Redis 则承接跨服务的缓存需求,二者结合使缓存命中率大幅提升,数据库查询压力显著降低。实测显示,在商品详情查询场景中,引入多级缓存后接口响应时间可从 50-100ms 降至 1-5ms,并发承载能力提升 10 倍以上。
在微服务架构中,Caffeine 的本地缓存可减少服务间对 Redis 的竞争,而 Redis 确保了不同服务实例间的缓存一致性。例如,用户登录状态缓存可存储在 Redis 中供所有服务访问,而服务本地的高频配置数据可存储在 Caffeine 中,兼顾一致性与性能。
当 Redis 集群因网络故障或节点宕机暂时不可用时,Caffeine 本地缓存可作为'兜底缓存',保障核心接口的可用性;反之,若单个服务实例故障,Redis 中的缓存数据可被其他实例复用,避免缓存雪崩。
引入多级缓存后,需额外维护 Caffeine 的本地缓存配置(如容量、过期时间、淘汰策略)和 Redis 的集群部署、持久化策略;同时需处理缓存一致性问题(如更新/删除时的缓存同步),增加了开发和运维成本。
Caffeine 缓存占用 JVM 堆内存,若配置不当(如容量过大、淘汰策略不合理),易导致 JVM 内存溢出(OOM);Redis 集群则需独立的服务器资源,增加了硬件成本。
多级缓存存在'缓存不一致'的天然风险:例如,服务 A 更新数据后删除了自身 Caffeine 缓存和 Redis 缓存,但服务 B 的 Caffeine 缓存中仍有旧数据,直到过期才会更新。虽可通过发布订阅机制(如 Redis Pub/Sub)触发全量服务的本地缓存失效,但会增加系统复杂度。
如商品详情、新闻资讯、字典配置等数据,查询频率远高于更新频率,多级缓存可最大化发挥性能优势。例如,电商平台的商品列表查询,90% 以上的请求可通过 Caffeine 命中,剩余请求通过 Redis 命中,仅极少数请求需查询数据库。
微服务间的接口调用频繁,多级缓存可减少跨服务依赖和网络开销。例如,订单服务查询用户信息时,先查本地 Caffeine 缓存,未命中再查 Redis 缓存,最后调用用户服务接口并同步缓存,降低服务间的耦合度。
如秒杀活动、限时优惠等场景,并发量可达数万 QPS,单一数据库或 Redis 无法承载。多级缓存可通过 Caffeine 承接大部分瞬时请求,Redis 保障分布式一致性,数据库仅处理最终的下单逻辑。
采用'先更新数据库,再删除缓存'的策略(避免更新缓存失败导致不一致);对于强一致性需求场景,可引入 Redis Pub/Sub 机制,当数据更新时,发布缓存失效消息,所有服务实例接收消息后删除本地 Caffeine 缓存;对于弱一致性场景,可依赖缓存过期机制自然失效。
通过 Spring Boot Actuator 监控 Caffeine 缓存命中率、容量使用情况;通过 Redis 监控工具(如 Redis Insight)监控 Redis 的命中率、内存占用、集群状态;设置告警机制,当缓存命中率低于阈值(如 80%)或 Redis 内存使用率超过阈值(如 80%)时及时预警。
Spring Boot + MySQL 是 API 接口开发的基础架构,而 Redis + Caffeine 多级缓存则是应对高并发场景的'性能利器'。其核心价值在于通过'本地缓存 + 分布式缓存'的组合,兼顾了性能、一致性与可用性,但同时也带来了架构复杂度和资源消耗的提升。
在实践中,需结合业务场景选择是否引入多级缓存:对于低频请求、强一致性需求的接口,单一数据库或 Redis 缓存即可满足需求;对于高频读、低频写的核心接口,多级缓存则能带来显著的性能提升。同时,需通过合理的缓存配置、一致性策略和监控机制,规避潜在风险,确保系统稳定运行。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online