Guava Cache

摘要: 学习Google内部使用的工具包Guava,在Java项目中轻松地增加缓存,提高程序获取数据的效率; 业务实现上需要用到本地缓存来解决一些数据量相对较小但是频繁访问的数据

Guava Cache适用场景:
你愿意消耗一部分内存来提升速度;
你已经预料某些值会被多次调用;
缓存数据不会超过内存总量;

Guava Cache是一个全内存的本地缓存实现,它提供了线程安全的实现机制。整体上来说Guava cache 是本地缓存的不二之选,简单易用,性能好

Guava Cache与ConcurrentMap很相似,但也不完全一样。最基本的区别是ConcurrentMap会一直保存所添加的元素,直到显式的移除;Guava Cache为了限制内存的占用,通常都是设定为自动回收元素。在某些场景下,尽管LoadingCahe不回收元素,但是它还是很有用的,因为它会自动加载缓存。

依赖:
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>23.0</version>
</dependency>


import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;

// 获取历史版本的频道商品列表
private LoadingCache<String, List<Bean>> beanCache;

@PostConstruct
public void init() {
    beanCache = CacheBuilder.newBuilder()
                //过期时间5分钟(时间段内没有更新就会被回收)
                .expireAfterWrite(5 * 60, TimeUnit.SECONDS).
                //缓存数量为5000个
                .maximumSize(5000)
                .build(new CacheLoader<String, List<Bean>>() {
                    @Override
                    public List<Bean> load(String key) throws Exception {
                        return this.getBeanInfo(key);
                    }
                });
}

 

刷新机制:包括refresh和expire刷新机制

expireAfterAccess: 当缓存项在指定的时间段内没有被读或写就会被回收。

expireAfterWrite:当缓存项在指定的时间段内没有更新就会被回收。

refreshAfterWrite:当缓存项上一次更新操作之后的多久会被刷新。

考虑到时效性,我们可以使用expireAfterWrite,使每次更新之后的指定时间让缓存失效,然后重新加载缓存。guava cache会严格限制只有1个加载操作,这样会很好地防止缓存失效的瞬间大量请求穿透到后端引起雪崩效应。
     然而,通过分析源码,guava cache在限制只有1个加载操作时进行加锁,其他请求必须阻塞等待这个加载操作完成;而且,在加载完成之后,其他请求的线程会逐一获得锁,去判断是否已被加载完成,每个线程必须轮流地走一个“”获得锁,获得值,释放锁“”的过程,这样性能会有一些损耗。这里由于我们计划本地缓存1秒,所以频繁的过期和加载,锁等待等过程会让性能有较大的损耗。

     refreshAfterWrite的特点是: 在refresh的过程中,严格限制只有1个重新加载操作,而其他查询先返回旧值,这样有效地可以减少等待和锁争用,所以refreshAfterWrite会比expireAfterWrite性能好。但是它也有一个缺点,因为到达指定时间后,它不能严格保证所有的查询都获取到新值。guava cache并没使用额外的线程去做定时清理和加载的功能,而是依赖于查询请求。在查询的时候去比对上次更新的时间,如超过指定时间则进行加载或刷新。所以,如果使用refreshAfterWrite,在吞吐量很低的情况下,如很长一段时间内没有查询之后,发生的查询有可能会得到一个旧值(这个旧值可能来自于很长时间之前),这将会引发问题。

 

先执行postProcessBeforeInitialization,然后是afterPropertiesSet,再然后是init-method,最后是postProcessAfterInitialization。

 

/**
 * Created with IntelliJ IDEA.
 *
 * @Description:
 * @author: bowang
 * @create: 2019-06-04 下午3:30
 **/
public class AsyncCacheLoader {
    private static final Logger LOGGER = LoggerFactory.getLogger(AsyncCacheLoader.class);

    public static <K, V> CacheLoader<K, V> buildAysncCacheLoader(final Function<K, V> function,
                                                                 final ThreadPoolExecutor executor) {
        return CacheLoader.asyncReloading(new CacheLoader<K, V>() {
            @Override
            public V load(K k) throws Exception {
                LOGGER.info("线程{}刷新缓存", Thread.currentThread().getId());
                try {
                    return function.apply(k);
                } finally {
                    LOGGER.info("线程{}刷新缓存结束", Thread.currentThread().getId());
                }
            }
        }, executor);
    }
}


private LoadingCache<BeaverLinkSkuListRequest, List> shippingRegionInfoCache = CacheBuilder.newBuilder()
                /** 基于容量的回收 1000 个key */
                .maximumSize(10)
                /** 5s */
                .refreshAfterWrite(5,TimeUnit.SECONDS)
                /** 初始容量 */
                .initialCapacity(20)
                .build(AsyncCacheLoader.buildAysncCacheLoader(this::refreshInfo, ThreadPoolUtils.executorService));


//注意key  一般都是String, Integer, 其他bean看情况重写hashcode和equals
private List refreshInfo(BeaverLinkSkuListRequest request) {
    List<String> list = new ArrayList<>();
    if (null != request && StringUtils.isBlank(request.getName())) {
        return getBeaverLinkSkuList(request.getCommonParams(), request.getBeaverLink(), request.getLimitSize());
    } else {
        return getSuperBeaverLinkSkuList(request.getCommonParams(), request.getBeaverLink(), request.getLimitSize());
    }
}

 

相关学习文档:http://ifeve.com/google-guava-cachesexplained/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Guava Cache是Google提供的一套Java工具包中的一部分,它是一套非常完善的本地缓存机制(JVM缓存)。它的设计灵感来源于ConcurrentHashMap,可以按照多种策略来清理存储在其中的缓存值,同时保持很高的并发读写性能。在使用Guava Cache时,可以通过get()或者put()等方法进行缓存操作,当进行这些操作时,Guava Cache会进行惰性删除,即在获取或者放置缓存的时候判断缓存是否过期并进行删除。在Guava Cache的核心原理中,使用Segment来进行缓存值的定位和管理。在创建Guava Cache对象时,可以使用CacheLoader来自动加载数据到缓存中,当缓存不存在时,CacheLoader会负责获取数据并将其放置到缓存中。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [[由零开始]Guava Cache介绍和用法](https://blog.csdn.net/qq497811258/article/details/108260969)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [Guava Cache简介、应用场景分析、代码实现以及核心的原理](https://blog.csdn.net/weixin_44795847/article/details/123702038)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值