Guava Cache

Guava是Google提供的一套Java工具包,而Guava Cache是一套非常完善的本地缓存机制(JVM缓 存)。 Guava cache的设计来源于CurrentHashMap,可以按照多种策略来清理存储在其中的缓存值且保持很高的并发读写性能。

GuavaCache、Tair、EVCache、Aerospike比较

应用场景:

  • 对性能有非常高的要求
  • 不经常变化
  • 占用内存不大
  • 有访问整个集合的需求
  • 数据允许不时时一致

 Cuava Cache优势:

  •  缓存过期和淘汰机制,采用LRU的方式,将不常使用的键值从Cache中删除
  • 并发处理能力,GuavaCache类似CurrentHashMap,是线程安全的。提供了设置并发级别的api,使得缓存支持并发的写入和读取,分离锁是分拆锁定,把一个集合看分成若干partition, 每个partiton一把锁。ConcurrentHashMap 就是分了16个区域,这16个区域之间是可以并发的。GuavaCache采用Segment做分区。
  • 更新锁定,一般情况下,在缓存中查询某个key,如果不存在,则查源数据,并回填缓存。(Cache Aside Pattern) 在高并发下会出现,多次查源并重复回填缓存,可能会造成源的宕机(DB),性能下降 。GuavaCache可以在CacheLoader的load方法中加以控制,对同一个key,只让一个请求去读源并,回填缓存,其他请求阻塞等待。
  • 集成数据源,一般我们在业务中操作缓存,都会操作缓存和数据源两部分,而GuavaCache的get可以集成数据源,在从缓存中读取不到时可以从数据源中读取数据并回填缓存
  • 监控缓存加载/命中情况用作统计业务

Cuava Cache核心原理 

Guava Cache的数据结构和CurrentHashMap相似,最基本的区别是 ConcurrentMap会一直保存所有添加的元素,直到显式地移除。 相对地,Guava Cache为了限制内存占用,通常都设定为自动回收元素。

  • LocalCache为Guava Cache的核心类,包含一个Segment数组组成 
  • Segement数组的长度决定了cache的并发数
  • 每一个Segment使用了单独的锁,其实每个Segment继承了ReentrantLock,对Segment的写操 作需要先拿到锁
  • 每个Segment由一个table和5个队列组成
  • 5个队列:ReferenceQueue keyReferenceQueue : 已经被GC,需要内部清理的键引用队列,ReferenceQueue valueReferenceQueue : 已经被GC,需要内部清理的值引用队列,ConcurrentlinkedQueue> recencyQueue : LRU队列,当segment上达到 临界值发生写操作时该队列会移除数据,Queue> writeQueue:写队列,按照写入时间进行排序的元素队列,写入 一个元素时会把它加入到队列尾部,Queue> accessQueue:访问队列,按照访问时间进行排序的元素队列, 访问(包括写入)一个元素时会把它加入到队列尾部
  • AtomicReferenceArray> table:AtomicReferenceArray可以用原子方式 更新其元素的对象引用数组
  • ReferenceEntry<key,value>  ReferenceEntry是Guava Cache中对一个键值对节点的抽象,每个ReferenceEntry数组项都是一 条ReferenceEntry链。并且一个ReferenceEntry包含key、hash、valueReference、next字段 (单链) Guava Cache使用ReferenceEntry接口来封装一个键值对,而用ValueReference来封装Value值

GuavaCache核心原理之回收机制 

  • 基于容量回收:  在缓存项的数目达到限定值之前,采用LRU的回收方式
  • 定时回收 : expireAfterAccess:缓存项在给定时间内没有被读/写访问,则回收。回收顺序和基于大小回收一 样(LRU),定时回收 expireAfterAccess:缓存项在给定时间内没有被读/写访问,则回收。回收顺序和基于大小回收一 样(LRU)
  • 基于引用回收:通过使用弱引用的键、或弱引用的值、或软引用的值,Guava Cache可以垃圾回收

GuavaCache构建的缓存不会"自动"执行清理和回收工作,也不会在某个缓存项过期后马上清理,也没 有诸如此类的清理机制。 GuavaCache是在每次进行缓存操作的时候,惰性删除 如get()或者put()的时候,判断缓存是否过期 

Cuava Cache并发操作

1,并发的设置

GuavaCache通过设置 concurrencyLevel 使得缓存支持并发的写入和读取,concurrencyLevel=Segment数组的长度,同ConcurrentHashMap类似Guava cache的并发也是通过分离锁实现

LoadingCache<String,Object> cache = CacheBuilder.newBuilder()
// 最大3个 同时支持CPU核数线程写缓存
.maximumSize(3).concurrencyLevel(Runtime.getRuntime().availableProcessors()).bui
ld();

V get(K key, CacheLoader<? super K, V> loader) throws ExecutionException {
int hash = this.hash(Preconditions.checkNotNull(key));
//通过hash值确定该key位于哪一个segment上,并获取该segment
return this.segmentFor(hash).get(key, hash, loader);
}

LoadingCache采用了类似ConcurrentHashMap的方式,将映射表分为多个segment。segment之间 可以并发访问,这样可以大大提高并发的效率,使得并发冲突的可能性降低了。

2,更新锁定

GuavaCache提供了一个refreshAfterWrite定时刷新数据的配置项

如果经过一定时间没有更新或覆盖,则会在下一次获取该值的时候,会在后台异步去刷新缓存刷新时只有一个请求回源取数据,其他请求会阻塞(block)在一个固定时间段,如果在该时间段内没 有获得新值则返回旧值。<防止都去访问数据库,回圆导致缓存击穿>

 3,网站首页案例(访问更多,并发访问明显)

面试问题:

1,GuavaCache会oom(内存溢出)吗?

会,当我们设置缓存永不过期(或者很长),缓存的对象不限个数(或者很大)时,不断向GuavaCache加入大字符串,最终将会oom。

解决方案:缓存时间设置相对小些,使用弱引用方式存储对象

2,GuavaCache缓存到期就会立即清除吗?

不是的,GuavaCache是在每次进行缓存操作的时候,如get()或者put()的时候,判断缓存是否过期,一个如果一个对象放入缓存以后,不在有任何缓存操作(包括对缓存其他key的操作),那么该缓存不 会主动过期的。

3,GuavaCache如何找出最久未使用的数据?

用accessQueue,这个队列是按照LRU的顺序存放的缓存对象(ReferenceEntry)的。会把访问过的对象放到队列的最后。并且可以很方便的更新和删除链表中的节点,因为每次访问的时候都可能需要更新该链表,放入到链表 的尾部。这样,每次从access中拿出的头节点就是最久未使用的。对应的writeQueue用来保存最久未更新的缓存队列,实现方式和accessQueue一样。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值