Caffeine结合Redis空值缓存实现多级缓存

 更新时间:2026年01月25日 10:18:37   作者:三水不滴  
本文介绍了SpringBoot整合Caffeine本地缓存+Redis分布式缓存+空值缓存的三级缓存方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

在高并发系统中,缓存是提升响应速度、减轻数据库压力的核心手段,但单一缓存方案往往难以应对复杂场景 —— 本地缓存缺乏分布式一致性,Redis 缓存存在网络开销,还可能遭遇穿透、雪崩、击穿等致命问题。本文基于实战案例,详解 SpringBoot 整合Caffeine 本地缓存 + Redis 分布式缓存 + 空值缓存的三级缓存方案,从架构设计到代码落地,构建高可用、低延迟的缓存体系。

一、多级缓存架构设计:为什么要 “三级联动”?

传统缓存方案要么依赖单一本地缓存(无法分布式共享),要么仅用 Redis(网络 IO 开销影响性能),而三级缓存架构通过 “本地缓存 + 分布式缓存 + 数据库” 的层级设计,实现了 “速度” 与 “一致性” 的平衡:

  1. 第一级:Caffeine 本地缓存基于 Java 内存的高性能缓存,读写延迟低至纳秒级,专门存储热点数据(如高频访问的商品信息、配置参数),避免重复查询 Redis 和数据库,提升核心接口响应速度。
  2. 第二级:Redis 分布式缓存分布式环境下的共享缓存,解决本地缓存数据不一致问题,同时承担 “中间缓冲” 角色,减少数据库直接访问压力。
  3. 第三级:数据库数据最终存储源,仅在缓存未命中时触发查询,保证数据可靠性。

核心优势

  • 性能极致:本地缓存命中率超 90%,Redis 缓存命中率超 95%,99% 请求响应时间 < 10ms;
  • 高可用:故障隔离设计,某一级缓存失效不影响整体服务(如 Redis 宕机时,本地缓存可临时兜底);
  • 资源优化:减少 Redis 网络 IO 和数据库查询压力,降低集群部署成本;
  • 多层防护:从架构层面规避缓存穿透、雪崩、击穿三大经典问题。

二、核心问题解决方案:三大缓存难题逐个击破

1. 缓存穿透:拦截无效查询

问题:恶意请求查询不存在的数据(如 ID=-1 的商品),导致缓存失效后直接穿透到数据库,引发性能问题。解决方案:空值缓存 + 布隆过滤器双重防护

  • 空值缓存:数据库查询无结果时,在 Redis 和 Caffeine 中缓存空值(设置短期过期时间,如 5 分钟),避免重复穿透;
  • 布隆过滤器:预先将数据库中存在的主键(如商品 ID、用户 ID)存入布隆过滤器,请求先经过过滤器校验,无效 ID 直接拦截,不进入缓存和数据库。

2. 缓存雪崩:避免集中失效

问题:大量缓存数据在同一时间过期,或 Redis 集群宕机,导致所有请求瞬间涌向数据库,引发数据库雪崩。解决方案:随机过期时间 + 优雅降级

  • 随机 TTL:缓存数据时,在基础过期时间(如 30 分钟)上增加随机值(5-10 分钟),使缓存过期时间分散,避免集中失效;
  • 优雅降级:Redis 宕机时,启用本地缓存兜底,同时触发告警机制,保障服务核心功能可用。

3. 缓存击穿:保护热点数据

问题:热点数据(如秒杀商品)缓存过期瞬间,大量并发请求穿透到数据库,导致数据库压力骤增。解决方案:热点数据预热 + 分布式锁

  • 数据预热:系统启动时,主动将热点数据加载到 Caffeine 和 Redis 中,避免缓存冷启动;
  • 分布式锁:缓存过期后,通过 Redis 分布式锁(如 Redisson)控制,仅允许一个线程查询数据库并更新缓存,其他线程等待缓存更新后再查询,防止并发穿透。

三、实战落地:SpringBoot 整合三级缓存

1. 依赖配置

首先引入核心依赖(Maven 示例),包含 SpringBoot 缓存 starter、Caffeine、Redis、Redisson(分布式锁):

<!-- SpringBoot缓存核心依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- Caffeine本地缓存 -->
<dependency>
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
    <version>3.1.8</version>
</dependency>
<!-- Redis依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- Redisson分布式锁 -->
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.23.3</version>
</dependency>

2. 核心配置文件(application.yml)

配置 Caffeine 缓存参数、Redis 连接信息、分布式锁等:

spring:
  # Redis配置
  redis:
    host: 127.0.0.1
    port: 6379
    password: 123456
    lettuce:
      pool:
        max-active: 8
        max-idle: 8
        min-idle: 2
        max-wait: 1000ms
    timeout: 3000ms
  # 缓存配置
  cache:
    type: caffeine
    caffeine:
      # 初始容量、最大容量、过期时间(写入后30分钟过期)
      initial-capacity: 100
      maximum-size: 1000
      expire-after-write: 30m

# 自定义缓存配置
cache:
  # 空值缓存过期时间(5分钟)
  null-value-expire: 5m
  # 热点数据预热key前缀
  hot-data-prefix: "hot:"
  # 分布式锁前缀
  lock-prefix: "cache:lock:"

3. 核心代码实现

(1)缓存配置类:初始化 Caffeine 和 Redis 缓存

import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.cache.CacheManager;
import org.springframework.cache.caffeine.CaffeineCacheManager;
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.HashMap;
import java.util.Map;

@Configuration
public class CacheConfig {

    // Caffeine缓存管理器(本地缓存)
    @Bean
    public CacheManager caffeineCacheManager() {
        CaffeineCacheManager cacheManager = new CaffeineCacheManager();
        // 配置Caffeine缓存参数:初始容量100,最大容量1000,写入后30分钟过期
        cacheManager.setCaffeine(Caffeine.newBuilder()
                .initialCapacity(100)
                .maximumSize(1000)
                .expireAfterWrite(Duration.ofMinutes(30)));
        return cacheManager;
    }

    // Redis缓存管理器(分布式缓存)
    @Bean
    public RedisCacheManager redisCacheManager(RedisConnectionFactory connectionFactory) {
        // 序列化配置(避免Redis存储乱码)
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofMinutes(30)) // 默认过期时间30分钟
                .serializeKeysWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(new GenericJackson2JsonRedisSerializer()));

        // 自定义不同缓存的过期时间(如空值缓存5分钟)
        Map<String, RedisCacheConfiguration> cacheConfigs = new HashMap<>();
        cacheConfigs.put("nullValueCache", config.entryTtl(Duration.ofMinutes(5)));

        return RedisCacheManager.builder(connectionFactory)
                .cacheDefaults(config)
                .withInitialCacheConfigurations(cacheConfigs)
                .build();
    }
}

(2)缓存工具类:封装三级缓存查询逻辑

核心逻辑:先查 Caffeine→再查 Redis→最后查数据库,同时处理空值缓存、分布式锁、缓存更新:

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

@Component
public class CacheUtil {

    @Resource
    private CacheManager caffeineCacheManager;
    @Resource
    private RedisCacheManager redisCacheManager;
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private BloomFilterUtil bloomFilterUtil; // 布隆过滤器工具类

    // 缓存查询核心方法:key-缓存键,clazz-返回类型,dbLoader-数据库查询逻辑
    public <T> T getCache(String key, Class<T> clazz, DataLoader<T> dbLoader) {
        // 1. 布隆过滤器校验:无效key直接返回null
        if (!bloomFilterUtil.contains(key)) {
            return null;
        }

        // 2. 查询Caffeine本地缓存
        Cache caffeineCache = caffeineCacheManager.getCache("localCache");
        T localValue = caffeineCache.get(key, clazz);
        if (localValue != null) {
            return localValue;
        }

        // 3. 查询Redis分布式缓存
        Cache redisCache = redisCacheManager.getCache("redisCache");
        T redisValue = redisCache.get(key, clazz);
        if (redisValue != null) {
            // Redis命中,同步到本地缓存
            caffeineCache.put(key, redisValue);
            return redisValue;
        }

        // 4. 缓存未命中,分布式锁控制数据库查询
        RLock lock = redissonClient.getLock("cache:lock:" + key);
        try {
            // 尝试获取锁,最多等待3秒,持有锁10秒
            if (lock.tryLock(3, 10, TimeUnit.SECONDS)) {
                // 再次查询Redis(防止其他线程已更新缓存)
                redisValue = redisCache.get(key, clazz);
                if (redisValue != null) {
                    caffeineCache.put(key, redisValue);
                    return redisValue;
                }

                // 5. 查询数据库
                T dbValue = dbLoader.load();
                if (dbValue != null) {
                    // 数据库有结果,更新各级缓存
                    redisCache.put(key, dbValue);
                    caffeineCache.put(key, dbValue);
                } else {
                    // 数据库无结果,缓存空值(5分钟过期)
                    Cache nullValueCache = redisCacheManager.getCache("nullValueCache");
                    nullValueCache.put(key, null);
                    caffeineCache.put(key, null);
                }
                return dbValue;
            } else {
                // 获取锁失败,返回默认值或抛出异常
                throw new RuntimeException("缓存更新繁忙,请稍后重试");
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return null;
        } finally {
            // 释放锁
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    // 数据加载函数式接口(封装数据库查询逻辑)
    @FunctionalInterface
    public interface DataLoader<T> {
        T load();
    }
}

(3)缓存更新与清除:保障数据一致性

当数据库数据发生变更(新增、修改、删除)时,需同步清除各级缓存,避免数据不一致:

// 缓存清除方法(用于数据库更新后)
public void clearCache(String key) {
    // 1. 清除本地缓存
    Cache caffeineCache = caffeineCacheManager.getCache("localCache");
    caffeineCache.evict(key);
    // 2. 清除Redis缓存
    Cache redisCache = redisCacheManager.getCache("redisCache");
    redisCache.evict(key);
    // 3. 清除空值缓存
    Cache nullValueCache = redisCacheManager.getCache("nullValueCache");
    nullValueCache.evict(key);
}

(4)热点数据预热:系统启动时加载

通过CommandLineRunner实现系统启动时预热热点数据,避免缓存冷启动:

import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.List;

@Component
public class HotDataPreloader implements CommandLineRunner {

    @Resource
    private CacheUtil cacheUtil;
    @Resource
    private ProductMapper productMapper; // 数据库DAO层

    @Override
    public void run(String... args) throws Exception {
        // 加载热点商品数据(如销量前100的商品)
        List<Product> hotProducts = productMapper.selectHotProducts(100);
        for (Product product : hotProducts) {
            String key = "product:" + product.getId();
            // 存入本地缓存和Redis
            cacheUtil.caffeineCacheManager.getCache("localCache").put(key, product);
            cacheUtil.redisCacheManager.getCache("redisCache").put(key, product);
        }
        System.out.println("热点数据预热完成,共加载" + hotProducts.size() + "条数据");
    }
}

四、优化与监控:让缓存体系更稳定

1. 配置优化建议

  • Caffeine 参数:初始容量设为预期热点数据量的 80%,最大容量避免超过 JVM 内存的 30%(防止 OOM);
  • Redis 优化:开启持久化(AOF+RDB),配置主从复制,避免单点故障;调整连接池参数适配并发量;
  • 过期时间:根据数据更新频率调整,高频更新数据(如库存)过期时间设为 5-10 分钟,低频数据设为 1-2 小时。

2. 监控与告警

  • 缓存命中率:通过 Spring Boot Actuator 暴露缓存指标,监控 Caffeine 和 Redis 命中率(目标:均≥90%);
  • 响应时间:统计接口缓存命中 / 未命中的响应时间,超过阈值(如 50ms)触发告警;
  • 异常监控:监控 Redis 连接异常、分布式锁获取失败等情况,及时排查问题。

3. 注意事项

  • 数据一致性:缓存清除需与数据库事务同步(建议用事务提交后异步清除,避免阻塞业务);
  • 内存管理:Caffeine 缓存避免存储大对象,定期清理过期数据;Redis 启用内存淘汰策略(如 LRU);
  • 敏感数据:缓存中不存储明文敏感数据(如密码、手机号),需加密后存储;
  • 降级策略:Redis 集群故障时,关闭 Redis 缓存读取,仅用本地缓存 + 数据库兜底,保障核心功能可用。

五、总结

SpringBoot+Caffeine+Redis + 空值缓存的三级缓存方案,通过 “本地缓存提效、分布式缓存保一致、空值缓存防穿透” 的设计,完美解决了高并发场景下的缓存核心难题。该方案不仅能将接口响应时间压缩至毫秒级,还能大幅降低数据库压力,同时具备故障隔离、优雅降级的高可用特性,适用于电商、支付、社交等各类高并发系统。

实际落地时,可根据业务场景灵活调整缓存参数(如过期时间、最大容量)和预热策略,结合监控工具持续优化,让缓存体系真正成为系统的 “性能加速器”。

到此这篇关于Caffeine结合Redis空值缓存实现多级缓存的文章就介绍到这了,更多相关Caffeine结合Redis实现多级缓存内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 百行代码实现基于Redis的可靠延迟队列

    百行代码实现基于Redis的可靠延迟队列

    本文主要介绍了百行代码实现基于Redis的可靠延迟队列,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-06-06
  • Redis三种特殊数据类型的具体使用

    Redis三种特殊数据类型的具体使用

    本文主要介绍了Redis三种特殊数据类型的具体使用,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-02-02
  • 浅析Redis 切片集群的数据倾斜问题

    浅析Redis 切片集群的数据倾斜问题

    如果 Redis 中的部署,采用的是切片集群,数据是会按照一定的规则分散到不同的实例中保存,比如,使用 Redis Cluster 或 Codis,这篇文章主要介绍了Redis 切片集群的数据倾斜分析,需要的朋友可以参考下
    2022-06-06
  • Redis Value过大问题(键值过大)

    Redis Value过大问题(键值过大)

    这篇文章主要介绍了Redis Value过大问题(键值过大),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-12-12
  • redis数据的两种持久化方式对比

    redis数据的两种持久化方式对比

    Redis是我们开发中常用的数据库,今天和大家分享的就是redis持久化的2种方式:RDB(Redis DataBase)和AOF(Apend Only File),希望对大家学习redis有帮助,一起来看看吧。
    2017-08-08
  • Windows中Redis安装配置流程并实现远程访问功能

    Windows中Redis安装配置流程并实现远程访问功能

    很多在windows环境中安装Redis总是出错,今天小编抽空给大家分享在Windows中Redis安装配置流程并实现远程访问功能,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2021-06-06
  • Spring redis使用报错Read timed out排查及解决过程

    Spring redis使用报错Read timed out排查及解决过程

    项目使用spring集成redis,偶尔会出现read timed out的情况,刚开始以为是网络不稳定引起的,后面发现影响业务测试的准确性,这篇文章主要给大家介绍了关于Spring redis使用报错Read timed out排查及解决过程的相关资料,需要的朋友可以参考下
    2024-02-02
  • Redis的安全机制详细介绍及配置方法

    Redis的安全机制详细介绍及配置方法

    本文介绍Redis安全机制的配置方法,包括绑定IP地址、设置密码、保护模式、禁用危险命令、防火墙限制、TLS加密、客户端连接限制、最大内存使用和日志审计等,通过合理配置这些安全机制,可以有效地保护Redis实例免受未授权访问和其他潜在的安全威胁,感兴趣的朋友一起看看吧
    2025-11-11
  • 浅谈redission锁的默认失效时间

    浅谈redission锁的默认失效时间

    Redisson是一个基于Redis的Java驻留库,提供了许多分布式对象和服务,包括分布式锁,本文主要介绍了浅谈redission锁的默认失效时间, 具有一定的参考价值,感兴趣的可以了解一下
    2024-02-02
  • Redis如何一键部署脚本

    Redis如何一键部署脚本

    这篇文章主要介绍了Redis如何一键部署脚本,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-04-04

最新评论