本地缓存LoadingCache

334 阅读2分钟

“携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情

背景

为什么要使用本地缓存

  • 本地缓存基于本地环境的内存,访问速度非常快,对于一些变更频率低、实时性要求低的数据,可以放在本地缓存中,提升访问速度
  • 使用本地缓存能够减少和Redis类的远程缓存间的数据交互,减少网络I/O开销,降低这一过程中在网络通信上的耗时

基于Guava Cache实现本地缓存

LoadingCache是GuavaCache构建缓存实体的方法,是一个支持多线程并发读写、高性能、通用的本地缓存。它是线程安全的!

private LoadingCache<String,Map<String,String>> cache = CacheBuilder.newBuilder()
                //设置缓存容器的初始容量为2
                .initialCapacity(2)
                //缓冲池大小
                .maximumSize(2)
                //设置时间 对象没有写访问则对象从内存中删除
                .expireAfterWrite(1, TimUnit.HOURS)
                //build方法中可以指定CacheLoader,在缓存不存在时通过CacheLoader的实现自动加在缓存
                .build(new CacheLoader<String, Map<String,String>>(){
                    @Override
                    public Map<String, String> load(String key) throws Exception{
                        //具体的实现逻辑
                        ......
                    }
                );

CacheBuilder方法

  • initialCapacity():设置缓存容器的初始容量
  • maximumSize():配置缓存数量上限,超过设定值就会根据LRU最近最少使用算法来移除对象
  • expireAfterWrite():设置时间,对象没有被写访问则对象从内存中删除
  • expireAfterAccess():设置时间,对象没有被读/写访问则对象从内存中删除 --使用较少
  • refreshAfterWrite():定时刷新,可以为缓存增加自动定时刷新功能

expireAfterWrite为了避免缓存雪崩,guava会限制只有一个加载操作时进行加锁,其他请求必须阻塞等待这个加载操作完成。而且,在加载完成之后,其他请求的线程会逐一获得锁,去判断是否已被加载完成,每个线程必须轮流的走一个获得锁,获得值,释放锁”的过程,这样性能会有一些损耗。

refreshAfterWrite当缓存项上一次更新操作之后的多久会被刷新。在refresh的过程中,guava会限制只有一个加载操作时进行加锁,而其他查询先返回旧值,这样能有效减少等待和锁争用,所以refreshAfterWrite会比expireAfterWrite性能好。

V get(K k):内部调用getOrLoad(K key)方法,缓存中有对应的值则返回,没有则使用CacheLoader load方法