本文主要记录guava_cache的学习心得!

缓存是什么?为何要用缓存呢?

先参考下图!

这是一张小白图!简单形容了一个普普通通的服务端请求的处理模型! 当一个request请求通过网络不远千里的来到我们的机房! 首先nginx会给它找到一个合适的处理窗口,也就是我们的jvm进程,当jvm在进程空间内没有找到请求的结果,会尝试去分布式缓存中获取!如果分布式缓存中任然没有命中,再尝试去数据库中获取!

在上图中,a,b,c,d耗时逐渐增加,而且是跳跃式的增加!DB作为稳定性,安全性最值得依赖的存储空间,作为保障放在了最后一关!请求到数据库,需要通过漫长的网络和大量的磁盘io才能找到正确的数据!而又因为磁盘的io速度远远小于内存io,所以出现了分布式缓存!可是访问分布式缓存又隔着一层网络io,于是进程内部缓存应运而生!

以上三种角色,没有优劣之分,都在我们的系统中,承担了不可替代的重要角色!

讲到这里可以粗糙的理解为:缓存就是我们进程空间内部一片存储空间,是为了数据可以被进程更快速访问!

这里很多人会疑问,那实例个HashMap不就可以实现吗? 干嘛要提guava-cache!

这里就要说一下,机器的内存资源是十分宝贵的,内存相比磁盘的特点就是小而快!如果随便定义一块进程内的存储空间当cache,当原来越多的数据被放入,内存就又了爆掉的风险!而为这块进程内部存储空间包上了一层管理手段,使进程更合理更安全高效的使用内存,就是我们的缓存技术!为了更高效安全的使用内存,诸多缓存技术被开发出来,guava cache就是其中之一!

下面会从代码,去学习guava cache的使用和实现!

1  构建 (使用lodingcache作为示例)

guava cache的构建使用了简单易懂的构建者模式!核心参数如图:

maximumSize 最大存储数量,防止内存被过度使用而爆掉 
expireAfterAccess  最后获取后的过期时间,无效数据剔除!
expireAfterWrite  写入后的过期时间,防止数据累积过多!同 expireAfterAccess 可二选一!
removalListener 数据移除时的监听器!
CacheLoader 数据加载器! 核心方法load(key), 在内存中没有获取到数据时会调用,将数据加载到cache中!
maximumSize ,expireAfterAccess ,expireAfterWrite 保证了内存空间使用的安全合理!
removalListener  和 CacheLoader 则提供了多样化的cache使用方式!

2 get方法 (核心方法)

可见,loadingCache的实现是LocalCache的静态内部类LocalLoadingCache,调用getOrLoad(key)来获取value!getOrLoad内部调用get(key,loader)方法!

在get方法内部,可以看到,guava cache使借鉴了jdk 1.8版本之前的ConcurrentHashMap,使用segment分段的方式,保证在多线程情景下的高效安全访问!

上图的get方法实现中可以看到!首先会判断 缓存中元素数量!

如果缓存中有数据,count!=0,通过key和hash获取value,可见getEntry() -->getFirst(hash) ,其结构同jdk HashMap原理,使用 AtomicReferenceArray  作为table数组,根据hash定位下表,获取到目标链表,然后遍历链表,比对key值,尝试获取value!

如果获取到value,会使用recordRead方法记录当前时间戳为最后获取此key的时间,读取此key的次数+1!在返回value时,会调用 scheduleRefresh()刷新任务方法返回!这里也可以看到cache包装的强大功能,根据当前时间now减value的写入时间戳的结果是否大于刷新时间间隔,来判断是否需要刷新!

是,则使用loader加载器加载新值,刷新缓存并返回!否,则旧值返回!

如果缓存中没有数据,count==0 ,也会执行 lockedGetOrLoad()方法!如下:

依然是根据hash,定位到index下标,如果找到下标,则循环遍历链表,比对key值,获取value!其中,会判断获取到的value,如果是null,塞入队列,等待异步清除!

如果不是null,也要判断是否过期,在过期的情况下,依然会塞入异步清除队列!

如果没有获取到value的情况下,判断是否需要创建新的entry,既createNewEntry==true,会创建一个弱引用或软引用,LocalCache.LoadingValueReference(),放进table中,最后调用load()方法,为该引用加载数据!

注:判断数据是否过期的方法isExpired()

如果设置的是写入后过期,则拿当前时间减写入时间和过期时间比较大小,

如果是最后一次获取后过期,则拿当前时间减最后一次access时间 和过期时间比较大小!

其上为cache的核心方法get()的大概实现!

总价概括为:cache使用了segment分段锁控制了并发写入,使用了数组+hash+链表的数据结构实现了高效读写,数组是juc大神dog.lea写的原子性安全读写的AtomicReferenceArray,使用了引用队列存放key,value,保证了gc回收及时!recencyQueue  和 accessQueue (前者是读取是写入,后者是加锁状态下的写都会写入)保证了回收算法的异步高效实现!

由此可见,内存的使用,在服务端是一件严谨甚至严苛的事,使用得当,会给服务的效率带来极大的提升!使用不得当,内存的安全,进程的生命,就会立即受到致命的威胁!

本地缓存技术,越来越被重视。以下为几种常见cache技术的特性:

1 ehcache,使用简单,访问效率高,轻量接入, 适合数据更新少,并发低的场景下

2 guava cache  功能强大,配置多,支持三种淘汰策略,支持刷新,移除,记数等功能,支持弱引用 软引用, 适合有一定的内存空间的条件,频繁读写场景下!

3 jetcache  阿里开源,springboot支持,亲redis,多级缓存使用良好!

4 caffeine是guava的升级款,也是最近被主推的cache!使用方式同guava区别不大,但是性能上提升很大!

 

cache之guava的更多相关文章

  1. [Java 缓存] Java Cache之 Guava Cache的简单应用.

    前言 今天第一次使用MarkDown的形式发博客. 准备记录一下自己对Guava Cache的认识及项目中的实际使用经验. 一: 什么是Guava Guava工程包含了若干被Google的 Java项 ...

  2. Spring cache简单使用guava cache

    Spring cache简单使用 前言 spring有一套和各种缓存的集成方式.类似于sl4j,你可以选择log框架实现,也一样可以实现缓存实现,比如ehcache,guava cache. [TOC ...

  3. Ehcache与Guava Cache的区别浅谈

    最近在做一些缓存改造的场景,有如下一些经验总结: 缓存版本: Ehcache:2.8.3 Guava:17.0 Ehcache支持持久化到本地磁盘,Guava不可以: Ehcache有现成的集群解决方 ...

  4. (翻译)Google Guava Cache

    翻译自Google Guava Cache This Post is a continuation of my series on Google Guava, this time covering G ...

  5. SpringBoot学习笔记(6) SpringBoot数据缓存Cache [Guava和Redis实现]

    https://blog.csdn.net/a67474506/article/details/52608855 Spring定义了org.springframework.cache.CacheMan ...

  6. Spring Boot 揭秘与实战(二) 数据缓存篇 - Guava Cache

    文章目录 1. Guava Cache 集成 2. 个性化配置 3. 源代码 本文,讲解 Spring Boot 如何集成 Guava Cache,实现缓存. 在阅读「Spring Boot 揭秘与实 ...

  7. spring boot guava cache 缓存学习

    http://blog.csdn.net/hy245120020/article/details/78065676 ****************************************** ...

  8. Spring配置cache(concurrentHashMap,guava cache、redis实现)附源码

    在应用程序中,数据一般是存在数据库中(磁盘介质),对于某些被频繁访问的数据,如果每次都访问数据库,不仅涉及到网络io,还受到数据库查询的影响:而目前通常会将频繁使用,并且不经常改变的数据放入缓存中,从 ...

  9. Guava学习

    Guava学习笔记目录 Guava 是一个 Google 的基于java1.6的类库集合的扩展项目,包括 collections, caching, primitives support, concu ...

随机推荐

  1. 文本编辑_Vim&Vi

    一.Vim.Vi文本编辑器 1️⃣:vi: Visual Interface,文本编辑器 2️⃣:文本:ASCII, Unicode 3️⃣:VIM - Vi IMproved 二.Vim.vi的特点 ...

  2. 10.16-17 mailq&mail:显示邮件传输队列&发送邮件

    mailq命令 是mail queue(邮件队列)的缩写,它会显示待发送的邮件队列,显示的条目包括邮件队列ID.邮件大小.加入队列时间.邮件发送者和接受者.如果邮件进行最后一次尝试后还没有将邮件投递出 ...

  3. redis中AOF和RDB的关闭方法

    redis中AOF和RDB的关闭方法   问题:当往redis中导入数据时,有时会出现redis server went away的情况: 原因: 导入的数据量太大,而内存不够(即内存1G,但数据有2 ...

  4. Go语言的函数03---返回值

    package main import "fmt" /*无返回值*/ func Sub1(a, b int) { ret := a - b fmt.Println("a- ...

  5. 针对Spring MVC的Interceptor内存马

    针对Spring MVC的Interceptor内存马 目录 针对Spring MVC的Interceptor内存马 1 基础拦截器和调用流程的探索 1.1 基础拦截器 1.2 探索拦截器的调用链 1 ...

  6. Resnet-50网络结构详解

    解决的问题: 由于梯度消失,深层网络很难训练.因为梯度反向传播到前面的层,重复相乘可能使梯度无穷小.结果就是,随着网络的层数更深,其性能趋于饱和,甚至迅速下降. 核心思想: 引入一个恒等快捷键(也称之 ...

  7. SparkCore之业务操作逻辑

    在上spark的时候,一开始需要虚拟机模拟真实环境,而spark主要的三种模式:local.standalone.yarn 均可以通过虚拟机模拟. 这里要讨论的是业务逻辑如何和 spark 结合,具体 ...

  8. postman实现参数化执行及断言处理

    一.假设需要做的测试的参数如下: 注意保存为.csv文件时一定要选择格式为UTF-8 ,避免乱码. 二.输入参数和期望结果在postman中的用法: 注意一定要通过runner的方式进行运行,选择对应 ...

  9. ApplicationListener接口,在spring容器初始化后执行的方法

    一.如果我们希望在Spring容器将所有的Bean都初始化完成之后,做一些操作,那么就可以使用ApplicationListener接口,实现ApplicationListener接口中的onAppl ...

  10. 性能监控工具之Grafana+Prometheus+Exporters

    在本模块中,我将把几个常用的监控部分给梳理一下.前面我们提到过,在性能监控图谱中,有操作系统.应用服务器.中间件.队列.缓存.数据库.网络.前端.负载均衡.Web 服务器.存储.代码等很多需要监控的点 ...