如何在 Spring 中实现高性能多级缓存(含 Caffeine + Redis 实战)

70 阅读4分钟

沉默是金,总会发光

大家好,我是沉默

缓存不是“快就完事”,它关系到可用性、数据一致性与运维成本。

错误的缓存策略,会把你从 200ms 拉升到 2s,甚至引发雪崩、脏数据风暴。

本文把多级缓存流程策略、项目实践、常见坑与面试样例都给你,适合直接复制到项目中执行。

**-**01-

选型决策

常用缓存对比表:

类型Spring Cache(抽象)CaffeineEhcacheRedisGuava(已淘汰)
特性抽象层本地(JVM)本地/持久化分布式本地(旧)
性能取决于实现极高(W-TinyLFU)中高(支持磁盘)高(网络延迟依赖)中(LRU)
分布式支持无(接口)有(复杂)原生
持久化支持磁盘支持 RDB/AOF
适用场景注解化接入单机高并发热点需要重启恢复场景跨服务共享/高并发读写维护旧项目

图片

- 02-

多级缓存请求流程

微服务架构一般用 Caffeine、Redis, 两者结合:

图片

- 03-

实战案例代码

  1. Caffeine 配置(Spring Boot)
@Configuration@EnableCachingpublic class CaffeineConfig {    @Bean    public CacheManager cacheManager() {        CaffeineCacheManager mgrnew CaffeineCacheManager();        mgr.setCaffeine(Caffeine.newBuilder()            .initialCapacity(1000)            .maximumSize(10_000)            .expireAfterWrite(5, TimeUnit.MINUTES)            .recordStats());        return mgr;    }}

2. Redis Cache 配置(Spring Boot)

spring:  redis:    host: redis-server    port: 6379    lettuce:      pool:        max-active: 8
@Configuration@EnableCachingpublic class RedisCacheConfig {    @Bean    public CacheManager cacheManager(RedisConnectionFactory factory) {        RedisCacheConfiguration cfgRedisCacheConfiguration.defaultCacheConfig()            .entryTtl(Duration.ofHours(1))            .disableCachingNullValues()            .serializeValuesWith(RedisSerializationContext.SerializationPair               .fromSerializer(new GenericJackson2JsonRedisSerializer()));        return RedisCacheManager.builder(factory).cacheDefaults(cfg).build();    }}

3. 多级缓存读取示例(本地 + Redis + DB)

@Servicepublic class MultiLevelCacheService {    @Autowired private CacheManager caffeineCacheManager;    @Autowired private CacheManager redisCacheManager;    @Autowired private ProductRepository productRepository;    public Product getProduct(Long id) {        Product p = caffeineCacheManager.getCache("products").get(id, Product.class);        if (p != null) return p;        p = redisCacheManager.getCache("products").get(id, Product.class);        if (p != null) {            caffeineCacheManager.getCache("products").put(id, p);            return p;        }        p = productRepository.findById(id).orElse(null);        if (p != null) {            redisCacheManager.getCache("products").put(id, p);            caffeineCacheManager.getCache("products").put(id, p);        }        return p;    }}

4. 延时双删(更新一致性推荐做法)

@Transactionalpublic void updateProduct(Product product) {    // 第一次删除缓存    redisTemplate.delete("product:" + product.getId());    // 更新 DB    productDao.update(product);    // 异步延时再删除一次    CompletableFuture.runAsync(() -> {        try { Thread.sleep(500); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }        redisTemplate.delete("product:" + product.getId());    });}

**-****04-**总结

常见坑(必看)

  • 禁止:先删缓存再更新 DB(会产生脏数据)——用延时双删或事务消息。
  • 不要:无限制缓存时间(必须 TTL)。
  • 小心allEntries=true 全量清理会导致瞬间穿透。
  • 秒杀不要用悲观锁:悲观锁严重影响吞吐,优先用原子 SQL 或 Redis Lua。

缓存一致性策略速览(面试必背)

策略实现复杂度一致性适用场景
写时更新(Write-Through)金融类强一致场景
写后更新(Write-Behind)异步高吞吐场景
延时双删(Delayed Double Delete)秒杀 / 高并发更新
版本号校验(Cache + Version)精细覆盖控制(配置/用户设置)
事务消息(MQ)高(最终)分布式微服务架构

生产级最佳实践(落地清单)

  1. 优先画决策树:先回答“读写比、是否跨服务、是否强一致”三题再选方案。

  2. 设置 TTL(必做) :防止雪崩,热点可短 TTL + 后台刷新。

  3. 防穿透:布隆过滤器 + 空值缓存(短 TTL)。

  4. 同步本地缓存:Redis Pub/Sub 或消息队列通知各实例清理本地缓存。

  5. 监控报警:命中率、缓存加载时延、Redis 内存、DB QPS。阈值示例:命中率 < 0.7 警告。

  6. 降级策略:缓存不可用时自动查询 DB 并限流降级(避免级联故障)。

  7. 并发控制:热点数据 get 时可用 lock.tryLock + 双重检查避免击穿。

最后

单体/单机热点:用 Caffeine(JVM 本地,超低延迟)。

跨服务/分布式共享:用 Redis(分布式、持久化、HA)。

希望兼顾延迟与一致性:选 Caffeine + Redis(多级缓存 / Cache-Aside)。

旧项目兼容:Guava 可维,但新项目弃用,改用 Caffeine。

**-****05-**粉丝福利

我这里创建一个程序员成长&副业交流群, 


 和一群志同道合的小伙伴,一起聚焦自身发展, 

可以聊:


技术成长与职业规划,分享路线图、面试经验和效率工具, 




探讨多种副业变现路径,从写作课程到私活接单, 




主题活动、打卡挑战和项目组队,让志同道合的伙伴互帮互助、共同进步。 




如果你对这个特别的群,感兴趣的, 
可以加一下, 微信通过后会拉你入群, 
 但是任何人在群里打任何广告,都会被我T掉。