加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

最佳内存缓存框架Caffeine

发布时间:2020-12-15 06:52:49 所属栏目:Java 来源:网络整理
导读:Caffeine是一种高性能的缓存库,是基于Java 8的最佳(最优)缓存框架。 Cache(缓存) ,基于Google Guava,Caffeine提供一个内存缓存,大大改善了设计Guava's cache 和 ConcurrentLinkedHashMap 的体验。 1 LoadingCache Key , Graph graphs = Caffeine . n

Caffeine是一种高性能的缓存库,是基于Java 8的最佳(最优)缓存框架。

Cache(缓存),基于Google Guava,Caffeine提供一个内存缓存,大大改善了设计Guava's cache 和 ConcurrentLinkedHashMap 的体验。

1 LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
2     maximumSize(10_000)
3     expireAfterWrite(5, TimeUnitMINUTES4     refreshAfterWrite(1,1); padding: 0 5px">5     build(key -> createExpensiveGraph(key));

缓存类似于ConcurrentMap,但二者并不完全相同。最基本的区别是,ConcurrentMap保存添加到其中的所有元素,直到显式地删除它们。另一方面,缓存通常配置为自动删除条目,以限制其内存占用。在某些情况下,LoadingCache或AsyncLoadingCache可能很有用,因为它是自动缓存加载的。

Caffeine提供了灵活的结构来创建缓存,并且有以下特性:

  • 自动加载条目到缓存中,可选异步方式
  • 可以基于大小剔除
  • 可以设置过期时间,时间可以从上次访问或上次写入开始计算
  • 异步刷新
  • keys自动包装在弱引用中
  • values自动包装在弱引用或软引用中
  • 条目剔除通知
  • 缓存访问统计

?

1.? 加载/填充

Caffeine提供以下四种类型的加载策略:

1.1.? Manual

1 Cache> cache 2 (10,1); padding: 0 5px"> 3 4 (); 5 6 // Lookup an entry,or null if not found 7 Graph graph = cachegetIfPresent); 8 // Lookup and compute an entry if absent,or null if not computable 9 graph get)); 10 // Insert or update an entry 11 cacheput12 // Remove an entry 13 cacheinvalidate);

Cache接口可以显式地控制检索、更新和删除条目。?

1.2.? Loading?

5 6 7 Graph graph 8 // Lookup and compute entries that are absent 9 MapgetAll(keys);

LoadingCache通过关联一个CacheLoader来构建Cache

通过LoadingCache的getAll方法,可以批量查询?

1.3.? Asynchronous (Manual)?

AsyncCachebuildAsync// Lookup and asynchronously compute an entry if absent 7 CompletableFuture<Graph> graph ));

AsyncCache是另一种Cache,它基于Executor计算条目,并返回一个CompletableFuture。?

1.4.? Asynchronously Loading?

AsyncLoadingCache 4 // Either: Build with a synchronous computation that is wrapped as asynchronous 5 6 // Or: Build with a asynchronous computation that returns a future 7 ((key) -> createExpensiveGraphAsync 8 9 10 CompletableFuture11 // Lookup and asynchronously compute entries that are absent 12 CompletableFuture<Map>> graphs );

AsyncLoadingCache 是关联了 AsyncCacheLoader 的 AsyncCache

?

2.? 剔除

Caffeine提供三种剔除方式:基于大小、基于时间、基于引用

2.1.? Size-based

1 // Evict based on the number of entries in the cache 2 LoadingCache// Evict based on the number of vertices in the cache 7 LoadingCache 8 maximumWeight 9 weigher((Key key-> graphvertices().size()) 10 ));

如果缓存的条目数量不应该超过某个值,那么可以使用Caffeine.maximumSize(long)。如果超过这个值,则会剔除很久没有被访问过或者不经常使用的那个条目。

如果,不同的条目有不同的权重值的话,那么你可以用Caffeine.weigher(Weigher)来指定一个权重函数,并且使用Caffeine.maximumWeight(long)来设定最大的权重值。

简单的来说,要么限制缓存条目的数量,要么限制缓存条目的权重值,二者取其一。限制数量很好理解,限制权重的话首先你得提供一个函数来设定每个条目的权重值是多少,然后才能显示最大的权重是多少。

2.2.??Time-based

// Evict based on a fixed expiration policy expireAfterAccess 5 LoadingCache 6 // Evict based on a varying expiration policy 10 LoadingCache11 expireAfter(new Expiry>() { 12 public long expireAfterCreate(Key keylong currentTime13 // Use wall clock time,rather than nanotime,if from an external resource 14 long seconds = graphcreationDateplusHours(5) 15 minus(SystemcurrentTimeMillis(), MILLIS16 toEpochSecond17 return TimeUnitSECONDStoNanos(seconds18 } 19 expireAfterUpdate20 long currentDuration21 return currentDuration; 22 23 expireAfterRead24 25 26 27 }) 28 ));
  • expireAfterAccess(long,TimeUnit): 最后一次被访问(读或者写)后多久失效
  • expireAfterWrite(long,TimeUnit): 最后一次被创建或修改后多久失效
  • expireAfter(Expiry): 创建后多久失效?

建议,主动维护缓存中条目,而不是等到访问的时候发现缓存条目已经失效了才去重新加载。意思就是,提前加载,定期维护。

可以在构建的时候Caffeine.scheduler(Scheduler)来指定调度线程

2.3.? Reference-based?

// Evict when neither the key nor value are strongly reachable weakKeysweakValues 6 7 // Evict when the garbage collector needs to free memory 8 LoadingCachesoftValues));

Caffeine.weakKeys() 使用弱引用存储key。如果没有强引用这个key,则允许垃圾回收器回收该条目。注意,这是使用==判断key的。

Caffeine.weakValues() 使用弱引用存储value。如果没有强引用这个value,则允许垃圾回收器回收该条目。注意,这是使用==判断key的。

Caffeine.softValues() 使用软引用存储value。

?

3.? 删除?

术语:

  • eviction? 指受策略影响而被删除
  • invalidation? 值被调用者手动删除
  • removal? 值因eviction或invalidation而发生的一种行为? ?

3.1.? 明确地删除

1 // individual key 2 cache3 // bulk keys 4 cacheinvalidateAll5 // all keys 6 cache()

3.2.? 监听器

1 CacheremovalListener-> 3 Systemoutprintf("Key %s was removed (%s)%n" cause)) ();

?

4.? 刷新

));?

通过LoadingCache.refresh(K)进行异步刷新,通过覆盖CacheLoader.reload(K,V)可以自定义刷新逻辑

?

5.? 统计

recordStats();?

使用Caffeine.recordStats(),你可以打开统计功能。Cache.stats()方法会返回一个CacheStats对象,该对象提供以下统计信息:

  • hitRate(): 命中率
  • evictionCount(): 被剔除的条目数量
  • averageLoadPenalty(): 加载新值所花费的平均时间

?

6.? 示例

终于要说到重点了

一般来讲,用Redis作为一级话缓存,Caffeine作为二级缓存

6.1.? 示例一:单独使用

pom.xml

<groupId>com.github.ben-manes.caffeine</groupId> 2 <artifactId>caffeine</artifactId> 3 <version>2.8.0</version> 4 </dependency>

config

 1 package com.cjsexampleconfig;
 2 
 3 import com.alibaba.fastjson.JSON 4 com.cjs.example.model.Student 5 com.github.benmanes.caffeine.cache.CacheLoader 6 com.github.benmanes.caffeine.cache.Caffeine 7 com.github.benmanes.caffeine.cache.LoadingCache 8 com.github.benmanes.caffeine.cache.Scheduler 9 lombok.extern.slf4j.Slf4j10 org.checkerframework.checker.nullness.qual.NonNull11 org.checkerframework.checker.nullness.qual.Nullable12 org.springframework.beans.factory.annotation.Autowired13 org.springframework.context.annotation.Bean14 org.springframework.context.annotation.Configuration15 org.springframework.data.redis.core.HashOperations16 org.springframework.data.redis.core.StringRedisTemplate17 org.springframework.util.StringUtils18 
19 java.util.concurrent.TimeUnit20 
21 /**
22  * @author ChengJianSheng
23  * @date 2019-09-15
24  */
25 @Slf4j
26 @Configuration
27 public class CacheConfig {
28 
29     @Autowired
30     private StringRedisTemplate stringRedisTemplate31 
32     @Bean("studentCache")
33     public LoadingCache<IntegerStudent> studentCache() 34           return CaffeinenewBuilder()
35                   maximumSize(10).recordStats36                   expireAfterWrite1TimeUnitHOURS37 //                  .scheduler(Scheduler.systemScheduler())  // 需要自定义调度器,用定时任务去主动提前刷新
38                   build(new CacheLoader>() 39                       @Nullable
40                       @Override
41                       Student load(@NonNull Integer keythrows Exception 42                           loginfo"从缓存中加载...key={}");
43                           HashOperationsStringhashOperations = opsForHash();
44                           String value hashOperationsget"STU_HS"valueOf());
45                           if StringUtilsisEmptyvalue)) 46                               return null47                           }
48                           JSONparSEObjectclass49                       50                   });
51     52 
53 
54 }

service?

serviceorg.springframework.stereotype.Service 6 javax.annotation.Resourcejava.util.Comparatorjava.util.Listjava.util.Mapjava.util.stream.Collectors12 13 14 15 16 17 @Service 18 StudentService 19 20 @Resourcename = 21 22 23 getByIdid) 24 25 26 List> getAllidList28 MapstudentMap 29 studentMapvalues().parallelStreamsortedComparatorcomparingStudent::getId)).collectCollectorstoList()); 30 32 Double hitRate33 stats34 35 36 * 不直接写到本地缓存,而是先写到Redis,然后从Redis中读到本地 38 */ 39 }

补充一点:你都用本地缓存了,必定已经用了一级缓存了。一级缓存无法达到预期的性能,才会选择用本地缓存。

controller?

controllercom.cjs.example.service.StudentServiceorg.springframework.web.bind.annotation.GetMappingorg.springframework.web.bind.annotation.PathVariableorg.springframework.web.bind.annotation.RequestMappingorg.springframework.web.bind.annotation.RestController10 java.util.Arrays13 17 18 @RestController 19 @RequestMapping"/student"20 StudentController 21 22 StudentService studentService24 25 @GetMapping"/info/{studentId}"26 @PathVariable"studentId") studentId27 28 29 30 "/getAll"31 32 ArraysasList10110210310410533 34 35 "/hitRate"36 37 38 }

?

6.2.? 示例二:和SpringBoot一起用

SpringBoot支持Caffeine,可以简化一些步骤,但同时也有诸多限制

application.yml

1 spring: 2 redis: 3 host: 127.0.0.1 4 password: 123456 5 port: 6379 6 cache: 7 type: caffeine 8 cache-names: teacher 9 caffeine: 10 spec: maximumSize=500,expireAfterAccess=600s

service

com.cjs.example.model.Teacherorg.springframework.cache.annotation.Cacheable19 TeacherService 21 22 23 24 @CacheablecacheNames "teacher"key "#teacherId"25 Teacher teacherId26 "从缓存中读取...Key={}"27 "TEA_HS"30 31 Teacher35 }

用注解方便是方便,但是不好控制,还是自定义的好

?

7.? 工程结构

完整的pom.xml

<?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> 5 <parent> 6 <groupId>org.springframework.boot 7 <artifactId>spring-boot-starter-parent 8 <version>2.1.8.RELEASE 9 <relativePath/> <!-- lookup parent from repository --> 10 </parent> 11 <groupId>com.cjs.example12 <artifactId>cjs-caffeine-example13 <version>0.0.1-SNAPSHOT14 <name>cjs-caffeine-example</name> 15 16 <properties> 17 <java.version>1.8</java.version> 18 </properties> 19 20 <dependencies> 21 <dependency> 22 23 <artifactId>spring-boot-starter-cache24 </dependency> 25 26 27 <artifactId>spring-boot-starter-data-redis28 29 30 31 <artifactId>spring-boot-starter-web32 33 34 35 36 37 38 39 40 41 <groupId>org.projectlombok42 <artifactId>lombok43 <optional>true</optional> 44 45 46 <groupId>com.alibaba47 <artifactId>fastjson48 <version>1.2.6049 50 </dependencies> 51 52 <build> 53 <plugins> 54 <plugin> 55 56 <artifactId>spring-boot-maven-plugin57 </plugin> 58 </plugins> 59 </build> 60 61 </project>

https://github.com/chengjiansheng/cjs-caffeine-example

?

8.? 文档

https://github.com/ben-manes/caffeine/wiki?

https://github.com/ben-manes/caffeine

https://www.itcodemonkey.com/article/9498.html?

?

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读