简介
缓存的种类有很多,需要根据不同的应用场景来选择不同的缓存方案,一种是分布式缓存如redis、memcached,另外一种是本地(进程内)缓存如:ehcache、GuavaCache、Caffeine。本文记录Guava Cache的一些使用。
Guava Cache 是Google的开源工具包 Guava 中提供的一个本地缓存工具,它支持高并发,并且是线程安全的,借鉴了ConcurrentHashMap的数据结构(一个支持LRU的ConcurrentHashMap,并提供了基于容量,时间和引用的缓存回收方式),在多线程情况下可以安全的访问或者更新缓存。
应用场景
本地缓存的数据读写都在一个进程内,相比 redis 等分布式缓存,不需要网络传输的过程,访问速度很快,同时也受到 JVM 内存的制约,无法在数据量较多的场景下使用。
基于以上特点,guava cache 的主要应用场景为以下几种:
- 对于访问速度有较大要求
- 存储的数据不经常变化
- 数据量不大,占用内存较小
- 能够容忍数据不是实时的
使用
GuavaCache使用时主要分二种模式:LoadingCache
、CallableCache
。
核心区别在于:LoadingCache
创建时需要有合理的默认方法来加载或计算与键关联的值(可以理解为当从缓存中get某个key对应的value时,如果value不存在,则调用load方法将结果放到缓存并返回),CallableCache
创建时则没有这个限制,使用上更加简便灵活。
前置准备
引入jar包:1
2
3
4
5<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>30.1.1-jre</version>
</dependency>
LoadingCache使用
LoadingCache
的典型使用场景:查询某个数据时,先从本地缓存中获取,如果本地缓存没有,再从数据库查,并将数据库查询的结果放入本地缓存。
1 |
|
上面这段代码中,本地缓存的value没有直接使用 Employee 对象,而是使用 Optional 对象进行了封装,原因是 LoadingCache 是不支持缓存null
值的,如果load()
方法返回null
,则在get(key)
的时候会抛出异常:CacheLoader returned null for key
。但实际情况中 null 的情况有可能是存在的,推荐做法是使用 Optional 对象来封装结果,这样缓存的 value 就永远不可能为空,而真正的业务数据是否为空则可通过 Optional.ifPresent()
来判断。
CallableCache使用
CallableCache
的一个使用场景举例:某个定时任务每分钟会进行一次任务扫描,如果任务不为空则进行任务处理,如果任务执行异常则发送短信或企业微信提醒。因为扫描间隔比较短,为了防止一条异常任务执行失败而导致短信提醒得太过频繁,可以用一个本地缓存来控制。如下是示例:
1 | public class TaskService { |
显式清除缓存
Guava Cache 提供了几种显式清除缓存条目的方法,允许你手动移除缓存中的某个或某些条目。
移除单个条目
使用invalidate(key)
方法可以移除缓存中的特定键对应的条目。1
cache.invalidate(key);
移除多个条目
使用invalidateAll(keys)
方法可以移除缓存中所有在给定集合中的键对应的条目。1
cache.invalidateAll(keys);
移除所有条目
使用invalidateAll()
方法可以移除缓存中的所有条目。1
cache.invalidateAll();
注册移除监听器
可以在构建缓存时注册一个移除监听器(RemovalListener),它会在每次条目被移除时调用。1
2
3
4
5
6
7
8
9
Cache<KeyType, ValueType> cache = CacheBuilder.newBuilder()
.removalListener(new RemovalListener<KeyType, ValueType>() {
public void onRemoval(RemovalNotification<KeyType, ValueType> notification) {
// 处理移除事件
}
})
.build();