- 背景
- 如何创建?
- pom引入依赖
- CacheLoader方式
- 何时使用?
- 案例
- CacheBuilder 的详细方法
- Callable方式
- 何时使用?
- 案例
- 如何删除?
- 被动
- 基于数据大小的删除
- 基于过期时间的删除
- 基于引用的删除
- 主动
- 删除单条
- 批量删除
- 清空缓存
- 总结
数据库扛不住了可以使用Redis来分担读请求,在大访问量的系统中Redis集中式缓存方案,会成为大型系统的瓶颈。有什么方案解决呢?
可以在增加一层缓存层,即JVM进程的内存中进行本地缓存,分摊Redis的压力。Guava cache的设计来源于CurrentHashMap,可以按照多种策略来清理存储在其中的缓存值且保持很高的并发读写性能。以下场景适合于做本地缓存:
- 内存占用较小
- 数据极少变化
- 需要访问整个集合
- 数据实时性要求不高
创建缓存方式有两种。
pom引入依赖在项目中添加如下的依赖即可
CacheLoader方式 何时使用?com.google.guava guava 30.0-jre
是否存在一个默认函数来加载或计算与键关联的值?如果是这样,则应使用CacheLoader。
案例package com.linfanchen.springboot.lab.guava; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.collect.Lists; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutionException; import static com.alibaba.druid.sql.ast.SQLPartitionValue.Operator.List; @RunWith(SpringRunner.class) @SpringBootTest public class CacheTest { LoadingCacheCacheBuilder 的详细方法cache = CacheBuilder.newBuilder() .build(new CacheLoader () { @Override public String load(final String key) { return getCar(key); } @Override public Map loadAll(final Iterable extends String> keys) throws Exception { // 此包完整路径位于: com.google.common.collect.Lists ArrayList keysList = Lists.newArrayList(keys); return getCars(keysList); } }); private static String getCar(String key) { return "Lexus"; } private static Map getCars(List keys) { Map map = new HashMap<>(); map.put("bmw", "BMW 530Li"); map.put("benz", "E300L"); map.put("audi", "Audi A6L"); return map; } @Test public void firstTest() { java.util.List keys = new ArrayList<>(); keys.add("bmw"); keys.add("benz"); // 从本地缓存读取数据 try { System.out.println(cache.getAll(keys)); // 输出 {bmw=BMW 530Li, benz=E300L} } catch (ExecutionException e1) { e1.printStackTrace(); } } }
LoadingCacheCallable方式 何时使用?
希望使用原子的“ get-if-absent-compute”语义,则应将Callable传递给get调用。
案例package com.linfanchen.springboot.lab.guava; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.collect.Lists; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; @RunWith(SpringRunner.class) @SpringBootTest public class CacheCallableTest { LoadingCache如何删除? 被动 基于数据大小的删除cache = CacheBuilder.newBuilder() .build(new CacheLoader () { @Override public String load(final String key) { return getCar(key); } }); private static String getCar(String key) { return "Lexus" + key; } private static Map getCars(List keys) { Map map = new HashMap<>(); map.put("bmw", "BMW 530Li"); map.put("benz", "E300L"); map.put("audi", "Audi A6L"); return map; } @Test public void secondTest() { try { // 从本地缓存读取数据 String value = cache.get("es300h", new Callable () { @Override public String call() throws Exception { System.out.println("Now invoking callable code..."); return "callable code..."; } }); System.out.println(value); // 输出: callable code... } catch (ExecutionException e1) { e1.printStackTrace(); } } }
当数据的个数多于 maximumSize所设置的值时候,会根据 LRU+FIFO 策略进行淘汰。
基于过期时间的删除隔多长时间后没有被访问过的key被删除,时间标准参照 expireAfterAccess所设置的时间。
基于引用的删除可以通过weakKeys和weakValues方法指定Cache只保存对缓存记录key和value的弱引用。这样当没有其他强引用指向key和value时,key和value对象就会被垃圾回收器回收。
主动 删除单条cache.invalidate("bmw");
批量删除cache.invalidateAll(Arrays.asList("bmw","benz"));
清空缓存cache.invalidateAll();
总结guava cache 的异步 reload 策略可以有效实现容错、节约调用耗时的目的,但有一个致命的缺陷:主线程返回的数据有可能是已过期的。
通常,我们对于缓存中数据的实际失效时间并不敏感,在这样的情况下,即使 guava cache 返回了已失效数据,也并不会造成任何业务问题,而由此带来的性能提升与容错的好处是显而易见的。
参考文档:
官方网址
官方使用案例
Google Guava Cache高效本地缓存
Guava Cache简介、应用场景分析、代码实现以及核心的原理
guava cache详细介绍