本文主要记录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. Mysql不知道默认密码情况下登录/重置/忘记密码

    场景一: 基础系统:linux 镜像:LAMP环境(Ubuntu 18.04 Apache PHP7.0) 问题:ERROR 1405 (28000): Access denied for user ...

  2. MyBatis 缓存机制(十三)

    什么是缓存 缓存就是内存中的一个对象,用于对数据库查询结果的保存,用于减少与数据库的交互次数从而降低数据库的压力,进而提高响应速度. MyBatis 缓存机制原理 Mybatis 缓存机制原理是将第一 ...

  3. 并发王者课-青铜7:顺藤摸瓜-如何从synchronized中的锁认识Monitor

    在前面的文章中,我们已经体验过synchronized的用法,并对锁的概念和原理做了简单的介绍.然而,你可能已经察觉到,有一个概念似乎总是和synchronized.锁这两个概念如影相随,很多人也比较 ...

  4. 启动dubbo消费端过程提示No provider available for the service的问题定位与解决

    文/朱季谦 某次在启动dubbo消费端时,发现无法从zookeeper注册中心获取到所依赖的消费者API,启动日志一直出现这样的异常提示 Failed to check the status of t ...

  5. redux 源码浅析

    redux 源码浅析 redux 版本号: "redux": "4.0.5" redux 作为一个十分常用的状态容器库, 大家都应该见识过, 他很小巧, 只有 ...

  6. ThinkPHP无限级分类(递归)

    代码演示 没什么可说的直接看代码 <?php namespace app\controller; class Category { //模拟假数据 protected static functi ...

  7. Go语言网络通信---多用户连续通信的TCP编程

    server端: package main import ( "fmt" "net" ) func main() { //建立监听 listener, err ...

  8. Ajax|看这一篇就够了!详解Ajax工作原理及开发步骤

    传统开发的缺点,是对于浏览器的页面,全部都是全局刷新的体验.如果我们只是想取得或是更新页面中的部分信息那么就必须要应用到局部刷新的技术. 局部刷新也是有效提升用户体验的一种非常重要的方式. Ajax技 ...

  9. toFixed奇葩问题

    1.浮点数运算后的精度问题 在计算商品价格加减乘除时,偶尔会出现精度问题 // 加法 ===================== 0.1 + 0.2 = 0.30000000000000004 0.7 ...

  10. NVIDIA A100 GPUs上硬件JPEG解码器和NVIDIA nvJPEG库

    NVIDIA A100 GPUs上硬件JPEG解码器和NVIDIA nvJPEG库 Leveraging the Hardware JPEG Decoder and NVIDIA nvJPEG Lib ...