搭建Caffeine+Redis多级缓存机制

 更新时间:2025年07月28日 09:27:09   作者:moxiaoran5753  
本文主要介绍了搭建Caffeine+Redis多级缓存机制,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

本地缓存的简单实现方案有HashMap,CucurrentHashMap,成熟的本地缓存方案有Guava 与 Caffeine ,企业级应用推荐下面说下两者的区别

0. 核心异同对比

特性Guava CacheCaffeine
诞生背景Google Guava 库的一部分(2011年)基于 Guava Cache 重构的现代缓存库(2015+)
性能中等(锁竞争较多)极高(优化并发设计,吞吐量提升5~10倍)
内存管理基于 LRU 算法结合 W-TinyLFU 算法(高命中率)
过期策略支持 expireAfterWrite/access支持 expireAfterWrite/access + refresh
缓存回收同步阻塞异步非阻塞(后台线程)
监控统计基础统计(命中率等)详细统计(命中率、加载时间等)
依赖需引入整个 Guava 库轻量(仅依赖 Caffeine)
社区维护维护模式(新功能少)活跃更新(Java 17+ 兼容)

从上面的比较可知, Caffeine 各方面是优于Guava的,因此在搭建多级缓存机制时,建议使用Caffeine+Redis的组合方案。

业务执行流程

  • 请求优先读取 Caffeine 本地缓存(超快,减少网络IO)。
  • 本地缓存未命中 → 读取 Redis 分布式缓存
  • Redis 未命中 → 查询数据库,并回填到两级缓存。

下面介绍下实现方式

注意:下面的实现方式是基于Springboot 2.4+,版本不同,配置上会略有差异

1.maven中引入下面的依赖

<!-- Caffeine 本地缓存 -->
<dependency>
	<groupId>com.github.ben-manes.caffeine</groupId>
	<artifactId>caffeine</artifactId>
</dependency>

<!-- 缓存抽象层 -->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-cache</artifactId>
</dependency>

<!-- redis 缓存操作 -->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-redis</artifactId>
	   <exclusions>
		 <exclusion>
			 <groupId>io.lettuce</groupId>
			 <artifactId>lettuce-core</artifactId>
		 </exclusion>
		</exclusions>
</dependency>

<dependency>
 <groupId>redis.clients</groupId>
 <artifactId>jedis</artifactId>
</dependency>

2.application中进行配置

spring: 
 cache:
    caffeine:
      spec: maximumSize=1000,expireAfterWrite=10m  # 本地缓存
    redis:
      time-to-live: 1h  # Redis缓存过期时间
  # redis 配置
  redis:
    # 地址
    host: 127.0.0.1
    # 端口,默认为6379
    port: 6379
    # 数据库索引
    database: 0
    # 密码
    password: abc123
    # 连接超时时间
    timeout: 6000ms  # 连接超时时长(毫秒)
    jedis:
      pool:
        max-active: 1000  # 连接池最大连接数(使用负值表示没有限制)
        max-wait: -1ms      # 连接池最大阻塞等待时间(使用负值表示没有限制)
        max-idle: 10      # 连接池中的最大空闲连接
        min-idle: 5       # 连接池中的最小空闲连接

3.自定义多级缓存管理器

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.cache.CacheProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.cache.support.CompositeCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
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 com.github.benmanes.caffeine.cache.Caffeine;

import java.util.concurrent.TimeUnit;


@Configuration
@EnableCaching
@EnableConfigurationProperties(CacheProperties.class)
public class MyCacheConfig {

    @Bean
    public RedisCacheConfiguration cacheConfiguration(CacheProperties cacheProperties) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
        config = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()));
        config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));

        CacheProperties.Redis redisProperties = cacheProperties.getRedis();
        if (redisProperties.getTimeToLive() != null) {
            config = config.entryTtl(redisProperties.getTimeToLive());
        }
        if (redisProperties.getKeyPrefix() != null) {
            config = config.prefixKeysWith(redisProperties.getKeyPrefix());
        }
        if (!redisProperties.isCacheNullValues()) {
            config = config.disableCachingNullValues();
        }
        if (!redisProperties.isUseKeyPrefix()) {
            config = config.disableKeyPrefix();
        }
        return config;
    }

    @Bean
    public Caffeine<Object, Object> caffeineConfig() {
        return Caffeine.newBuilder()
                .maximumSize(1000)
                .expireAfterWrite(10, TimeUnit.MINUTES);
    }

    @Bean
    @Primary  // 添加 @Primary 注解指定 CaffeineCacheManager 作为默认的缓存管理器
    public CacheManager caffeineCacheManager(Caffeine<Object, Object> caffeine) {
        CaffeineCacheManager manager = new CaffeineCacheManager();
        manager.setCaffeine(caffeine);
        return manager;
    }

    @Bean
    public RedisCacheManager redisCacheManager(
            RedisConnectionFactory redisConnectionFactory,
            RedisCacheConfiguration cacheConfiguration) {

        return RedisCacheManager.builder(redisConnectionFactory)
                .cacheDefaults(cacheConfiguration)
                .build();
    }

    @Bean
    public CacheManager compositeCacheManager(
            @Qualifier("caffeineCacheManager") CacheManager caffeineCacheManager,
            @Qualifier("redisCacheManager") CacheManager redisCacheManager) {

        return new CompositeCacheManager(
                caffeineCacheManager,
                redisCacheManager
        );
    }
}
        

4.业务逻辑层调用

使用示例:

@Service
public class ProductService {

    @Autowired
    private ProductRepository repository;

    // 优先读本地缓存,其次Redis,最后数据库
    @Cacheable(cacheNames = "product", key = "#id")
    public Product getProductById(Long id) {
        return repository.findById(id).orElseThrow();
    }

    // 更新数据时清除两级缓存
    @CacheEvict(cacheNames = "product", key = "#product.id")
    public Product updateProduct(Product product) {
        return repository.save(product);
    }
}

手动控制多级缓存

@Service
public class CacheService {

    @Autowired
    private CacheManager cacheManager;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    public Product getProductWithManualControl(Long id) {
        // 1. 先查本地缓存
        Cache caffeineCache = cacheManager.getCache("product");
        Product product = caffeineCache.get(id, Product.class);
        if (product != null) {
            return product;
        }

        // 2. 查Redis缓存
        product = (Product) redisTemplate.opsForValue().get("product:" + id);
        if (product != null) {
            // 回填本地缓存
            caffeineCache.put(id, product);
            return product;
        }

        // 3. 查数据库
        product = repository.findById(id).orElseThrow();
        
        // 回填两级缓存
        redisTemplate.opsForValue().set("product:" + id, product, Duration.ofHours(1));
        caffeineCache.put(id, product);
        
        return product;
    }
}

缓存一致性

  • 使用 @CacheEvict 或 Redis Pub/Sub 同步失效两级缓存。
  • 示例:通过 Redis 消息通知其他节点清理本地缓存。

防止缓存击穿

  • Caffeine 配置 refreshAfterWrite
Caffeine.newBuilder()
    .refreshAfterWrite(5, TimeUnit.MINUTES)
    .build(key -> loadFromRedisOrDb(key));

监控统计:

  • Caffeine 统计:cache.getNativeCache().stats()
  • Redis 统计:INFO commandstats

验证多级缓存

  • 本地缓存生效:连续调用同一接口,观察第二次响应时间骤降。
  • Redis 缓存生效:重启应用后,首次请求仍快速返回(数据来自Redis)。

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

相关文章

  • 基于Redis实现抢红包和发红包功能

    基于Redis实现抢红包和发红包功能

    抢红包是我们生活常用的社交功能, 这个功能最主要的特点就是用户的并发请求高, 在系统设计上, 可以使用非常多的办法来扛住用户的高并发请求, 在本文中简要介绍使用Redis缓存中间件来实现抢红包算法,需要的朋友可以参考下
    2024-04-04
  • redis实现加锁的几种方法示例详解

    redis实现加锁的几种方法示例详解

    这篇文章主要给大家介绍了关于redis实现加锁的几种方法,加锁命令分别是INCR、SETNX和SET,文中给出了详细的示例代码,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。
    2017-09-09
  • 关于Redis未授权访问的问题

    关于Redis未授权访问的问题

    这篇文章主要介绍了Redis未授权访问的问题,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-07-07
  • redis中使用bloomfilter的白名单功能解决缓存穿透问题

    redis中使用bloomfilter的白名单功能解决缓存穿透问题

    本文主要介绍了redis中使用bloomfilter的白名单功能解决缓存穿透问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-07-07
  • RedisTemplate集成+封装RedisUtil过程

    RedisTemplate集成+封装RedisUtil过程

    本文介绍了如何搭建一个多模块的Redis项目,包括项目搭建、配置和测试,通过使用父项目管理多个子模块,可以实现单点构建、统一版本管理和清晰的项目结构,文章还提供了在Spring Boot项目中集成RedisTemplate的示例,并解决了编码问题
    2024-12-12
  • Redis集群指定主从关系及动态增删节点方式

    Redis集群指定主从关系及动态增删节点方式

    这篇文章主要介绍了Redis集群指定主从关系及动态增删节点方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-01-01
  • redis实现存储帖子的点赞状态和数量的示例代码

    redis实现存储帖子的点赞状态和数量的示例代码

    使用Redis来实现点赞功能是一种高效的选择,因为Redis是一个内存数据库,适用于处理高并发的数据操作,这篇文章主要介绍了redis实现存储帖子的点赞状态和数量的示例代码,需要的朋友可以参考下
    2023-09-09
  • Redis中SDS简单动态字符串详解

    Redis中SDS简单动态字符串详解

    Redis中的SDS(Simple Dynamic String)是一种自动扩容的字符串实现方式,它可以提供高效的字符串操作,并且支持二进制安全。SDS的设计使得它可以在O(1)时间内实现字符串长度的获取和修改,同时也可以在O(N)的时间内进行字符串的拼接和截取。
    2023-04-04
  • redis在php中常用的语法【推荐】

    redis在php中常用的语法【推荐】

    string是redis最基本的类型,而且string类型是二进制安全的。这篇文章主要介绍了redis在php中常用的语法,需要的朋友可以参考下
    2018-08-08
  • 浅析Redis中红锁RedLock的实现原理

    浅析Redis中红锁RedLock的实现原理

    RedLock 是一种分布式锁的实现算法,由 Redis 的作者 Salvatore Sanfilippo(也称为 Antirez)提出,本文主要为大家详细介绍了红锁RedLock的实现原理,感兴趣的可以了解下
    2024-02-02

最新评论