Guava Cache探索及spring项目整合GuavaCache实例
背景
对于高频访问但是低频更新的数据我们一般会做缓存,尤其是在并发量比较高的业务里,原始的手段我们可以使用HashMap或者ConcurrentHashMap来存储.
这样没什么毛病,但是会面临一个问题,对于缓存中的数据只有当我们显示的调用remove方法,才会移除某个元素,即便是高频的数据,也会有访问命中率的高低之分,内存总是有限的,我们不可能无限地去增加Map中的数据.
我希望的比较完美的场景时.对于一个业务,我只想分配给你2k的内存,我们假设map中一条数据(键值对)是1B,那么最多它能存2048条数据,当数据达到这个量级的时候,需要淘汰一些访问率比较低的数据来给新的数据腾地方,使用传统的HashMap比较难实现,因为我们不知道哪些数据访问率低(除非专门去记录),那么Guava针对内存缓存优化的一个组件就闪亮登场了.
准备
上面说到我们需要一种淘汰策略来自动筛选缓存数据,下面简单了解下,几种淘汰算法
先进先出算法(FIFO):这种淘汰策略顾名思义,先存的先淘汰.这样简单粗暴,但是会错杀一些高频访问的数据
最近最少使用算法(LRU):这个算法能有效优化FIFO的问题,高频访问的数据不太容易被淘汰掉,但也不能完全避免.GuavaCache一些特性符合这种算法
最近最少频率算法(LFU): 这个算法又对LRU做了优化,会记录每个数据的访问次数,综合访问时间和访问次数来淘汰数据.
Guava Cache基础
GuavaCache提供了线程安全的实现机制,简单易用,上手成本很低,在需要使用内存做缓存的业务场景时可以考虑使用.
GuavaCache缓存机制有两个接口,Cache和LoadingCache,后者也是一个接口,继承自Cache,并额外多了几个接口,如果我们想实例化一个Cache对象,还需要了解一个CacheBuilder类,这个类就是雨从来构建Cache对象的,我们先来用CacheBuilder实例化一个Cache对象再学习它的一些字段含义.
public static void main(String[] args) {
Cache<String,String> myMap = CacheBuilder.newBuilder()
.expireAfterAccess(30L, TimeUnit.SECONDS)
.expireAfterWrite(3L,TimeUnit.MINUTES)
.concurrencyLevel(6)
.initialCapacity(100)
.maximumSize(1000)
.softValues()
.build();
myMap.put("name", "张三");
System.out.println(myMap.getIfPresent("name"));
}
这样我们就创建一个类似map接口的Cache对象,描述一下上面创建的这个对象:
创建了一个Cache对象,这个对象有这样的特性,初始大小为100(能存100个键值对),最大size为1000,在数据写入3分钟后会被自动移除,并且数据如果在30秒内,没有被访问则会被移除,另外这Map结构的对象支持最多6个调用方同时更新这个缓存结构的数据,即并发更新操作最大数量为6.
我们看到还有一个softValues()属性没有讲,会放在下面说明,其实CacheBuilder并不只有这么几个属性可设置,下面我们具体讲一下.
CacheBuilder中一些常用的属性字段:
concurrencyLevel(int):指定允许同时更新的操作数,若不设置CacheBuilder默认为4,这个参数会影响缓存存储空间的分块,可以简单理解为,默认会创建指定size个map,每个map称为一个区块,数据会分别存到每个map里,我们根据实际需要设置这个值的大小.
initialCapacity(int):指定缓存初始化的空间大小,如果设置了40,并且concurrencyLevel取默认,会分成4个区块,每个区块最大的size为10,当更新数据时,会对这个区块进行加锁,这就是为什么说,允许同时更新的操作数为4,延伸一点,在淘汰数据时,也是每个区块单独维护自己的淘汰策略.也就是说,如果每个区块size太大,竞争就会很激烈.
maximumSize(long):指定最大缓存大小.当缓存数据达到最大值时,会按照策略淘汰掉一些不常用的数据,需要注意的是,在缓存数据量快要到达最大值的时候,就会开始数据的回收,简单理解为"防患于未然"吧
下边三个参数分别是,SoftValues(),weakKeys(),weakValues(),在解释这三个参数前,需要我们先了解一下java中的软引用,和弱引用.
和弱引用对应的是强引用,也是我们在编码过程中最常使用的,我们声明的变量,对象,基本都是强引用,这样的对象,jvm在GC时不会回收,哪怕是抛出OOM.
而弱引用就不一样了,在java中,用java.lang.ref.WeakReference标示声明的值,jvm在垃圾回收的时候会将它回收掉,那么软引用呢?就是用SoftReference标示的,声明为弱引用的对象,会在jvm的内存不足时回收掉.
看出区别了吗,简单总结下就是,软引用,只有在内存不足时才可能被回收,在正常的垃圾回收时不会被回收,弱引用,会在jvm进行垃圾回收的时候被删除.
softValues():将缓存中的数据设置为softValues模式。数据使用SoftReference类声明,就是在SoftReference实例中存储真实的数据。设置了softValues()的数据,会被全局垃圾回收管理器托管,按照LRU的原则来定期GC数据。数据被GC后,可能仍然会被size方法计数,但是对其执行read或write方法已经无效
weakKeys()和weakValues():当设置为weakKey或weakValues时,会使用(==)来匹配key或者value值(默认强引用时,使用的是equals方法),这种情况下,数据可能会被GC,数据被GC后,可能仍然会被size方法计数,但是对其执行read或write方法已经无效
Guava cache在spring项目中的使用
下面以一个我在项目中的实际应用梳理一下在spring项目中应该如果整合guava cache
1.引入guava的maven依赖
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>26.0-jre</version>
</dependency>
上面使用的版本是我在写这篇笔记时的最新版本.
2.在application-context.xml加入配置
<!--开启缓存注解-->
<cache:annotation-driven /> <bean id="cacheManager" class="org.springframework.cache.guava.GuavaCacheManager">
<property name="cacheSpecification" value="initialCapacity=500,maximumSize=5000,expireAfterAccess=2m,softValues" />
<property name="cacheNames">
<list>
<value>questionCreatedTrack</value>
</list>
</property>
</bean>
在上面配置中我们实现了一个cacheManager,这是必须要配置的,默认配置的是org.springframework.cache.support.SimpleCacheManager,我们这里把它改成了Guava的缓存管理器的实现.如果使用其他的实现,比如redis,这里只需要配置成redis的相关缓存管理器即可
cacheManager可以简单理解为保存Cache的地方,Cache里边有我们具体想要缓存的数据,一般以key-value的键值对形式
上述配置的bean中声明的两个属性,一个是cacheSpecification,不需要多说了,参考上面的详细参数,需要了解一点的是,这里的参数使用的是CacheBuilderSpec类,以解析代表CacheBuilder配置的字符串的形式来创建CacheBuilder实例
cacheNames可以根据自己的实际业务命名,可声明多个
3.在代码中使用spring的cache相关注解
@Cacheable(value = "questionCreatedTrack",key="#voiceId",condition = "#voiceId>0")
public Long getQuestionIdByVoiceId(Long anchorId, Long voiceId) {
String key = String.format(HOMEWORK_QUESTION_ANCHOR_KEY, anchorId);
String value = redisProxy.getValue(key, String.valueOf(voiceId));
return StringUtils.isEmpty(value) ? null : Long.parseLong(value);
} @CachePut(value = "questionCreatedTrack",key = "#voiceId",condition = "#voiceId>0")
public Long statCollectionQuestionToCache(Long anchorId, Long voiceId, Long questionId) {
String key = String.format(HOMEWORK_QUESTION_ANCHOR_KEY, anchorId);
redisProxy.setOneToHash(key, String.valueOf(voiceId), String.valueOf(questionId));
return questionId;
} @CacheEvict(value = "questionCreatedTrack",key="#voiceId")
public void removeCollectionQuestionFromCache(Long anchorId, Long voiceId) {
String key = String.format(HOMEWORK_QUESTION_ANCHOR_KEY, anchorId);
redisProxy.deleteOneToHash(key, String.valueOf(voiceId));
}
先简单说一下这里的逻辑,我主要是使用内存做一个一级缓存,redis做第二级的缓存,上面三个方法的作用分别是
getQuestionIdByVoiceId(..):通过voiceId查询questionId,使用@Cacheable注解标记的意思是,代码执行到这个方法时,会先去guava cache里去找,有的话直接返回不走方法,没有的话再去执行方法,返回后同时加入Cache,缓存结构中的value是方法的返回值,key是方法入参中的vocieId,这里的缓存结构是,key=voiceId,value=questionId
statCollectionQuestionToCache():方法的逻辑是将voiceId和questionId保存进redis里,使用@CachePut注解标记的意思是,不去缓存里找,直接执行方法,执行完方法后,将键值对加入Cache.
removeCollectionQuestionFromCache():方法的逻辑是删除redis中的key为voiceId的数据,使用@CacheEvict注解标记的意思是,清除Cache中key为voiceId的数据
通过以上三个注解,可以实现这样的功能,当查询voiceId=123的对应questionId时,会先去Cache里查,Cache里如果没有再去redis里查,有的话同时加入Cache,(没有的话,也会加入Cache,这个下面会说),然后在新增数据以及移除数据的时候,redis和Cache都会同步.
@Cacheable方法查询结果为null怎么处理
这个需要我们根据实际需要,决定要不要缓存查询结果为null的数据,如果不需要,需要使用下面的注解
@Cacheable(value = "questionCreatedTrack",key="#voiceId",condition = "#voiceId>0",unless = "#result==null")
参考资料
http://www.voidcn.com/article/p-pvvfgdga-bos.html
https://www.cnblogs.com/fashflying/p/6908028.html
Guava Cache探索及spring项目整合GuavaCache实例的更多相关文章
- 从零开始--Spring项目整合(1)使用maven框架搭建项目
这些年一直在用spring的框架搭建项目,现在开始我们从零开始利用Spring框架来搭建项目,目前我能想到有Spring.SpringMVC.SpringJDBC.Mybatis.WebSockt.R ...
- 从零开始--Spring项目整合(2)整合SpringMVC
1.pom.xml 定义版本 <properties> <spring.version>4.2.7.RELEASE</spring.version> <jac ...
- guava cache与spring集成
缓存的背景 缓存,在我们日常开发中是必不可少的一种解决性能问题的方法.简单的说,cache 就是为了提升系统性能而开辟的一块内存空间.在cpu进行计算的时候, 首先是读取寄存器,然后内存,再是硬盘.由 ...
- [Java 缓存] Java Cache之 Guava Cache的简单应用.
前言 今天第一次使用MarkDown的形式发博客. 准备记录一下自己对Guava Cache的认识及项目中的实际使用经验. 一: 什么是Guava Guava工程包含了若干被Google的 Java项 ...
- Google guava cache源码解析1--构建缓存器(1)
此文已由作者赵计刚授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 1.guava cache 当下最常用最简单的本地缓存 线程安全的本地缓存 类似于ConcurrentHas ...
- 第二章 Google guava cache源码解析1--构建缓存器
1.guava cache 当下最常用最简单的本地缓存 线程安全的本地缓存 类似于ConcurrentHashMap(或者说成就是一个ConcurrentHashMap,只是在其上多添加了一些功能) ...
- Spring Boot 整合 Hibernate5
Run java -jar -Dspring.profiles.active=dev sport.web.services.jar Maven <parent> <groupId&g ...
- 3.springMVC+spring+Mybatis整合Demo(单表的增删该查,这里主要是贴代码,不多解释了)
前面给大家讲了整合的思路和整合的过程,在这里就不在提了,直接把springMVC+spring+Mybatis整合的实例代码(单表的增删改查)贴给大家: 首先是目录结构: 仔细看看这个目录结构:我不详 ...
- Spring cache简单使用guava cache
Spring cache简单使用 前言 spring有一套和各种缓存的集成方式.类似于sl4j,你可以选择log框架实现,也一样可以实现缓存实现,比如ehcache,guava cache. [TOC ...
随机推荐
- MySQL在线DDL gh-ost 使用说明
背景: 作为一个DBA,大表的DDL的变更大部分都是使用Percona的pt-online-schema-change,本文说明下另一种工具gh-ost的使用:不依赖于触发器,是因为他是通过模拟从库, ...
- 逐步搭建vs2015的API自带认证调用+跨域调用
demo百度网盘链接:https://pan.baidu.com/s/1HJ19RJwS6qCixui8KF8QBg 提取码:yt1c 首先我们建立一个webapi项目,这个就不需要小编解释了.如下图 ...
- Lily_music 网页音乐播放器 -可搜索(附歌词联动播放效果解说)
博客地址:https://ainyi.com/59 写在前面 这是我今年(2018)年初的小项目,当时也是手贱,不想用别的播放器,想着做一个自己的网页播放器,有个歌曲列表.可关键词搜索.歌词滚动播放的 ...
- 第46章 发现端点(Discovery Endpoint) - Identity Server 4 中文文档(v1.0.0)
发现端点可用于检索有关IdentityServer的元数据 - 它返回发布者名称,密钥材料,支持的范围等信息.有关详细信息,请参阅规范. 发现端点可通过/.well-known/openid-conf ...
- Odd-e CSD Course Day 4
今天有一部分是透過 Code Review 來引發我們對於下面幾個方向的想法 Good Unit Tests 在今天的 Code Review 裡,Stanly 翻出了一個我們寫的 Unit Test ...
- C# 如何隐藏或显示工作表中的网格线
我们知道Excel中有许多虚线形式的网格线,它们用于区分Excel工作表中的单元格.有了网格线,读者可以轻松地查看和核对工作表中的数据.Excel工作表中,网格线是默认存在的,但我们可以根据自身的需求 ...
- 50.Linux-分析ifconfig到内核的调用过程,实现内核启机自动设MAC地址(原)
内核版本: Linux version 3.10.14 1.由于每次开发板开机的网卡eth0的物理地址都是随机的. 然后在网上找到可以通过命令行实现设置mac物理地址: ifconfig eth0 d ...
- 浏览器与Node的事件循环(Event Loop)有何区别?
前言 本文我们将会介绍 JS 实现异步的原理,并且了解了在浏览器和 Node 中 Event Loop 其实是不相同的. 一.线程与进程 1. 概念 我们经常说 JS 是单线程执行的,指的是一个进程里 ...
- Vue移动端项目模板
一个集成移动端开发插件的Vue移动端模板包含1.css: 使用stylus开发css 集成reset样式文件 修改UI组件文件 统一样式处理(如主题色等)2.UI组件 使用热门的vant与mint-u ...
- ASP.NET Core 一步步搭建个人网站(1)_环境搭建
ASP.NET Core2.0发布有一阵子了,这是.NET 开源跨平台的一个重大里程碑, 也意味着比1.0版本要更加成熟.目前.net core具有开源.跨平台.灵活部署.模块化架构等等特性,吸引着一 ...