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-05-05
  • redis-cli创建redis集群的实现

    redis-cli创建redis集群的实现

    本文主要介绍了redis-cli创建redis集群的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-06-06
  • Redis优化token校验主动失效的实现方案

    Redis优化token校验主动失效的实现方案

    在普通的token颁发和校验中 当用户发现自己账号和密码被暴露了时修改了登录密码后旧的token仍然可以通过系统校验直至token到达失效时间,所以系统需要token主动失效的一种能力,所以本文给大家介绍了Redis优化token校验主动失效的实现方案,需要的朋友可以参考下
    2024-03-03
  • Redis预防缓存穿透的6种策略

    Redis预防缓存穿透的6种策略

    缓存穿透是指查询一个根本不存在的数据,由于缓存不命中,请求会穿透缓存层直接访问数据库,本文整理了6个Redis预防缓存穿透的方法,希望对大家有一定的帮助
    2025-04-04
  • 多维度深入分析Redis的5种基本数据结构

    多维度深入分析Redis的5种基本数据结构

    此篇文章主要对Redis的5种基本数据类型,即字符串(String)、列表(List)、散列(Hash)、集合(Set)、有序集合(Sorted Set),从使用场景和底层结构出发,进行多维度深入分析。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-11-11
  • Redis一键巡检脚本的实现

    Redis一键巡检脚本的实现

    在使用Redis作为数据存储的时候,定期进行巡检是非常重要的,本文主要介绍了Redis一键巡检脚本的实现,具有一定的参考价值,感兴趣的可以了解一下
    2024-06-06
  • redis事务_动力节点Java学院整理

    redis事务_动力节点Java学院整理

    这篇文章主要介绍了redis事务,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-08-08
  • Redis跳跃表添加元素的方法实现

    Redis跳跃表添加元素的方法实现

    本文主要介绍了Redis跳跃表添加元素的方法实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-06-06
  • Redis如何从海量key中查询出某一固定前缀的key

    Redis如何从海量key中查询出某一固定前缀的key

    当Redis存储一亿key时,使用keys指令可能因返回全部key导致服务器卡顿,而scan指令通过游标分批获取,避免阻塞,适合生产环境,需注意重复结果可用hashSet去重,count参数可调整返回数量但非强制
    2025-07-07
  • 一文搞懂Redis中的慢查询日志和监视器

    一文搞懂Redis中的慢查询日志和监视器

    我们都知道MySQL有慢查询日志,但Redis也有慢查询日志,可用于监视和优化查询,本文给大家详细介绍了Redis中的慢查询日志和监视器,文章通过代码示例讲解的非常详细,需要的朋友可以参考下
    2024-04-04

最新评论