当前位置: 首页 > article >正文

本地缓存~

前言

Caffeine是使用Java8对Guava缓存的重写版本,在Spring Boot 2.0中取而代之,基于LRU算法实现,支持多种缓存过期策略。

以下摘抄于https://github.com/ben-manes/caffeine/wiki/Benchmarks-zh-CN

基准测试通过使用Java microbenchmark harness 来提供准确的分析结果。这些缓存将被如下配置,

  • Caffeine 和 ConcurrentLinkedHashMap根据CPU的数量调整其内部大小。
  • Guava 并发度被配置为 64 (默认情况下为 4 来减少内存开销)。请注意Guava将会#2063 解决性能问题,但已经被积压多年(提升25倍以上!)。
  • Ehcache v2 内部被硬编码为100段, 而 v3 版本没有进行分段
  • Infinispan “old” 是一个类似Guava的缓存,并且并发度被配置为64
  • Infinispan "new"是使用无锁deque(默认版本为 v7.2+)重写的

本地缓存对比

特性GuavaCaffeine
自动加载实体到缓存中✔️✔️
自动刷新✔️✔️
过期或被删除的机制✔️✔️
自动回收✔️✔️
统计累计访问缓存✔️✔️
异步✔️
写入外界资源✔️
算法S-LRU 分段的最近最少未使用算法W-TinyLFU 高命中率、低内存占用
持久化

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用指南

pom引入

<dependency>
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
</dependency>

配置

  • maximumSize:

  • refreshAfterWrite

  • expireAfterWrite

  • expireAfterAccess

  • maximumWeight

preview

方法

  • Get

    如果Guava 的load()返回时null,那么get()会抛出异常ExecutionException,而Caffeine就不会,会返回null

  • getAll

    获取多个key的value,返回一个map

  • load

    刷新

  • getUnchecked(): guava才有,Caffeine没有,所以Caffeine需要做好控判断

  • Caffeine.newBuilder().build()创建LoadingCache

  • invalidate

  • cleanUp

缓存填充方法

  • 手动填充

    # 0. 构建LoadingCache
    Cache<String, DataObject> cache = Caffeine.newBuilder()
      .expireAfterWrite(1, TimeUnit.MINUTES)
      .maximumSize(280)
      .build();
      
    String key = "test";
    # 1. 缓存中不存指定的值,则方法将返回 null
    DataObject dataObject = cache.getIfPresent(key); 
    
    cache.put(key, "20220510 Demo演示");
    # 2. return "20220510 Demo演示"
    dataObject = cache.getIfPresent(key);
    
    # 3. 如果key的值不存在,则返回“空时mock一个值”
    dataObject = cache
      .get(key, k -> "空时mock一个值"); 
    
    # 4. 手动失效
    cache.invalidate(key);
    # 返回null
    dataObject = cache.getIfPresent(key);
    
  • 同步加载

    LoadingCache<String, DataObject> cache = Caffeine.newBuilder()
      .maximumSize(100)
      .expireAfterWrite(1, TimeUnit.MINUTES)
      .build(k -> "Data for " + k);
    
  • 异步加载

    AsyncLoadingCache<String, DataObject> cache = Caffeine.newBuilder()
      .maximumSize(100)
      .expireAfterWrite(1, TimeUnit.MINUTES)
      .buildAsync(k -> "Data for " + k);
    
    String key = "test-async";
     
    cache.get(key).thenAccept(dataObject -> {
        assertNotNull(dataObject);
        assertEquals("Data for " + key, dataObject.getData());
    });
     
    cache.getAll(Arrays.asList("test-async", "test-async2", "test-async3"))
      .thenAccept(dataObjectMap -> assertEquals(3, 3));
    

注解的实现方式

Springboot 配置

spring:
# 配置缓存,初始缓存容量为10,最大容量为200,过期时间(这里配置写入后过期时间为3秒)
  cache:
    type: caffeine
    caffeine:
      spec: initialCapacity=10,maximumSize=200,expireAfterWrite=3s

@Cacheable

 // key 是指传入时的参数
 @Cacheable(value="users", key="#id")
 public Integer find(Integer id) {
     return id;
 }
// 表示第一个参数
@Cacheable(value="users", key="#p0")
 public Long find(Long id) {
     return id;
 }
// 表示User中的id值
 @Cacheable(value="users", key="#user.id")
 public User find(User user) {
      return user;
 }
 // 表示第一个参数里的id属性值
 @Cacheable(value="users", key="#p0.id")
 public User find(User user) {
     return user;
 }

// 表示第一个参数里的id属性值
 @Cacheable(value="users", key="#root.getXXX(#user.id)")
 public User find(User user) {
     return user;
 }

public String getXXX(String id){
  // 获取上文问的traceid 方法
  // ...
  return 上下文的traceId;
}
 // 根据条件判断是否缓存
 @Cacheable(value="users", key="#user.id", condition="#user.id%2==0"/*,unless="#result==null"*/)
 public User find(User user) {
    return user;
 }

注意事项

  • SpringBoot启动类开启@EnableCaching

  • A class 调用 B class 的@Cacheable的方法,一定是public方法

  • A class 调用 B class 的@Cacheable的方法,B 一定是@Component、@Service等

  • B class 的@Cacheable的方法xx1调用@Cacheable的方法xx2,B缓存不起作用。解决方案:Spring上下文手动获取Bean实例调用xx2,或者AopContext.currentProxy())调用xx2,不过ApoContenxt这个,需要在启动累开启@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)

  • 开启@EnableCache,注意区分Redis的CacheManager,在注解通过指定@Cacheable(cacheManager=“caffeineCacheManager”,…)


http://www.kler.cn/a/529568.html

相关文章:

  • 【UE】 APlayerState
  • 仿真设计|基于51单片机的贪吃蛇游戏
  • Unity Shader Graph 2D - 角色身体电流覆盖效果
  • npm 和 pip 安装中常见问题总结
  • AI开发学习之——PyTorch框架
  • 【ArcGIS遇上Python】批量提取多波段影像至单个波段
  • 功防世界 Web_php_include
  • 理解红黑树
  • word2vec 实战应用介绍
  • Kotlin 协程 与 Java 虚拟线程对比测试(娱乐性质,请勿严谨看待本次测试)
  • VSCode设置内容字体大小
  • DeepSeek R1本地化部署 Ollama + Chatbox 打造最强 AI 工具
  • 【 软件测试项目实战】 以淘宝网购物车管理功能为例
  • 扩散模型(二)
  • kamailio-ACC、ACC_JSON 和 ACC_RADIUS 的区别
  • android java系统弹窗的基础模板
  • Clion开发STM32时使用stlink下载程序与Debug调试
  • MySQL基础学习总结(二)_select round(3
  • 【Rust自学】19.2. 高级trait:关联类型、默认泛型参数和运算符重载、完全限定语法、supertrait和newtype
  • MacBook Pro(M1芯片)Qt环境配置
  • 【工欲善其事】利用 DeepSeek 实现复杂 Git 操作:从原项目剥离出子版本树并同步到新的代码库中
  • 9 点结构模块(point.rs)
  • 面经——C语言——指针大小,内存分配,野指针,大小端
  • 【LeetCode: 598. 区间加法 II + 脑筋急转弯】
  • 我的Go+语言初体验——环境搭建并用命令行和 VScode 输出 “Hello World”_gop windows helloworld
  • 一些常用的HTML结构