Java开发利器之Guava Cache的使用教程

目录
  • 前言
  • 缓存的意义
  • Guava Cache特色
  • API介绍
  • Builder配置
  • 简单示例
    • 本地缓存

前言

缓存技术被认为是减轻服务器负载、降低网络拥塞、增强Web可扩展性的有效途径之一,其基本思想是利用客户访问的时间局部性(Temproral Locality)原理, 将客户访问过的内容在Cache中存放一个副本,当该内容下次被访问时,不必连接到驻留网站,而是由Cache中保留的副本提供。

在企业Web应用中,通过缓存技术能够提高请求的响应速度;减少系统IO开销;降低系统数据读写压力...

缓存的意义

首先我们要知道,在我们开发过程中,为什么要使用缓存,缓存能够为我们带来哪些好处!

优点

  • 通过缓存承载系统压力,减少对系统或网络资源访问而引起的性能消耗,在流量较大时能够很好地减少系统拥塞
  • 缓存一般都是使用存取非常快的组件实现,通过缓存能够快速响应客户端请求,从而降低客户访问延迟,提审系统响应速度
  • 在配备负载均衡的应用架构中,通过缓存静态资源能够有效减少服务器负载压力
  • 当下游应用故障时,通过返回缓存数据能够在一定程度上增强应用容错性

缺点

  • 缓存数据与实际数据不一致问题问题
  • 高并发场景时存在缓存击穿、缓存穿透、缓存雪崩等问题

总的来说,缓存主要是针对高频访问但低频更新的数据,从而加快服务器响应与原资源访问压力

Guava Cache是一个相对比较简单并且容易理解的本地缓存框架,今天主要以此为开端来认识并学习如何使用缓存

Guava Cache特色

本地缓存我们可以简单的理解为Map,将数据保存到Map(内存)中,下次使用该数据时,通过key直接从Map中取即可。但是使用Map会有一些几个问题需要考虑:

  • 缓存的容量。不可能无限制的对数据进行缓存,当数据较大时占用系统资源会导致主业务受影响
  • 缓存的清理。有些缓存使用频率很低,如果一直占用资源也是一种浪费
  • 并发访问时的效率问题。缓存更新时瞬时对系统、网络资源的访问导致故障
  • 缓存使用情况评估

当然以上问题我们通过我们对Map包装下即可实现,当然Guava Cache也就是基于这种思想,底层原理则是基于Map实现,我们看下其有哪些特色:

缓存过期和淘汰机制

通过设置Key的过期时间,包括访问过期和创建过期;设置缓存容量大小,采用LRU的方式,选择最近最久的缓存进行删除。

并发处理能力

Cache主要基于CurrentHashMap实现线程安全;通过对key的计算,基于分段锁,提高缓存读写效率,降低锁的粒度,提升并发能力

更新锁定

在缓存中查询某个key,如果不存在,则查源数据,并回填缓存。在高并发下会出现,多次查询元数据并重复回填缓存,可能会造成系统故障,最明显的DB服务器宕机,性能下降等。GuavaCache通过在CacheLoader调用load方法时,对同一个key同一时刻只会有一个请求去读源数据并回填缓存,后面的请求则直接继续从缓存读取,有效阻断并发请求对资源服务的影响。

集成数据源

一般我们在业务中操作缓存,都会操作缓存和数据源两部分GuavaCache的get可以集成数据源,在从缓存中读取不到时可以从数据源中读取数据并回填缓存

监控统计

监控缓存加载次数、命中率、失误率以及数据加载时长等

API介绍

1.缓存构建

ManualCache 此时Cache相当于一个Map,对数据进行CRUD操作时,需要同步操作缓存Map; 高并发情况时,可以使用get(k,loader)读缓存,通过Cache锁机制,防止对系统资源(DB)的并发访问 通过put方法实现缓存的存入与更新;

LoadingCache 此时构建的是一个实现了Cache接口的LoadingCache,相比ManualCache,提供了缓存回填机制,即当缓存不存在时,会基于CacheLoader查询数据并将结果回填到缓存, 在高并发时,可以有效地基于缓存锁减少对系统资源的调用。此时仅需要关注缓存的使用,缓存的更新与存入都是基于CacheLoader实现;

2.缓存获取

get(k) 根据key查询,没有则触发load;如果load为空则抛出异常

getUnchecked(k) 缓存不存在或返回为null会抛出检查异常

get(k,loader) 根据key查询,没有则调用loader方法,且对结果缓存;如果loader返回null则抛出异常,此时不会调用默认的load方法

getIfPresent(k) 有缓存则返回,否则返回null,不会触发load

3.缓存更新

put(k,v) 如果缓存已经存在,则会先进行一次删除

4.缓存删除

invalidate(k) 根据key使缓存失效

过期  通过配置的过期参数,比如expireAfterAccess、expireAfterWrite、refreshAfterWrite

过载  当缓存数据量超过设置的最大值时,根据LRU算法进行删除

引用  构建缓存时将键值设置为弱引用、软引用,基于GC机制来清理缓存

5.统计

hitRate() 缓存命中率;

hitMiss() 缓存失误率;

loadCount() 加载次数;

averageLoadPenalty() 加载新值的平均时间,单位为纳秒;

evictionCount() 缓存项被回收的总数,不包括显式清除。

Builder配置

配置 描述
expireAfterAccess 多久没有读写则过期
expireAfterWrite 写入后多久没更新自动过期,先删除,后load
refreshAfterWrite 上一次更新后多久自动刷新,先reload后删除,并发时会取到老的数据
removalListener 设置缓存删除监听
initialCapacity 缓存初始化大小
concurrencyLevel 最大的并发数,可以理解为并发线程数量
maximumSize 最大缓存数量,超过时会根据策略清除
maximumWeight 最大权重容量数,仅用于确定缓存是否超过容量
recordStats 缓存命中统计

简单示例

ManualCache模式

下面以用户服务为例,我们看下如何在增删改查方法中使用缓存:

private Cache<String, User> cache = CacheBuilder.newBuilder()
            .expireAfterWrite(3, TimeUnit.SECONDS)//写入多久没更新自动过期,先删除,后load
            .removalListener(new RemovalListener<Object, Object>() {
                @Override
                public void onRemoval(RemovalNotification<Object, Object> notification) {
                    LOGGER.info("{} remove {}",LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")),notification.getKey());
                }
            })
            .initialCapacity(20) //初始化容量
            .concurrencyLevel(10) // 并发
            .maximumSize(100) //最多缓存数量
            .recordStats() // 开启统计
            .build();

@Override
    public User getUser(String id){
//        缓存不存在时,通过LocalCache锁机制,防止对数据库的高频访问
       User user;
       try {
           user = cache.get(id,()-> {
               LOGGER.info("缓存不存在,从loader加载数据");
               return userDao.get(id);
           });
       } catch (ExecutionException e) {
           throw new RuntimeException(e);
       }
        return user;
    }

    @Override
    public User saveOrUpdateUser(User user){
        userDao.saveOrUpdate(user);
        cache.put(user.getId(),user);
        return user;
    }

    @Override
    public void removeUser(String id){
        userDao.remove(id);
        cache.invalidate(id);
    }

LoadingCache模式

private LoadingCache<String, User> cache = CacheBuilder.newBuilder()
            // 省略
            .build(new CacheLoader<String, User>() {
                @Override
                public User load(String key) throws Exception {
                    LOGGER.info("{} load {}",LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")),key);
                    return userDao.get(key);
                }

                @Override
                public ListenableFuture<User> reload(String key, User oldUser) throws Exception {
                    LOGGER.info("{} reload {}", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")),key);
                    ListenableFutureTask<User> listenableFutureTask = ListenableFutureTask.create(() -> userDao.get(key));
                    CompletableFuture.runAsync(listenableFutureTask);
                    return listenableFutureTask;
                }
            });

    @SneakyThrows
    @Override
    public User getUser(String id){
        // 缓存不存在或返回为null会抛出异常
        try {
            return cache.getUnchecked(id);
        } catch (Exception e) {
            return null;
        }
    }

    @Override
    public User saveOrUpdateUser(User user){
        cache.invalidate(user.getId());
        return userDao.saveOrUpdate(user);
    }

    @Override
    public void removeUser(String id){
        cache.invalidate(id);
        userDao.remove(id);
    }

总结:第一种写法更像是前面说到的Map,在对数据进行CRUD操作时,需要用户手动对缓存进行同步的更新或删除操作,所以叫ManualCache(手动),当然Guava Cache对Map的加强依然有效,比如过期清除,缓存容量限制。第二种方式写法差不多,主要是引入了CacheLoader接口,在读数据时缓存数据不存在时,通过CacheLoader的load方法先写缓存后返回数据

注意

1.expireAfterWrite、refreshAfterWrite的区别

在refreshAfterWrite导致缓存失效时,并不会因为更新缓存而阻塞缓存数据的返回,只不过是返回老的数据

2.不能缓存null

有时候为了将值为null的数据统一缓存,这样就不会因为没有缓存数据而访问数据库造成压力

3.读写时才进行删除

Guava Cache的缓存数据删除是在更新或写入时才会触发,没有单独的调度服务完成这一工作

本地缓存

类似的本地缓存还有,有兴趣的可以自己尝试,其实实现思想应该也差不多

到此这篇关于Java开发利器之Guava Cache的使用教程的文章就介绍到这了,更多相关Java Guava Cache内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java项目Guava包 HashMultimap使用及注意事项

    目录 1. 数据模型介绍 2. 简单使用介绍 2.1 容器创建 2.2 添加元素 2.3 移除元素 2.4 替换元素 2.5 获取元素及遍历 2.6 输出所有的key 2.7 输出所有的value 3. 小结 今天给大家介绍一个相对基础的知识点 HashMultmap: guava基本上可以说是java开发项目中,大概率会引入的包,今天介绍的主角是一个特殊的容器 -- HashMultmap,可以简单的将它的数据结构理解为Map<K, Set<V>> 那么为什么会突然想到介绍一下它

  • java guava主要功能介绍及使用心得总结

    目录 1. 前言 2. Guava主要功能介绍 2.1 集合操作 2.2 缓存 2.3 字符串处理 2.4 函数式编程 2.5 其他实用工具 3. 结论 1. 前言 Guava是一个由Google开发的Java核心库,它提供了很多有用的方法和实用工具类,可以帮助开发人员提高代码质量和开发效率.在本文中,我们将详细介绍Guava框架的主要功能,并通过实例代码来展示如何使用这些功能. Guava框架的初衷是为了解决Java标准库中的一些不足之处,例如集合操作的不便利.缺少功能强大的缓存实现等问题.随

  • Java两大工具库Commons和Guava使用示例详解

    目录 正文 先定义观察者 然后定义账户类 最后实现事件总线 创建观察者接口和具体观察者 创建Subject: 正文 除了操作集合.限流和缓存,Guava还有另一个隐秘的功能:事件总线EventBus机制——是发布-订阅模式的实现,不需要显式地注册回调——比观察者模式更灵活. EventBus是在单体架构内实现松耦合的一种很好的手段,通过它可以实现与业务逻辑无关的事件监听和消费.Guava提供的事件总线EventBus分为两种: 1.同步事件EventBus,主要用于单线程环境: 2.异步事件As

  • Java效率提升神器之Guava-Joiner

    目录 Joiner Joiner.MapJoiner 源代码分析 拼接Map键值对 姊妹篇:Java效率提升神器jOOR 在我们的开发中经常会用到Guava中的一些功能.但是我们所使用到的只是Guava API中的小的可怜的一个子集.我们大家一起来发掘一下Guava中更多的一些功能. Joiner 这是在我们代码中出现频率比较高的一个功能.经常需要将几个字符串,或者字符串数组.列表之类的东西,拼接成一个以指定符号分隔各个元素的字符串,比如要将一个用List保存的字符集合拼起来作为SQL语句的条件

  • 浅谈Java中GuavaCache返回Null的注意事项

    Guava在实际的Java后端项目中应用的场景还是比较多的,比如限流,缓存,容器操作之类的,有挺多实用的工具类,这里记录一下,在使用GuavaCache,返回null的一个问题 I. 常见使用姿势 @Test public void testGuava() { LoadingCache<String, String> cache = CacheBuilder.newBuilder().build(new CacheLoader<String, String>() { @Overri

  • Android开发利器之pidcat安装方式

    介绍pidcat: pidcat 是Android届JakeWharton大神开发的一款命令行工具,堪称Android开发利器,它能方便Android程序猿捕获日志,过滤日志,定位程序问题,超级好用.pidcat对不同的Tag,显示不同的颜色,可辨认度很高,支持根据Tag过滤log,命令强大,用法简单易学,比Android默认的logcat命令强大太多.logcat只支持过滤TAG,而不支持应用级别的日志,如果只想输入某个app的日志,貌似不好做到,估计得结合Android的logcat工具等等

  • python开发利器之ulipad的使用实践

    介绍 UliPad是一个国人开发的python轻量级编辑器,导向和灵活的编程器.它如类浏览器,代码自动完成许多功能,如:HTML查看器,目录浏览器,向导等. 下载与安装 下载地址:https://pypi.python.org/pypi/UliPad 安装,傻瓜式,一路next即可 配置 安装好之后双击启动之后逐步进行下面的配置. 1.文件>目录浏览,这样我们可以在左侧看到目录方便管理脚本,最终效果图如下: 2.编辑>参数>python>设置python解释器>增加>选

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

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

  • Java应用服务器之tomcat部署的详细教程

    一.相关术语简介 首先我们来了解下tomcat是什么,tomcat是apache软件基金会中的一个项目,由apache.Sun 和其他一些公司及个人共同开发而成.主要作用是提供servlet和jsp类库:tomcat是一个免费开源的web服务器,它和nginx.httpd服务不同的是,它不擅长处理HTML代码,更多的是处理JSP程序:有点类似fpm服务专门处理php程序: jdk:java开发工具箱(Java Development Kit),主要提供java开发相关工具包,库文件以及jre和j

  • [JAVA]十四种Java开发工具点评

    在计算机开发语言的历史中,从来没有哪种语言象Java那样受到如此众多厂商的支持,有如此多的开发工具,Java菜鸟们如初入大观园的刘姥姥,看花了眼,不知该何种选择.的确,这些工具各有所长,都没有绝对完美的,就算是老鸟也很难做出选择.在本文中我简要介绍了常见的十四种Java开发工具的特点,管中窥"器",希望能对大家有所帮助. 1.JDK (Java Development Kit) 2.Java Workshop 3.NetBeans 与Sun Java Studio 5 4.Borlan

  • Android图片加载利器之Picasso基本用法

    今天开始我们来学习一下Picasso,计划包括以下几方面的内容: 图片加载利器之Picasso进阶 图片加载利器之Picasso源码解析 目前市场上比较流行的图片加载框架主要有UniversalImageLoader,Picasso,Glide,Fresco. 下面简单介绍一下这几个框架: UniversalImageLoader:这个可以说是非常非常经典的一个了,相信每个app的开发人员都使用过,只可惜作者已经停止该项目的维护了,所以不太推荐使用. Picasso:是Square公司出品的图片

  • 使用Ajax进行文件与其他参数的上传功能(java开发)

    文件上传: 记得前一段时间,为了研究Ajax文件上传,找了很多资料,在网上看到的大部分是form表单的方式提交文件,对于Ajax方式提交文件并且也要提交表单中其他数据,发现提及的并不是很多,后来在同事的帮助下,使用ajaxfileupload最终完成了文件上传与其他提交的操作,现在分享给大家,希望大家能有有所帮助. 操作步骤: 1 导入jar包: 我们在使用文件上传时,需要使用到两个jar包,分别是commons-io与commons-fileupload,在这里我使用的两个版本分别是2.4与1

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

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

  • Python任务调度利器之APScheduler详解

    任务调度应用场景 所谓的任务调度是指安排任务的执行计划,即何时执行,怎么执行等.在现实项目中经常出现它们的身影:特别是数据类项目,比如实时统计每5分钟网站的访问量,就需要每5分钟定时从日志数据分析访问量. 总结下任务调度应用场景: 离线作业调度:按时间粒度执行某项任务 共享缓存更新:定时刷新缓存,如redis缓存:不同进程间的共享数据 任务调度工具 linux的crontab, 支持按照分钟/小时/天/月/周粒度,执行任务 java的Quartz windows的任务计划 本文介绍的是pytho

  • Android神兵利器之Image Asset Studio的实现

    曾几何时, Android开发没有那么方便, 制作一个图标也许都要请美工, 或者自己花时间去PS. 或者去一些在线图标制作网站, 例如: https://makeappicon.com/ http://iconion.com/ 但是, 这样的苦日子已经一去不复返咯~ 随着Google亲儿子Android Studio越发成熟, 给我们的Android开发带来了越来越多的便利.Google果然不错~ 今天给大家介绍的一个Android开发的神兵利器就是Android Studio自带的图标制作利器

随机推荐