SpringBoot使用本地缓存——Caffeine

SpringBoot使用本地缓存------Caffeine

缓存,想必大家都用过,将常用的数据存储在缓存上能在一定程度上提升数据存取的速度。这正是局部性原理的应用。之前用的缓存大多是分布式的,比如Redis。使用Redis作为缓存虽然是大多数系统的选择,但是需要另起服务,且会增加一定的耗时在数据传输上。对于一些小型应用,用Redis就有点大材小用了,此时便可以使用本地缓存。

本地缓存的技术选型:

  • 使用 ConcurrentHashMap 作为缓存

    这个是最简单的缓存使用,但是没有一定的内存淘汰策略,用作缓存来使用,功能比较单一。需要开发人员进行定制化开发。

  • Guava Cache

    Guava是Google团队开源的一款 Java 核心增强库,包含集合、并发原语、缓存、IO、反射等工具箱,性能和稳定性上都有保障,应用十分广泛,强烈推荐。其中 Guava Cache 支持很多特性,比如:支持最大容量限制、支持两种过期删除策略(插入时间和访问时间)、支持简单的统计功能、基于LRU算法实现

  • 本文将要介绍的Caffeine缓存

    Caffeine是一个高性能的Java本地缓存库,设计用于提供快速响应时间和高并发处理能力。它具有类似于Guava缓存的简单易用的API,同时也提供了许多额外的功能和性能优化。Caffeine支持缓存大小限制、缓存过期策略、异步加载数据等特性,可以帮助开发人员在应用程序中有效地管理和优化缓存。Caffeine还提供了可自定义的缓存策略和监听器,以帮助开发人员根据实际需求定制缓存行为。

  • 基于Ehcache实现本地缓存

    Ehcache是一个流行的Java开源缓存框架,用于在应用程序中管理缓存数据。它被广泛用于提高应用程序性能,减少数据库访问频率,和减少网络开销。同Caffeine和Guava Cache相比,Ehcache的功能更加丰富,扩展性更强。

Caffeine本地缓存

在项目开发中,为提升系统性能,减少 IO 开销,采用本地缓存是必不可少的。最常见的本地缓存是 Guava 和 Caffeine。Caffeine 是基于 Google Guava Cache 设计经验改进的结果,相较于 Guava 在性能和命中率上更具有效率,你可以认为其是 Guava Plus版本。

使用步骤

步骤 1: 添加依赖

xml 复制代码
<dependency>
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
    <version>3.1.5</version> <!-- 确保使用最新版本 -->
</dependency>

<!-- Spring Boot Cache 的依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>

步骤 2: 配置 Caffeine 缓存管理器

java 复制代码
package com.supermap.ai.agent.config;

import com.github.benmanes.caffeine.cache.Caffeine;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.CacheManager;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.TimeUnit;

/**
 * @Author: wangrongyi
 * @Date: 2024/7/8 14:02
 * @Description: 缓配配置类
 */
@Configuration
@Slf4j
public class CaffeineConfig {

    @Bean
    public CacheManager cacheManager() {
        CaffeineCacheManager cacheManager = new CaffeineCacheManager();
        //Caffeine配置
        Caffeine<Object, Object> caffeine = Caffeine.newBuilder()
                //最后一次写入后经过固定时间过期
                .expireAfterWrite(600, TimeUnit.SECONDS)
                //maximumSize=[long]: 缓存的最大条数
                .maximumSize(1000);
        cacheManager.setCaffeine(caffeine);
        log.info("缓存配置 CacheManager 初始化");
        return cacheManager;
    }
    
    @Bean
    public Cache<String, Object> caffeineCache() {
        log.info("缓存配置 Cache 初始化");
        return Caffeine.newBuilder()
                // 设置最后一次写入或访问后经过固定时间过期
                .expireAfterWrite(600, TimeUnit.SECONDS)
                // 初始的缓存空间大小
                .initialCapacity(1000)
                // 缓存的最大条数
                .maximumSize(1000)
                .build();
    }
}

Caffeine配置参数说明:

initialCapacity=[integer]: 初始的缓存空间大小

maximumSize=[long]: 缓存的最大条数

maximumWeight=[long]: 缓存的最大权重

expireAfterAccess=[duration]: 最后一次写入或访问后经过固定时间过期

expireAfterWrite=[duration]: 最后一次写入后经过固定时间过期

refreshAfterWrite=[duration]: 创建缓存或者最近一次更新缓存后经过固定的时间间隔,刷新缓存

weakKeys: 打开key的弱引用

weakValues:打开value的弱引用

softValues:打开value的软引用

recordStats:开发统计功能 注意:

expireAfterWrite和expireAfterAccess同事存在时,以expireAfterWrite为准。

maximumSize和maximumWeight不可以同时使用

weakValues和softValues不可以同时使用

注意: CacheManager 是使用注解形式时需要注入的Bean,Cache是在代码中显示使用时需要注入的,可以注入多个Bean,用名字区分,达到使用不通的缓存配置的效果。比如对过期时间有不通的要求,那么就可以注入两个过期时间不同的 CacheManager ,Bean的名字要做区分,在使用 @Cacheable 调用缓存时,便可以显示指定不同的 cacheManager。

步骤 3: 使用缓存

有两种使用缓存的方式:注解使用、代码显示使用

  • 注解使用

    java 复制代码
        // 获取缓存
        @GetMapping("/getCache")
        @Cacheable(value = "cache", key = "'key'")
        public String getCache() throws InterruptedException {
            Thread.sleep(3000);
            return "getCache";
        }
    
    	// 设置缓存
        @GetMapping("/setCache")
        @CachePut(value = "cache", key = "'key'")
        public String setCache() throws InterruptedException {
            Thread.sleep(3000);
            return "setCache";
        }
    
    	// 删除缓存
        @GetMapping("/delCache")
        @CacheEvict(value = "cache", key = "'key'")
        public String delCache() throws InterruptedException {
            Thread.sleep(3000);
            return "delCache";
        }

    注解使用说明:

    • @EnableCaching

      开启缓存功能,一般放在启动类上。

    • @CacheConfig

      当我们需要缓存的地方越来越多,你可以使用@CacheConfig(cacheNames = {"cacheName"})注解在 class 之上来统一指定value的值,这时可省略value,如果你在你的方法依旧写上了value,那么依然以方法的value值为准。

    • @Cacheable

      根据方法对其返回结果进行缓存,下次请求时,如果缓存存在,则直接读取缓存数据返回;如果缓存不存在,则执行方法,并把返回的结果存入缓存中。一般用在查询方法上。

      属性/方法名 解释
      value 缓存名,必填,它指定了你的缓存存放在哪块命名空间
      cacheNames 与 value 差不多,二选一即可
      key 可选属性,可以使用 SpEL 标签自定义缓存的key
      keyGenerator key的生成器。key/keyGenerator二选一使用
      cacheManager 指定缓存管理器
      cacheResolver 指定获取解析器
      condition 条件符合则缓存
      unless 条件符合则不缓存
      sync 是否使用异步模式,默认为false
    • @CachePut

      使用该注解标志的方法,每次都会执行,并将结果存入指定的缓存中。其他方法可以直接从响应的缓存中读取缓存数据。一般用在新增方法上。

      属性/方法名 解释
      value 缓存名,必填,它指定了你的缓存存放在哪块命名空间
      cacheNames 与 value 差不多,二选一即可
      key 可选属性,可以使用 SpEL 标签自定义缓存的key
      keyGenerator key的生成器。key/keyGenerator二选一使用
      cacheManager 指定缓存管理器
      cacheResolver 指定获取解析器
      condition 条件符合则缓存
      unless 条件符合则不缓存
    • @CacheEvict

      使用该注解标志的方法,会清空指定的缓存。一般用在更新或者删除方法上。

      属性/方法名 解释
      value 缓存名,必填,它指定了你的缓存存放在哪块命名空间
      cacheNames 与 value 差不多,二选一即可
      key 可选属性,可以使用 SpEL 标签自定义缓存的key
      keyGenerator key的生成器。key/keyGenerator二选一使用
      cacheManager 指定缓存管理器
      cacheResolver 指定获取解析器
      condition 条件符合则缓存
      allEntries 是否清空所有缓存,默认为 false。如果指定为 true,则方法调用后将立即清空所有的缓存
      beforeInvocation 是否在方法执行前就清空,默认为 false。如果指定为 true,则在方法执行前就会清空缓存
    • @Caching

      该注解可以实现同一个方法上同时使用多种注解。可从其源码看出:

  • 代码显示使用

    java 复制代码
        @Autowired
        private Cache<String, Object> cache;
    
        @GetMapping("/getCacheInfo")
        public void getCacheInfo() throws InterruptedException {
            String s = (String) cache.getIfPresent("key");
            if (s == null) {
                cache.put("key", "value");
                Thread.sleep(3000);
            }
            log.info("cache:{}", cache.getIfPresent("key"));
        }

    显示使用时需要注意处理并发读写,防止并发读写造成缓存不一致问题。

总结:使用Caffeine作为本地缓存能极大增加缓存数据的访问效率,提升读取性能。不过,本地缓存的使用受限于本地缓存的大小,所以面对缓存很大以及复杂的数据结构还是考虑用第三方的缓存服务,比如Redis。

关注不迷路。come on

相关推荐
coderWangbuer2 小时前
基于springboot的二手物品管理系统的设计与实现 (含源码+sql+视频导入教程)
spring boot·后端·sql
lzb_kkk2 小时前
【Redis】redis5种数据类型(哈希)
开发语言·redis·算法·缓存·哈希算法
AskHarries3 小时前
maven父子工程多模块如何管理统一的版本号?
java·spring boot·后端·maven
Flying_Fish_roe4 小时前
springcloud-GateWay
后端·spring·spring cloud
IT毕设梦工厂4 小时前
计算机毕业设计选题推荐-项目评审系统-Java/Python项目实战
java·spring boot·python·django·毕业设计·源码·课程设计
1316901704@qq.com4 小时前
Spring Boot项目自动生成OpenAPI3.0规范的接口描述文档yaml
java·spring boot·openapi
longlongqin4 小时前
redis的 stream数据类型实现 消息队列?
数据库·redis·缓存
奈李喔5 小时前
SpringBoot集成MyBatis-Plus
java·spring boot·mybatis
问道飞鱼5 小时前
springboot后端开发-常见注解及其用途
java·spring boot·后端