)
在 Spring Boot 项目中缓存是提升性能、降低数据库压力的重要手段。下面结合你提供的资金选项查询业务/capital/options/all接口对 Spring Boot 项目中使用缓存的方方面面做一个详细总结涵盖 Spring Cache 抽象、本地缓存 Caffeine、分布式缓存 Redis 以及 MyBatis 自带的二级缓存并给出最佳实践建议。一、缓存的使用场景与业务分析你的业务接口/capital/options/all用于查询资金相关的所有下拉选项包括资金账户、来源、指标来源等 7 张配置表。这类数据的特点是读多写极少配置数据基本固定只在后台管理修改时更新。数据量小每张表可能只有几十到几百条记录。响应要求高前端可能频繁调用该接口。因此非常适合引入缓存将全量查询结果缓存起来避免每次请求都查询数据库。二、Spring Boot 中的缓存方案概览Spring Boot 支持多种缓存方案主要分为两类基于 Spring 缓存抽象spring-boot-starter-cache通过注解Cacheable、CacheEvict等声明式地控制缓存。可插拔的缓存实现Caffeine本地、Redis分布式、Ehcache 等。与业务代码解耦切换实现无需修改业务逻辑。MyBatis 自带的二级缓存MyBatis 框架内置的缓存机制作用于 Mapper 级别。无需额外依赖直接在 Mapper 上配置即可。缓存粒度较细但无法直接缓存组合后的 DTO。下面将分别详细说明这两种方案在业务中的应用。三、方案一Spring Cache 抽象推荐1. 核心概念Cacheable标记在方法上表示结果可缓存。首次调用后缓存结果后续直接返回缓存值。CacheEvict清除缓存通常在更新操作后调用保证数据一致性。CachePut每次都会执行方法并更新缓存不常用。CacheManager缓存管理器负责创建和管理缓存实例。2. 集成步骤以 Caffeine 本地缓存为例2.1 添加依赖xmldependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-cache/artifactId /dependency dependency groupIdcom.github.ben-manes.caffeine/groupId artifactIdcaffeine/artifactId /dependency2.2 启用缓存在启动类或任意Configuration类上加EnableCaching。javaSpringBootApplication EnableCaching public class Application { ... }2.3 配置缓存管理器可选但推荐在application.yml中指定缓存类型和基本属性yamlspring: cache: type: caffeine caffeine: spec: maximumSize500, expireAfterWrite10m # 最大500条写入10分钟后过期或者通过 Java 配置类进行更细粒度的控制。2.4 在 Service 方法上添加缓存注解针对你的业务改造CapitalInfoServicejavaService public class CapitalInfoService { // 注入各个 Mapper Cacheable(value capitalOptions, key all) public CapitalAllOptionDTO queryCapitalAllOption() { // 原有的7次查询逻辑 return allOptionDTO; } CacheEvict(value capitalOptions, key all) public void refreshCapitalOptions() { // 空方法用于手动清空缓存例如在更新配置后调用 } // 更新配置的方法示例 CacheEvict(value capitalOptions, key all) public void updateAccount(CapitalAccount account) { accountMapper.updateById(account); } }2.5 注意事项缓存键key all使用固定字符串因为该方法无参且返回全量数据。如果方法有参数可以动态生成键如#id。序列化如果将来要切换 Redis需要让CapitalAllOptionDTO及其包含的实体类实现Serializable。缓存清理任何对配置表的修改操作增、删、改都必须清除缓存否则用户会看到旧数据。可以在每个更新方法上加CacheEvict或统一调用refreshCapitalOptions()。空值缓存如果数据库为空默认不会缓存空结果可能导致缓存穿透。可以用unless #result null避免。3. 切换为 Redis分布式缓存如果应用需要多实例部署只需更换依赖和配置业务代码无需修改移除 Caffeine 依赖添加spring-boot-starter-data-redis。在application.yml中配置 Redis 连接和 TTLyamlspring: cache: type: redis redis: time-to-live: 600000 # 10分钟毫秒 redis: host: localhost port: 6379Spring Boot 会自动配置RedisCacheManager之前的Cacheable注解继续生效。4. Spring Cache 的优势注解驱动简洁明了与业务代码分离。灵活可缓存任意方法的返回值包括组合 DTO。可扩展轻松切换本地/分布式缓存。支持多种缓存实现Caffeine、Redis、Ehcache 等。四、方案二MyBatis 二级缓存1. 核心概念MyBatis 内置了两级缓存一级缓存SqlSession 级别默认开启同一会话中重复查询不查库。二级缓存Mapper 级别跨会话共享需手动开启。二级缓存的作用域是同一个namespace即同一个 Mapper缓存的是 Mapper 中查询方法的返回结果如ListCapitalAccount。2. 在业务中的应用如果使用 MyBatis 二级缓存你需要为 7 个 Mapper 分别开启缓存javaMapper CacheNamespace(eviction FifoCache.class, flushInterval 60000, size 512) public interface CapitalAccountMapper extends BaseMapperCapitalAccount { }然后在 Service 方法中每次调用accountMapper.selectList(null)时如果二级缓存中有数据就不会查询数据库。3. 优缺点分析优点无需额外依赖MyBatis 自带。配置简单只需在 Mapper 上加注解。自动维护一致性执行当前 Mapper 的 DML 操作时自动清空该 Mapper 的缓存。缺点无法缓存组合后的 DTO每次调用queryCapitalAllOption()仍需执行 7 次查询虽然可能命中缓存。缓存粒度太细不能跨 Mapper 共享。分布式环境下各实例缓存独立会导致数据不一致。需要实体类实现Serializable如果缓存可读写。与 Spring 事务结合时需要注意缓存清空的时机必须在事务提交后清空MyBatis 默认在更新后清空但如果事务回滚缓存已被清空可能造成不一致。4. 适合场景单机应用对某个 Mapper 的查询有较高重复率。无需跨表组合缓存的简单场景。对于你的业务一次返回 7 张表的组合数据MyBatis 二级缓存虽然能减少数据库查询但仍有多次缓存读取和对象组装开销不如 Spring Cache 直接缓存最终 DTO 高效。五、两种方案的对比与选择特性Spring Cache Caffeine/RedisMyBatis 二级缓存缓存粒度任意方法返回值如 DTOMapper 级别的单个查询结果配置复杂度需添加依赖和配置但注解简单无需额外依赖仅 Mapper 配置数据一致性需手动CacheEvict可精细控制执行 DML 时自动清空当前 Mapper 缓存分布式支持支持切换 Redis不支持仅本地与业务解耦完全解耦可作用于 Service 层与 Mapper 耦合无法控制组合逻辑适用场景适合组合查询、跨表结果缓存适合单表简单查询的重复缓存针对你的业务强烈推荐使用 Spring Cache 抽象 Caffeine单机或 Redis分布式。理由如下直接缓存最终的CapitalAllOptionDTO一次方法调用只需查一次数据库缓存失效后性能最佳。注解使用灵活未来可无缝切换缓存实现。代码改动小只需在 Service 方法上加注解并在更新操作上加CacheEvict。六、最佳实践总结结合你的业务1. 实施步骤添加依赖spring-boot-starter-cache和caffeine。启用缓存启动类加EnableCaching。配置缓存application.yml中设置spring.cache.caffeine.specmaximumSize500, expireAfterWrite10m。注解 Service 方法queryCapitalAllOption()上加Cacheable(value capitalOptions, key all)。所有更新配置的方法如updateAccount、addSource等上加CacheEvict(value capitalOptions, key all)。测试验证首次调用查库第二次直接返回缓存。2. 注意事项缓存过期时间根据数据更新频率设置合理的expireAfterWrite例如 10 分钟或 30 分钟。即使有CacheEvict过期时间作为兜底防止缓存永久占用内存。缓存清理务必在所有修改操作中清除缓存否则会出现脏数据。如果修改操作分散在多个 Service可以统一调用一个刷新方法CacheEvict标注的空方法。序列化如果未来可能切换 Redis提前让 DTO 和实体类实现Serializable。缓存键冲突如果同一缓存区域有其他方法使用不同的 key 区分。监控与调优可以通过 Actuator 端点查看缓存命中率调整缓存大小和过期时间。3. 扩展思考分布式部署当应用需要水平扩展时将缓存实现切换为 Redis注意配置 Redis 的序列化方式如 Jackson2JsonRedisSerializer以便跨语言读取。缓存穿透/雪崩配置空值缓存unless和随机过期时间防止雪崩。多级缓存对于极高并发场景可考虑 Caffeine 作为一级缓存Redis 作为二级缓存但会增加复杂度。七、总结Spring Boot 项目中使用缓存应根据业务场景选择合适的方案对于组合查询、跨表结果首选Spring Cache 抽象它提供了声明式编程模型能够灵活地缓存任意方法的返回值并且支持多种缓存实现方便未来扩展。对于简单的单表查询重复缓存MyBatis 二级缓存也是一个轻量级的选择但需注意其局限性和分布式问题。在你的资金选项业务中采用 Spring Cache Caffeine 的实践方案不仅能够显著提升接口性能还能保持代码简洁、易于维护。缓存是性能优化的利器但也要注意数据一致性、过期策略和分布式环境下的挑战合理设计才能发挥最大价值。