且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

Guava Cache缓存设计原理(上)

更新时间:2022-06-09 00:51:02

Google开源的Java重用工具集库Guava里的一款缓存工具,实现的缓存功能:


  • 自动将entry节点加载进缓存结构
  • 当缓存的数据超过设置的最大值时,使用LRU移除
  • 具备根据entry节点上次被访问或者写入时间计算它的过期机制
  • 缓存的key被封装在WeakReference引用内
  • 缓存的Value被封装在WeakReference或SoftReference引用内
  • 统计缓存使用过程中命中率、异常率、未命中率等统计数据


Guava Cache架构设计源于ConcurrentHashMap,简单场景下可自行编码通过HashMap做少量数据的缓存。

但若结果可能随时间改变或希望存储的数据空间可控,***自己实现这种数据结构。


使用多个segments方式的细粒度锁,在保证线程安全的同时,支持高并发场景需求。

Cache类似Map,是存储K.V对的集合,不过它还需处理evict、expire、dynamic load等算法逻辑,需要一些额外信息实现这些操作,需做方法与数据的关联封装。

Guava Cache数据结构

Guava Cache缓存设计原理(上)

Cache由多个Segment组成,而每个Segment包含一个ReferenceEntry数组:

volatile @MonotonicNonNull AtomicReferenceArray<ReferenceEntry<K, V>> table;

每个ReferenceEntry数组项都是一条ReferenceEntry链。

Guava Cache缓存设计原理(上)

一个ReferenceEntry包含key、hash、value Reference、next字段,除了在ReferenceEntry数组项中组成的链。


ReferenceEntry可以是强引用类型的key,也可以WeakReference类型的key,为了减少内存使用量,还可以根据是否配置了expireAfterWrite、expireAfterAccess、maximumSize来决定是否需要write链和access链确定要创建的具体Reference:StrongEntry、StrongWriteEntry、StrongAccessEntry、StrongWriteAccessEntry等。

ReferenceEntry接口

  • reference map中的节点

Guava Cache缓存设计原理(上)

map中节点可处于如下状态:
有效的:

  • Live:设置了有效的键/值
  • Loading:加载中

无效的:

  • Expired:时间已过(键/值仍可设置)
  • Collected:键/值已部分收集,但尚未清理
  • Unset:标记为未设置,等待清理或重用

ValueReference

Value的引用Guava Cache缓存设计原理(上)

之所以用Reference命令,是因为Cache要支持:

  • WeakReference K
  • SoftReference、WeakReference V


ValueReference 因为Cache支持强引用的Value、SoftReference Value以及WeakReference Value,因而它对应三个实现类:StrongValueReference、SoftValueReference、WeakValueReference。

LoadingValueReference

为支持动态加载机制,它还有一个LoadingValueReference,需动态加载一个key的值时:


  • 先把该值封装在LoadingValueReference,以表达该key对应的值已经在加载
  • 若其它线程也要查询该key对应的值,就能得到该引用,并且等待该值加载完成,以保证该值只被加载一次
  • 在该值加载完成后,将LoadingValueReference替换成其他ValueReference类型


ValueReference对象中会保留对ReferenceEntry的引用,这是因为在Value因为WeakReference、SoftReference被回收时,需要使用其key将对应的项从Segment的table中移除。

Queue

在一个Segment中,所有ReferenceEntry还组成:

  • access链(accessQueue)
  • write链(writeQueue)


为了实现LRU,Guava Cache在Segment中添加的这两条链,都是双向链表,通过ReferenceEntry中的如下链接而成:


  • previousInWriteQueue
  • nextInWriteQueue
  • previousInAccessQueue
  • nextInAccessQueue


WriteQueue

Guava Cache缓存设计原理(上)

AccessQueue

Guava Cache缓存设计原理(上)

WriteQueue和AccessQueue都自定义了offer、peek、remove、poll等操作:

Guava Cache缓存设计原理(上)

offer操作,若是:

  • 新增节点
    直接加到该链的结尾
  • 已存在的节点
    将该节点链接的链尾


remove操作:

直接从该链中移除该节点

poll操作:

将头节点的下一个节点移除,并返回。