博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

[本地缓存]guava cache 在项目中的使用

Posted on 2021-03-28 23:23  海绵谷  阅读(365)  评论(0编辑  收藏  举报

引申

1.Guava Cache和Ehcache一样也是本地缓存,虽然都是本地缓存,但是在细分领域中也还是有不同的应用场景,Guava是Google提供的一套Java工具包,而GuavaCache作为Guava的Cache部分而提供了一套非常完善的本地缓存机制。在Guava之前,JDK的ConcurrentHashMap因为能友好的支持并发而被经常用作本地缓存,但它毕竟还是个Map,不具备缓存的一些特性,比如缓存过期,缓存数据的加载/刷新等。

guava cache 适用场景

1.愿意消耗一些本地内存空间来提升速度。
一些数据对一致性要求不高,就可以不用放到Redis等集中缓存中,这样频繁读取还会增加网络开销,同时也需要考虑集中缓存宕机的情况。我们在使用本地内存做缓存的时候,也需要考虑缓存的数据总量不能超出服务器内存,这样就应该做一些数据淘汰机制来确保
2.更新锁定
当请求查询某一个key的时候,如果不存在则从源中读取,然后再回填到本地缓存中,这时如果并发量非常大,可能会有多个请求同时从源中读取数据,然后再回填到本地缓存,造成多次执行的情况。Guava Cache可以在CacheLoader的load方法中加以控制,对同一个key,只让一个请求去源中读取数据,而其他请求阻塞等待结果。

guava cache 的实现 CacheLoader

  1. Guava Cache是一个全内存的本地缓存实现,它提供了线程安全的实现机制。CacheLoader,Callable callback
  2. CacheLoader创建方式在构建Cache对象的时候定义一个CacheLoader来获取数据,在缓存不存在的时候能够自动加载数据到缓存中。这种创建方式适用的场景是:在创建的时候采用指定的加载缓存的方式,经常用作从数据库中获取和缓存数据。
    3.Guava的cache数据删除的方式有两种,分别是主动删除和被动删除。
  3. 被动删除
    基于数据大小的删除;按照缓存的大小来删除,如果缓存容量即将到达指定的大小时,就会把不常用的键值对从cache中移除。使用CacheBuilder.maximumSize(size)方法进行设置。说明:❑ size指的是记录数,不是容量。❑ 并不是超过缓存容量才会删除数据,而是接近的时候开始。
    基于过期时间删除;在Guava Cache中提供了两个方法可以基于过期时间删除❑ expireAfterAccess(long, TimeUnit):某个key最后一次访问后,再隔多长时间后删除。❑ expireAfterWrite(long, TimeUnit):某个key被创建后,再隔多长时间后删除
    基于引用的删除;这种方式主要是基于Java的垃圾回收机制,判断缓存的数据引用的关系,如果没有被引用,则Guava Cache会将该数据删除
  4. 主动删除

/**
 * @author ngLee
 * @version 1.0
 * @Desc guava cache 本地缓存的实现 -- CacheLoader
 * @date 2021/3/28 21:56
 */
public class TLocalCache {
    public static  LoadingCache<String,String> cache = CacheBuilder.newBuilder()
            //设置并发级别,同时可以缓存的线程数
            .concurrencyLevel(10)
            //被动删除:某个key被创建后,再隔8秒后删除
            .expireAfterWrite(8, TimeUnit.SECONDS)
            //被动删除:个key最后一次访问后,再隔多2秒后删除。
            .expireAfterAccess(2,TimeUnit.SECONDS)
            //设置缓存的初始容量
            .initialCapacity(10)
            //被动删除:按照缓存的大小来删除如果缓存容量即将到达指定的大小时,就会把不常用的键值对从cache中移除
            .maximumSize(14)
            .recordStats()
            .removalListener(new RemovalListener<Object, Object>() {
                @Override
                public void onRemoval(RemovalNotification<Object, Object> removalNotification) {
                    System.out.println("过期移除:key="+removalNotification.getKey()+"--value="+
                            removalNotification.getValue()+"--移除原因="+removalNotification.getCause());
                }
            }).build(new CacheLoader<String, String>() {
                //build方法可以指定CacheLoader,在缓存不存在通过CacheLoader的实现自动加载
                @Override
                public String load(String key) throws Exception {
                    System.out.println("缓存不存在,自动加载:"+ key);
                    String vu = key +"对应的值";
                    return vu;
                }
            });
    
    public static void main(String[] args) {
        List<String> stringList = Lists.newArrayList("a","b","c","d","e","f","g","h"
        ,"i","l","m","n","o","p","x","y","z","q","w","r","ad","ac","bn","dd","ds","a","b","c","d","e");
        stringList.forEach(x->{
            try {
                String strV = cache.get(x);
                System.out.println("值:"+strV);
                TimeUnit.SECONDS.sleep(1);
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        System.out.println("cache status:");
        System.out.println(cache.stats().toString());
    }
}

guava cache 的实现 Callable

Callable方式这个方法返回缓存中相应的值,如果未获取到缓存值则调用Callable方法。这个方法简便地实现了“如果有缓存则返回,否则读取、缓存、然后返回”的模式。适用的场景是:这种方式比较灵活,可以在获取缓存的指定Callable对象,在缓存中获取不到数据的时候,可以动态决定采用哪种方式加载数据到缓存

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.RemovalListener;
import com.google.common.cache.RemovalNotification;

import java.util.concurrent.Callable;

/**
 * @author ngLee
 * @version 1.0
 * @Desc
 * @date 2021/3/28 23:00
 */
public class TCallableCache {
    
    public static Cache<String,String> cache = CacheBuilder.newBuilder().maximumSize(10)
            .removalListener(new RemovalListener<Object, Object>() {
                @Override
                public void onRemoval(RemovalNotification<Object, Object> removalNotification) {
                    System.out.println("过期移除:key="+removalNotification.getKey()+"--value="+
                            removalNotification.getValue()+"--移除原因="+removalNotification.getCause());
                }
            })
            .build();
    public static void main(String[] args) {
        try {
            for (int i = 0; i <20 ; i++) {
                int vals = i;
                String value = cache.get(i + "", new Callable() {
                    @Override
                    public Object call() throws Exception {
                        //未根据key 找到缓存,就设置缓存
                        String va = "设置缓存值"+ vals;
                        return va;
                    }
                });
                System.out.println("key="+i+"----对应的value="+value);
                System.out.println("=============");
                //主动删除缓存
                cache.invalidate("18");
                String tet = cache.getIfPresent("18");
                System.out.println("18=="+tet);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        
    }
}