Guava Cache的使用简介

1 引入

说到缓存,可能大家最先想到的还是Redis。作为基于键值对的非关系型数据库,Redis具有高性能、丰富的数据结构、持久化、高可用、分布式等特性,使其在业内得到了广泛的认可和使用。但是,使用Redis必然涉及到网络连接,当网络连接不稳定或网络耗时严重时,必然会影响到我们的业务使用。如果我们想提高我们的业务性能,又减少对其他机器的依赖,那么,使用本地缓存会是一个不错的选择。

使用本地缓存时,大多时候我们会采用ConcurrentHashMap来实现。对于本地缓存的使用,现在有一些较为成熟的本地缓存工具,如ehcache、guava cache,以及Caffeine。当需要对缓存进行持久化操作时,可以考虑使用ehcache。如果没有持久化操作,可以考虑使用guava cache或caffeine,caffeine是基于guava cache进行的二次优化,可根据自身业务需要选择使用哪一种本地缓存工具,本文将针对在DRS系统中使用到的Guava Cache进行讲解。

在本次项目中,选择的guava cache版本信息如下:

<dependency>
  <groupId>com.google.guava</groupId>
  <artifactId>guava</artifactId>
  <version>30.1-jre</version>
</dependency>

2 Guava Cache介绍

Guava cache和ConcurrentHashMap很相似,都是采用的key-value键值对的方式进行数据的存储,都采用了分段加锁的方式来提高多线程下的并发操作。不同的是,使用ConcurrentHashMap进行缓存存储时,会一直保留缓存数据,直到系统重启或显示删除缓存,而guava cache支持缓存过期时间的设置,可以进行缓存清理操作。同时,guava cache还可以自动加载缓存,当我们需要从数据库中加载数据到缓存时,不需要每次在获取缓存时判断缓存是否存在,guava cache会按照我们设置的加载方式进行数据的加载。除此之外,guava cache还可以进行一些统计操作,如统计缓存的命中率、加载新值的平均时间等。正是由于Guava cache的这些特性,我们才选择它应用于DRS系统中。下面,来看看我们是如何使用Guava Cache进行缓存管理操作的吧。

3 缓存的过期时间设置

Guava Cache支持三种过期设置,分别是基于容量的回收、定时回收,以及基于引用的回收。显然,基于容量的回收和基于引用的回收跟缓存时间没关系。当我们需要设置缓存的缓存的过期时间时,使用的必然是定时回收的回收策略,那么,在guava cache中,支持两种定时回收策略,分别是基于读写访问的回收与基于写访问的回收,也就是缓存在设置的时间内没有被读写访问或写访问,缓存将会被回收,这种特性是可以满足我们过期时间设置要求的。这两种方法分别是: expireAfterAccess(long, TimeUnit):基于读写访问的回收,缓存项在给定时间内没有被读/写访问,则回收 expireAfterWrite(long, TimeUnit):基于写访问的回收,即缓存项在给定时间内没有被写访问,则回收 如下,给出了基于写访问的回收策略,过期时间设置的是3秒:

Cache<String, String> cache = CacheBuilder.newBuilder()
  .expireAfterWrite(3, TimeUnit.SECONDS)
  .build();

但是,在我们的项目中,需要针对不同的key设置不同的过期时间。而上面的示例中,只能满足一种过期时间的设置,所有key的过期时间都是一样的。为了满足不同过期时间的需求,这里我们采用了一个两级结构的guava cache来实现,如下所示:

private Cache<String, Cache<String, String>> cacheCache = CacheBuilder.newBuilder().build();

public void init() {
  Cache<String, String> cache1 = CacheBuilder.newBuilder()
    .expireAfterWrite(3, TimeUnit.SECONDS)
    .build();
  cache1.put("appName", "drs-server");
  Cache<String, String> cache2 = CacheBuilder.newBuilder()
    .expireAfterWrite(2, TimeUnit.SECONDS)
    .build();
  cache2.put("appName", "drs-ops");
  cacheCache.put("key1", cache1);
  cacheCache.put("key2", cache2);
}

在上面的示例中,还可以使用Map+guava cache的方式,也就是一个map里面套一个guava cache,定义如下:

Map<String, Cache<String, String>> cacheMap = new HashMap<String, Cache<String, String>>();

那为什么我们要使用一个两级的cache结构而不是Map+cache的方式呢?原因在于,当读取的数据在缓存中没有时,我们是希望能够自动去从数据库加载的,所以使用两级的cache结构更合适。说到这里,就不得不提到guava cache的加载机制了。

4 缓存加载机制

guava cache有两种方式加载,一种是在构建缓存对象的时候,在build方法中设置缓存的加载方式(仅LocalCache对象可用);另一种是在获取缓存对象的时候,通过实现Callable接口方法的方式设置加载方式。 采用如下的方式构建缓存对象:

LoadingCache<String, String> cache = CacheBuilder.newBuilder()
  .build(new CacheLoader<String, String>() {
    @Override
    public String load(String s) throws Exception {
      return "load: " + UUID.randomUUID().toString().substring(1, 6);
    }
  });

通过上述方式构建完LocalCache的缓存对象,当调用get(K key)方法获取缓存时,如果指定的key对应的缓存值不存在,则会从load的方法中加载对象。但是,如果使用的是getIfPresent方法,则不会从执行load方法中加载缓存值,而是返回null值。当时用get(K key, Callable call) 获取缓存时,如果对应的缓存值不存在,则会从实现的Callable接口方法中来加载缓存。完整的示例代码如下所示:

public static void main(String[] args) throws Exception{
  LoadingCache<String, String> cache = CacheBuilder.newBuilder()
    .build(new CacheLoader<String, String>() {
      @Override
      public String load(String s) throws Exception {
        return "load: " + UUID.randomUUID().toString().substring(1, 6);
      }
    });

  System.out.println(cache.getIfPresent("appName1"));
  System.out.println(cache.get("appName2"));

  String loadValue = cache.get("appName3", new Callable<String>() {
    @Override
    public String call() throws Exception {
      return "call: " + UUID.randomUUID().toString().substring(1, 6);
    }
  });
  System.out.println(loadValue);
}

测试结果如下:

5 缓存清理

既然我们给缓存设置了过期时间,那么,缓存过期时间到了后,又是怎么来清理的呢?实际上,过期后的缓存并不会“自动”进行清理,而是在下一次进行读访问或是写访问的时候,通过判断当前时间与缓存加载进来时的时间进行比较,如果时间差大于给定的过期时间,则会清理缓存。如果设置了缓存的加载方式,则会重新加载缓存值,缓存的加载时间也会更新。通过代码调试可以发现,缓存清理是在get方法里实现的,用到的以下几个方法:

get(Object key, int hash); // 获取缓存值
getLiveEntry(Object key, int hash, long now); // 获取存活的对象
isExpired(ReferenceEntry<K, V> entry, long now); // 判断是否过期
expireEntries(long now); // 清理缓存

其中,判断是否过期的逻辑如下:

如果缓存已过期,则返回true,然后执行缓存清理删除操作,如下所示:

除此之外,guava cache还支持缓存的一些统计工作,如统计缓存命中率、加载新值的平均时间、缓存项被回收的总数等,还有缓存中断操作和刷新操作,由于本次项目中没有使用到这些特性,没有进行深入的了解,感兴趣的同学如有使用到,可以去官方网站进行深入了解,详见ifeve.com/google-guav…

以上就是Guava Cache的使用简介的详细内容,更多关于Guava Cache的使用的资料请关注我们其它相关文章!

(0)

相关推荐

  • Java内存缓存工具Guava LoadingCache使用解析

    这篇文章主要介绍了Java内存缓存工具Guava LoadingCache使用解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 一.Guava介绍 Guava是Google guava中的一个内存缓存模块,用于将数据缓存到JVM内存中.实际项目开发中经常将一些公共或者常用的数据缓存起来方便快速访问. Guava Cache是单个应用运行时的本地缓存.它不把数据存放到文件或外部服务器.如果不符合需求,可以选择Memcached.Redis等工具

  • 详解Guava Cache本地缓存在Spring Boot应用中的实践

    概述 在如今高并发的互联网应用中,缓存的地位举足轻重,对提升程序性能帮助不小.而 3.x开始的 Spring也引入了对 Cache的支持,那对于如今发展得如火如荼的 Spring Boot来说自然也是支持缓存特性的.当然 Spring Boot默认使用的是 SimpleCacheConfiguration,即使用 ConcurrentMapCacheManager 来实现的缓存.但本文将讲述如何将 Guava Cache缓存应用到 Spring Boot应用中. Guava Cache是一个全内

  • SpringBoot加入Guava Cache实现本地缓存代码实例

    这篇文章主要介绍了SpringBoot加入Guava Cache实现本地缓存代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 在pom.xml中加入guava依赖 <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>18.0</version>

  • springboot使用GuavaCache做简单缓存处理的方法

    问题背景 实际项目碰到一个上游服务商接口有10秒的查询限制(同个账号). 项目中有一个需求是要实时统计一些数据,一个应用下可能有多个相同的账号.由于服务商接口的限制,当批量查询时,可能出现同一个账号第一次查询有数据,但第二次查询无数据的情况. 解决方案 基于以上问题,提出用缓存的过期时间来解决. 这时,可用Redis和Guava Cache来解决: 当批量查询时,同一个账号第一次查询有数据则缓存并设置过期时间10s, 后续查询时直接从缓存中取,没有再从服务商查询. 最终采用Guava Cache

  • Springboot整合GuavaCache缓存过程解析

    这篇文章主要介绍了springboot整合GuavaCache缓存过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 Guava Cache是一种本地缓存机制,之所以叫本地缓存,是因为它不会把缓存数据放到外部文件或者其他服务器上,而是存放到了应用内存中. Guava Cache的优点是:简单.强大.轻量级. GuavaCache适用场景: 1.某些接口或者键值会被查询多次以上: 2.愿意使用或牺牲一些内存空间来提升访问或者计算速度: 3.缓

  • Guava Cache的使用简介

    1 引入 说到缓存,可能大家最先想到的还是Redis.作为基于键值对的非关系型数据库,Redis具有高性能.丰富的数据结构.持久化.高可用.分布式等特性,使其在业内得到了广泛的认可和使用.但是,使用Redis必然涉及到网络连接,当网络连接不稳定或网络耗时严重时,必然会影响到我们的业务使用.如果我们想提高我们的业务性能,又减少对其他机器的依赖,那么,使用本地缓存会是一个不错的选择. 使用本地缓存时,大多时候我们会采用ConcurrentHashMap来实现.对于本地缓存的使用,现在有一些较为成熟的

  • 解析springboot整合谷歌开源缓存框架Guava Cache原理

    目录 Guava Cache:⾕歌开源缓存框架 Guava Cache使用 使用压测⼯具Jmeter5.x进行接口压力测试: 压测⼯具本地快速安装Jmeter5.x 新增聚合报告:线程组->添加->监听器->聚合报告(Aggregate Report) Guava Cache:⾕歌开源缓存框架 Guava Cache是在内存中缓存数据,相比较于数据库或redis存储,访问内存中的数据会更加高效.Guava官网介绍,下面的这几种情况可以考虑使用Guava Cache: 愿意消耗一些内存空间

  • java的Guava工具包介绍

    集合 普通集合 List<String> list = Lists.newArrayList(); Set<String> set = Sets.newHashSet(); Map<String, String> map = Maps.newHashMap(); Set 取交集.并集.差集 HashSet<Integer> setA = Sets.newHashSet(1, 2, 3, 4, 5); HashSet<Integer> setB =

  • Mango Cache缓存管理库TinyLFU源码解析

    目录 介绍 整体架构 初始化流程 读流程 写流程 事件处理机制 主流程 write 清理工作 缓存管理 什么是LRU? 什么是SLRU? 什么是TinyLFU? mango Cache中的TinyLFU counter counter的初始化 counter的使用 lruCache slruCache filter TinyLFU的初始化 TinyLFU写入 TinyLFU访问 增加entry的访问次数 估计entry访问次数 总结 介绍 据官方所述,mango Cache是对Guava Cac

  • 详解高性能缓存Caffeine原理及实战

    目录 一.简介 二.Caffeine 原理 2.1.淘汰算法 2.1.1.常见算法 2.1.2.W-TinyLFU 算法 2.2.高性能读写 2.2.1.读缓冲 2.2.2.写缓冲 三.Caffeine 实战 3.1.配置参数 3.2.项目实战 四.总结 一.简介 下面是Caffeine 官方测试报告. 由上面三幅图可见:不管在并发读.并发写还是并发读写的场景下,Caffeine 的性能都大幅领先于其他本地开源缓存组件. 本文先介绍 Caffeine 实现原理,再讲解如何在项目中使用 Caffe

  • Java如何设置过期时间的map的几种方法

    目录 一.技术背景 二.技术效果 三.ExpiringMap 3.1功能简介 3.2源码 3.3示例 四.LoadingCache 4.1功能简介 4.2示例 4.3移除机制 4.4其他 五.HashMap的封装 一.技术背景 在实际的项目开发中,我们经常会使用到缓存中间件(如redis.MemCache等)来帮助我们提高系统的可用性和健壮性. 但是很多时候如果项目比较简单,就没有必要为了使用缓存而专门引入Redis等等中间件来加重系统的复杂性.那么Java本身有没有好用的轻量级的缓存组件呢.

  • Java高性能本地缓存框架Caffeine的实现

    目录 一.序言 二.缓存简介 (一)缓存对比 (二)本地缓存 三.SpringCache (一)需求分析 (二)序列化 (三)集成 四.小结 一.序言 Caffeine是一个进程内部缓存框架,使用了Java 8最新的[StampedLock]乐观锁技术,极大提高缓存并发吞吐量,一个高性能的 Java 缓存库,被称为最快缓存. 二.缓存简介 (一)缓存对比 从横向对常用的缓存进行对比,有助于加深对缓存的理解,有助于提高技术选型的合理性.下面对比三种常用缓存:Redis.EhCache.Caffei

随机推荐