当前位置: 代码网 > it编程>数据库>Redis > 搭建Caffeine+Redis多级缓存机制

搭建Caffeine+Redis多级缓存机制

2025年07月28日 Redis 我要评论
本地缓存的简单实现方案有hashmap,cucurrenthashmap,成熟的本地缓存方案有guava 与 caffeine ,企业级应用推荐下面说下两者的区别0. 核心异同对比特性guava ca

本地缓存的简单实现方案有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缓存内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com