前面我们先后学习了 Redis 分布式缓存的整合、缓存三大核心问题穿透、击穿、雪崩的解决方案以及缓存与数据库双写一致性的落地策略这些内容已经能解决绝大多数单体、分布式项目的基础缓存需求。但随着项目用户量、请求量不断上涨尤其是面对秒杀、首页高频访问、商品详情页等高并发场景你会发现单纯只使用 Redis 缓存依然存在明显的性能瓶颈甚至会出现 Redis 扛不住流量、接口响应延迟飙升的情况。举个真实生产场景某电商首页高峰期 QPS 达到 10 万所有请求都去访问 Redis 集群导致 Redis 网络带宽打满、连接数超标部分请求出现超时即使 Redis 能扛住每次请求的网络 IO 耗时哪怕 1-2 毫秒在 10 万 QPS 下也会被无限放大接口响应速度始终无法突破瓶颈。为了解决这个问题企业级高并发项目的终极缓存架构——多级缓存就应运而生。它不是对 Redis 缓存的替代而是在 Redis 之上增加一层“本地内存缓存”形成本地缓存Caffeine Redis 分布式缓存 数据库的三层缓存架构层层拦截请求极致压榨接口性能。核心访问逻辑非常简单记好这一句话就够了请求优先走本地内存缓存命中直接返回无任何网络开销、速度最快本地缓存未命中再查Redis 分布式缓存Redis 依然未命中最后查询数据库查询到数据后再依次回写到 Redis、本地缓存供后续请求快速命中。一、为什么要用多级缓存单纯 Redis 不够用吗很多同学都会有这样的疑惑Redis 已经是业界公认的高性能缓存中间件毫秒级响应为什么还要多此一举再加一层本地缓存其实答案很简单极致性能的追求以及对 Redis 集群的保护。我们先明确三种数据存储的读取速度对比这是多级缓存设计的核心依据记住这个排序面试必问本地内存缓存Caffeine Redis 网络缓存 MySQL 数据库我们用具体的耗时量级更直观地感受差距• 本地缓存Caffeine数据存储在 JVM 堆内存中无网络开销、无序列化/反序列化耗时访问速度是纳秒~微秒级比如 100 纳秒几乎可以忽略不计• Redis 缓存独立的分布式中间件请求需要经过“网络传输 序列化/反序列化 Redis 内部查询”访问速度是毫秒级正常情况下 1-5 毫秒网络波动时会更高• MySQL 数据库数据存储在磁盘中需要经过磁盘 IO、索引查询、事务处理访问速度是几十毫秒起步复杂查询甚至会达到上百毫秒。举个例子一个接口单纯用 Redis 缓存响应时间大概 5-10 毫秒加上本地缓存后热点请求命中本地响应时间能降到 1 毫秒以内性能提升 5-10 倍这就是多级缓存的核心价值。1. 单 Redis 缓存架构的核心痛点单纯依赖 Redis 缓存在高并发场景下会暴露 3 个致命问题也是我们必须引入多级缓存的原因1存在不可避免的网络开销所有请求都要跨网络访问 Redis 集群无论是本地部署还是远程部署哪怕 Redis 性能极强网络往返的耗时哪怕 1 毫秒在高并发场景下都会被无限放大。比如 10 万 QPS每请求多 1 毫秒总耗时就会增加 100 秒直接导致接口响应延迟飙升。2Redis 服务集群压力过大易触发瓶颈秒杀、首页轮播、商品详情等超级热点 Key会被所有服务实例频繁请求。比如一个热点商品 Key10 台服务实例每台每秒请求 1000 次总 QPS 就达到 1 万次极易打满 Redis 的 QPS 上限Redis 单机 QPS 大概 10 万左右集群可扩展但依然有上限、CPU 使用率和网络带宽导致 Redis 抖动、响应变慢。3Redis 集群故障风险易引发系统雪崩Redis 虽然有集群、哨兵机制保证高可用但依然可能出现抖动、断连、集群不可用的情况比如网络故障、集群扩容失误。一旦 Redis 不可用所有请求会直接击穿到数据库数据库无法承受高并发请求会直接崩溃引发整个系统雪崩。2. 多级缓存架构的核心优势引入本地缓存 Redis 的多级缓存架构后能完美解决上述痛点同时带来 4 大核心优势这也是企业高并发项目的标配•极致性能提升热点数据全部常驻应用本地内存微秒级响应接口响应速度大幅提升能轻松支撑 10 万 QPS 场景•分流减压保护 Redis绝大部分高频请求会被本地缓存直接拦截不再访问 Redis极大降低 Redis 集群的 QPS、CPU、网络带宽压力避免 Redis 成为系统瓶颈•高可用兜底防止雪崩即使 Redis 集群抖动、不可用本地缓存依然可以扛住热点流量避免请求直接击穿到数据库为 Redis 恢复争取时间提升系统整体可用性•架构分层合理职责清晰本地缓存负责扛“超级热点数据”访问频率极高、数据量小Redis 负责扛“全量分布式缓存”数据量大、需要跨实例共享数据库负责存储原始数据分层各司其职架构更稳定、可扩展。面试必背总结多级缓存的核心价值是“用本地缓存的极致速度分流 Redis 压力同时为系统提供高可用兜底”解决单 Redis 缓存的网络开销、压力过大、故障雪崩三大痛点实现性能与可用性的双重提升。二、本地缓存选型对比Caffeine、Guava、ConcurrentHashMap多级缓存的核心是“本地缓存”选择一个合适的本地缓存组件直接决定了本地缓存的性能、命中率和稳定性。目前 Java 项目中本地缓存主流有三种实现我们做一个完整的对比面试高频考点必须吃透缓存组件底层原理性能表现淘汰策略内存占用适用场景SpringBoot 支持ConcurrentHashMapJDK 自带线程安全 Map基于数组链表/红黑树实现一般仅满足基础缓存需求无优化无自动淘汰策略数据会无限堆积需手动清理高无内存优化数据存储冗余简单少量数据、小项目临时缓存、无需自动淘汰的场景无内置支持需手动封装Guava CacheGoogle 开源本地缓存基于 ConcurrentHashMap 封装优秀满足大部分中小型项目需求LRU最近最少使用淘汰算法支持过期时间中等有基础内存优化老项目遗留、旧系统维护、对性能要求不极致的场景无内置支持需手动引入依赖、封装Caffeine基于 Google Guava 优化采用更高效的内存结构极强业界最优比 Guava 快 10 倍以上Window TinyLFU 算法兼顾访问频率访问时间命中率最高低内存优化极佳占用小新项目、高并发场景、多级缓存本地层首选企业级标准选型SpringBoot 2.x 官方内置支持无需复杂配置企业级项目统一选型 Caffeine无论是性能、淘汰算法、内存占用还是 SpringBoot 的支持度Caffeine 都是最优解具体优势再强调 3 点面试直接答• 性能最优底层优化极致访问速度比 Guava 快 10 倍以上支持高并发场景下的快速访问• 命中率最高采用 Window TinyLFU 淘汰算法完美解决 LRU 算法“被冷数据冲掉热点数据”的问题缓存命中率业界领先• 集成便捷SpringBoot 2.x 官方内置支持无需手动引入复杂依赖配置简单API 优雅开发成本低。注意ConcurrentHashMap 仅适合临时缓存少量数据绝对不能用于高并发多级缓存的本地层Guava Cache 已逐渐被 Caffeine 替代新项目不推荐使用。三、多级缓存整体架构设计多级缓存的核心是“分层拦截、依次回写”我们先明确本地缓存Caffeine Redis 数据库三层缓存的完整访问链路这是所有项目通用的标准流程必须记牢1. 完整访问链路1. 用户请求通过网关进入微服务实例首先访问一级缓存本地 Caffeine 缓存2. 如果本地缓存命中数据存在且未过期直接返回数据整个流程结束无任何网络 IO 开销耗时微秒级3. 如果本地缓存未命中数据不存在或已过期继续访问二级缓存Redis 分布式缓存4. 如果 Redis 缓存命中返回数据同时将数据回写到本地 Caffeine 缓存供后续请求快速命中再返回给前端5. 如果 Redis 缓存也未命中最后访问三级存储MySQL 数据库6. 如果数据库命中数据依次回写缓存先写入 Redis分布式共享再写入本地 Caffeine 缓存本地快速访问最后返回数据给前端7. 如果数据库也无数据直接返回空或默认值不写入任何缓存避免缓存穿透。2. 核心设计原则多级缓存不是“本地缓存Redis”的简单叠加必须遵循以下 5 个设计原则否则会出现内存溢出、数据不一致、性能不升反降等问题•本地缓存只存超级热点 Key本地缓存是 JVM 堆内存空间有限绝对不能存放全量数据只缓存访问频率极高、数据量小的超级热点 Key比如首页轮播数据、秒杀商品信息防止 JVM 内存溢出OOM•每层缓存都必须设置过期时间和淘汰策略本地缓存设置过期时间最大容量淘汰策略Redis 设置过期时间避免数据长期驻留、出现脏数据•两层缓存过期时间差异化设计核心原则——本地缓存 TTL Redis 缓存 TTL本地缓存先过期流量回落 Redis保证分布式场景下的数据统一•数据更新时双缓存同步删除更新/删除数据时必须同时删除本地缓存和 Redis 缓存坚决杜绝“只删一个缓存”的情况保证双缓存一致性•必须解决分布式多实例下本地缓存更新不同步问题这是多级缓存最容易出问题的点后面会专门拆解解决方案。四、SpringBoot 整合多级缓存完整实现Caffeine Redis下面我们一步步实现 SpringBoot 与多级缓存的整合从依赖引入、配置文件、缓存管理器、工具类到业务层实战代码全部可直接复制注释详细新手也能轻松落地。前置环境SpringBoot 2.7.x、Redis 6.x、MySQL 8.0确保 Redis 已启动数据库正常连接。1. Maven 依赖引入 SpringBoot 核心依赖包含 Web、Spring Cache统一缓存整合、Redis、Caffeine、Lombok无需额外引入其他依赖SpringBoot 已内置 Caffeine 支持。!-- SpringBoot Web 核心 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- Spring Cache 统一缓存整合核心用于整合本地缓存和Redis -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-cache/artifactId /dependency !-- Redis 分布式缓存依赖 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-redis/artifactId /dependency !-- Caffeine 本地缓存依赖SpringBoot 2.x 内置无需指定版本 -- dependency groupIdcom.github.ben-manes.caffeine/groupId artifactIdcaffeine/artifactId /dependency !-- Lombok 简化开发可选建议引入 -- dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency !-- MyBatis-Plus 简化数据库操作可选也可使用原生MyBatis -- dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3.1/version /dependency !-- MySQL 驱动 -- dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependency2. application.yml 完整配置统一配置 Redis、Caffeine 本地缓存、数据库重点配置缓存过期时间、最大容量、序列化方式避免出现缓存乱码、内存溢出等问题。spring: # 数据库配置用于查询原始数据 datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/springboot_demo?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneGMT%2B8 username: root password: 123456 # Redis 分布式缓存配置二级缓存 redis: host: localhost port: 6379 password: # 无密码留空有密码填写实际密码 database: 0 # 缓存专用数据库避免与业务数据冲突 lettuce: pool: maximum-pool-size: 20 # 最大连接数根据高并发需求调整 minimum-idle: 5 # 最小空闲连接保证快速响应 max-wait: 3000 # 最大等待时间避免连接阻塞 timeout: 5000 # 连接超时时间单位毫秒 # Spring Cache 多级缓存统一配置核心 cache: type: CAFFEINE # 默认使用 Caffeine 本地缓存配合自定义缓存管理器实现多级缓存 # Caffeine 本地缓存配置一级缓存 caffeine: maximum-size: 10000 # 本地缓存最大条数核心防止OOM expire-after-write: 60s # 本地缓存过期时间60秒比Redis短 initial-capacity: 1000 # 本地缓存初始容量避免频繁扩容 # MyBatis-Plus 配置可选根据实际使用调整 mybatis-plus: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.xxx.entity configuration: map-underscore-to-camel-case: true # 下划线转驼峰 # 日志配置可选用于调试缓存命中情况 logging: level: com.xxx.service: debug org.springframework.cache: debug✅ 关键配置说明•caffeine.maximum-size: 10000本地缓存最大条数必须配置防止 JVM 内存溢出根据业务热点数据量调整一般 1 万-10 万条•caffeine.expire-after-write: 60s本地缓存过期时间60 秒比 Redis 短后面 Redis 配置 300 秒实现差异化 TTL•redis.database: 0缓存专用数据库与业务数据库分离避免误操作删除业务数据•cache.type: CAFFEINE默认使用本地缓存配合自定义缓存管理器实现“本地Redis”的多级缓存路由。3. 多级缓存配置类自定义缓存管理器SpringBoot 内置的缓存管理器只能单独使用 Caffeine 或 Redis无法实现“优先本地、再 Redis”的多级缓存逻辑。因此我们需要自定义缓存管理器整合 Caffeine 和 Redis实现自定义缓存路由。同时配置 Redis 序列化方式避免缓存乱码、过期策略确保多级缓存正常协同工作。import com.github.benmanes.caffeine.cache.Caffeine; import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.caffeine.CaffeineCache; import org.springframework.cache.support.SimpleCacheManager; 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.serializer.GenericJackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializationContext; import org.springframework.data.redis.serializer.StringRedisSerializer; import java.time.Duration; import java.util.ArrayList; import java.util.List; /** * 多级缓存配置类 * 整合 Caffeine 本地缓存 Redis 分布式缓存实现优先本地、再 Redis 的多级缓存逻辑 */ Configuration EnableCaching // 开启 Spring Cache 注解支持 public class MultiCacheConfig { /** * 1. 配置 Caffeine 本地缓存一级缓存 * 这里单独配置 Caffeine 实例可自定义不同缓存空间的过期时间、容量 */ Bean public CaffeineObject, Object caffeineCache() { return Caffeine.newBuilder() .maximumSize(10000) // 最大容量 .expireAfterWrite(Duration.ofSeconds(60)) // 过期时间 60秒 .initialCapacity(1000) // 初始容量 // 可选配置缓存移除监听器用于调试生产可注释 .removalListener((key, value, cause) - { System.out.println(本地缓存移除key key , 原因 cause); }); } /** * 2. 配置 Redis 缓存管理器二级缓存 * 配置序列化方式、全局过期时间、自定义缓存空间 */ Bean public RedisCacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) { // Redis 缓存配置全局 RedisCacheConfiguration redisCacheConfig RedisCacheConfiguration.defaultCacheConfig() .entryTtl(Duration.ofSeconds(300)) // Redis 全局过期时间 300秒比本地长 // key 序列化String 序列化避免乱码 .serializeKeysWith(RedisSerializationContext.SerializationPair .fromSerializer(new StringRedisSerializer())) // value 序列化JSON 序列化支持复杂对象避免乱码 .serializeValuesWith(RedisSerializationContext.SerializationPair .fromSerializer(new GenericJackson2JsonRedisSerializer())) .disableCachingNullValues(); // 不缓存 null 值防止缓存穿透 // 自定义不同缓存空间的过期时间可选比如商品缓存、用户缓存分开配置 // 比如商品缓存过期时间 300秒用户缓存过期时间 1800秒 return RedisCacheManager.builder(redisConnectionFactory) .cacheDefaults(redisCacheConfig) // 应用全局配置 .withCacheConfiguration(productCache, redisCacheConfig.entryTtl(Duration.ofSeconds(300))) .withCacheConfiguration(userCache, redisCacheConfig.entryTtl(Duration.ofSeconds(1800))) .build(); } /** * 3. 自定义多级缓存管理器核心 * 实现优先查询本地 Caffeine 缓存未命中再查询 Redis 缓存 */ Bean public CacheManager multiLevelCacheManager(CaffeineObject, Object caffeine, RedisCacheManager redisCacheManager) { SimpleCacheManager cacheManager new SimpleCacheManager(); ListCache caches new ArrayList(); // 自定义缓存名称与业务缓存空间对应比如 productCache、userCache String[] cacheNames {productCache, userCache}; for (String cacheName : cacheNames) { // 每个缓存空间都整合 Caffeine 本地缓存 Redis 缓存 CaffeineCache caffeineCache new CaffeineCache(cacheName, caffeine.build()); // 自定义缓存实现重写 get 方法实现优先本地、再 Redis 的逻辑 Cache multiLevelCache new Cache() { Override public String getName() { return cacheName; } Override public Object getNativeCache() { return caffeineCache.getNativeCache(); } Override public ValueWrapper get(Object key) { // 1. 先查询本地 Caffeine 缓存 ValueWrapper localValue caffeineCache.get(key); if (localValue ! null) { return localValue; } // 2. 本地未命中查询 Redis 缓存 Cache redisCache redisCacheManager.getCache(cacheName); ValueWrapper redisValue redisCache.get(key); if (redisValue ! null) { // 3. Redis 命中回写到本地缓存 caffeineCache.put(key, redisValue.get()); return redisValue; } // 4. 都未命中返回 null return null; } Override public T T get(Object key, ClassT type) { // 复用 get 方法逻辑简化实现 ValueWrapper wrapper get(key); return wrapper ! null ? type.cast(wrapper.get()) : null; } Override public T T get(Object key, CallableT valueLoader) { // 数据库查询逻辑未命中时调用 valueLoader查询数据库 ValueWrapper wrapper get(key); if (wrapper ! null) { return (T) wrapper.get(); } try { T value valueLoader.call(); if (value ! null) { // 数据库查询到数据回写到本地和 Redis 缓存 put(key, value); } return value; } catch (Exception e) { throw new RuntimeException(多级缓存查询数据库失败, e); } } Override public void put(Object key, Object value) { // 写入缓存同时写入本地 Caffeine 和 Redis caffeineCache.put(key, value); Cache redisCache redisCacheManager.getCache(cacheName); redisCache.put(key, value); } Override public void evict(Object key) { // 删除缓存同时删除本地 Caffeine 和 Redis caffeineCache.evict(key); Cache redisCache redisCacheManager.getCache(cacheName); redisCache.evict(key); } Override public void clear() { // 清空缓存同时清空本地 Caffeine 和 Redis caffeineCache.clear(); Cache redisCache redisCacheManager.getCache(cacheName); redisCache.clear(); } }; caches.add(multiLevelCache); } cacheManager.setCaches(caches); return cacheManager; } }✅ 核心说明这个自定义缓存管理器是多级缓存的核心重写了 Cache 的 get、put、evict 等方法实现了“优先本地、再 Redis、最后数据库”的完整逻辑同时保证了双缓存的同步写入、同步删除无需手动操作两层缓存。4. 自定义多级缓存工具类虽然自定义了缓存管理器但为了进一步简化业务层代码我们封装一个统一的多级缓存工具类将缓存查询、删除、清空等操作封装起来业务层直接注入调用无需关注底层缓存逻辑实现业务代码无侵入。import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; import org.springframework.stereotype.Component; import javax.annotation.Resource; import java.util.function.Supplier; /** * 多级缓存统一工具类 * 封装缓存查询、写入、删除、清空操作业务层直接调用无需关注底层逻辑 * 一级缓存Caffeine 本地缓存 * 二级缓存Redis 分布式缓存 * 三级存储MySQL 数据库 */ Component public class MultiLevelCacheUtil { Resource private CacheManager multiLevelCacheManager; /** * 核心方法多级缓存查询自动回写 * 执行顺序本地 Caffeine → Redis → 数据库通过 supplier 提供 * param cacheName 缓存空间名称与配置类中一致比如 productCache * param key 缓存 key确保唯一建议格式业务模块:id比如 product:1001 * param dbSupplier 数据库查询函数式接口未命中时调用查询数据库 * return 数据可能为 null */ public T T get(String cacheName, String key, SupplierT dbSupplier) { // 获取对应的多级缓存 Cache cache multiLevelCacheManager.getCache(cacheName); if (cache null) { throw new RuntimeException(多级缓存空间不存在 cacheName); } // 调用缓存管理器的 get 方法自动执行 本地→Redis→数据库 流程 return cache.get(key, dbSupplier); } /** * 写入多级缓存同时写入本地 Redis * param cacheName 缓存空间 * param key 缓存 key * param value 缓存值 */ public void put(String cacheName, String key, Object value) { Cache cache multiLevelCacheManager.getCache(cacheName); if (cache ! null) { cache.put(key, value); } } /** * 删除多级缓存同时删除本地 Redis * param cacheName 缓存空间 * param key 缓存 key */ public void delete(String cacheName, String key) { Cache cache multiLevelCacheManager.getCache(cacheName); if (cache ! null) { cache.evict(key); } } /** * 清空指定缓存空间的所有缓存同时清空本地 Redis * param cacheName 缓存空间 */ public void clearCache(String cacheName) { Cache cache multiLevelCacheManager.getCache(cacheName); if (cache ! null) { cache.clear(); } } /** * 清空所有缓存空间的缓存谨慎使用一般用于系统重启、全量更新 */ public void clearAllCache() { multiLevelCacheManager.getCacheNames().forEach(this::clearCache); } }5. 业务 Service 层实战使用以商品服务为例演示多级缓存的实际使用业务层只需注入工具类一行代码实现多级缓存查询、更新、删除完全无侵入代码简洁高效。先定义实体类、Mapper 接口简化示例重点展示缓存使用// 1. 商品实体类Product.java import lombok.Data; import java.math.BigDecimal; import java.time.LocalDateTime; Data public class Product { private Long id; // 商品ID private String name; // 商品名称 private BigDecimal price; // 商品价格 private Integer stock; // 库存 private LocalDateTime createTime; // 创建时间 private LocalDateTime updateTime; // 更新时间 } // 2. 商品 Mapper 接口ProductMapper.java import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.apache.ibatis.annotations.Mapper; Mapper public interface ProductMapper extends BaseMapperProduct { // 继承 BaseMapper无需手动编写查询、更新、删除方法 }业务 Service 层实现import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; /** * 商品服务演示多级缓存实战使用 */ Service public class ProductService { Resource private ProductMapper productMapper; Resource private MultiLevelCacheUtil multiLevelCacheUtil; // 缓存空间名称与配置类中一致 private static final String CACHE_NAME productCache; // 缓存 key 前缀避免 key 冲突格式业务模块:id private static final String CACHE_KEY_PREFIX product:; /** * 多级缓存查询根据商品ID查询商品 * 自动执行本地Caffeine → Redis → 数据库未命中自动回写缓存 */ public Product getProductById(Long id) { // 构建缓存 key String cacheKey CACHE_KEY_PREFIX id; // 调用多级缓存工具类dbSupplier 是数据库查询逻辑函数式接口 return multiLevelCacheUtil.get(CACHE_NAME, cacheKey, () - productMapper.selectById(id)); } /** * 更新商品更新数据库 同步删除双缓存保证一致性 * 核心逻辑先更数据库再删缓存避免双写顺序错误 */ Transactional(rollbackFor Exception.class) public void updateProduct(Product product) { // 1. 先更新数据库事务保证原子性 productMapper.updateById(product); // 2. 同步删除本地缓存 Redis 缓存避免脏数据 String cacheKey CACHE_KEY_PREFIX product.getId(); multiLevelCacheUtil.delete(CACHE_NAME, cacheKey); } /** * 删除商品删除数据库 同步删除双缓存 */ Transactional(rollbackFor Exception.class) public void deleteProduct(Long id) { // 1. 先删除数据库 productMapper.deleteById(id); // 2. 同步删除双缓存 String cacheKey CACHE_KEY_PREFIX id; multiLevelCacheUtil.delete(CACHE_NAME, cacheKey); } /** * 手动刷新商品缓存比如商品上下架、活动调价后手动刷新 */ public void refreshProductCache(Long id) { String cacheKey CACHE_KEY_PREFIX id; // 先删除旧缓存 multiLevelCacheUtil.delete(CACHE_NAME, cacheKey); // 再查询数据库重新写入缓存 Product product productMapper.selectById(id); if (product ! null) { multiLevelCacheUtil.put(CACHE_NAME, cacheKey, product); } } }✅ 实战说明• 查询商品只需调用工具类的 get 方法传入缓存空间、key 和数据库查询逻辑工具类会自动完成“本地→Redis→数据库”的查询和回写业务层无需关注底层缓存逻辑• 更新/删除商品先操作数据库再调用工具类的 delete 方法同步删除本地和 Redis 缓存避免数据不一致• 手动刷新缓存适合商品上下架、调价等场景手动删除旧缓存、重新写入新数据确保缓存数据最新。五、多级缓存核心关键设计细节多级缓存的落地细节决定成败。以下 3 个核心设计细节既是面试高频考点也是生产环境必须遵循的原则缺一不可。1. 两层缓存过期时间设计差异化 TTL前面反复强调本地缓存 TTL Redis 缓存 TTL这是保证分布式场景下数据一致性的关键我们再深入拆解原因面试直接答• 防止 JVM 内存溢出本地缓存是 JVM 堆内存空间有限短 TTL 能让数据快速过期释放内存避免数据无限堆积• 保证数据统一本地缓存先过期后续请求会去 Redis 查询最新数据再回写到本地缓存确保所有服务实例的本地缓存都能同步到 Redis 的最新数据• 兜底保障即使本地缓存出现脏数据也会快速过期从 Redis 拉取最新数据避免脏数据长期驻留。生产环境推荐配置参考• 本地 CaffeineTTL 60-300 秒根据热点数据更新频率调整• RedisTTL 300-1800 秒是本地缓存的 5-10 倍• 特殊场景比如秒杀商品本地 TTL 30 秒Redis TTL 180 秒确保数据快速同步。2. Caffeine 淘汰算法优势Caffeine 的核心优势之一就是其淘汰算法——Window TinyLFU这也是它比 Guava Cache 性能好、命中率高的核心原因我们拆解一下不用深入底层记住核心逻辑即可Guava Cache 使用的是 LRU最近最少使用算法缺点很明显容易被“一次性冷数据”冲掉热点数据。比如某条冷数据被大量请求一次比如首页广告会被 LRU 算法认为是“热点数据”从而挤掉真正长期访问的热点数据导致缓存命中率下降。而 Window TinyLFU 算法兼顾了访问频率LFU和访问时间LRU核心逻辑• 对数据的访问频率进行统计优先保留访问频率高的数据• 引入“时间窗口”机制对长期未访问的热点数据逐步降低其频率权重避免“僵尸热点数据”长期占用内存• 对一次性冷数据直接过滤不占用缓存空间避免冲掉真正的热点数据。面试总结Caffeine 采用 Window TinyLFU 淘汰算法兼顾访问频率和时间缓存命中率高于 Guava Cache能更好地适应高并发热点数据场景。3. 本地缓存容量限制防止 OOM这是新手最容易踩的坑本地缓存不设置最大容量maximumSize导致数据无限堆积最终引发 JVM 内存溢出OOM系统崩溃。核心原则本地缓存只存超级热点 Key不存全量数据同时必须配置 maximumSize根据业务热点数据量调整一般建议• 中小型项目maximumSize 1 万-5 万条• 大型高并发项目maximumSize 5 万-10 万条• 避免设置过大比如 100 万条否则会占用大量 JVM 内存影响服务正常运行。同时可配合 Caffeine 的 removalListener缓存移除监听器监控本地缓存的移除情况排查是否存在异常比如大量热点数据被淘汰可能是容量设置过小。如果你在实战中遇到问题欢迎在评论区留言交流一起避坑、一起进步别忘了点赞在看收藏三连关注我解锁更多 SpringBoot AOP 实战干货下期再见❤️