最佳内存缓存框架Caffeine
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提供了灵活的结构来创建缓存,并且有以下特性:
? 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 ));
建议,主动维护缓存中条目,而不是等到访问的时候发现缓存条目已经失效了才去重新加载。意思就是,提前加载,定期维护。 可以在构建的时候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.? 删除? 术语:
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对象,该对象提供以下统计信息:
? 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? ? (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |