// 获取最新 db 数据更新缓存
testDemoCache.refresh(key);
// 清理 guava cache 缓存
estDemoCache.invalidateAll();
自动清理缓存
手动清理缓存,人力成本较高,现在有一种方案是,在新增/修改数据后,通过 MQ 广播来实现所有机器自动清理缓存。或通过 canal 等 binlog 中间件监听 db 变更来清理缓存。
Guava cache 原理解析
Guava cache 继承了 ConcurrentHashMap 的思路,使用多个 segments 方式的细粒度锁,在保证线程安全的同时,支持高并发场景需求。下面介绍它的底层实现,分析其性能优异的原因
几个重要的组件
1、CacheBuilder 缓存构建器。构建缓存的入口,指定缓存配置参数并初始化本地缓存。采用 Builder 设计模式提供了设置好各种参数的缓存对象。 2、LocalCache 数据结构。缓存核心类 LocalCache 数据结构与 ConcurrentHashMap 很相似,由多个 segment 组成,且各 segment 相对独立,互不影响,所以能支持并行操作,每个 segment 由一个 table 和若干队列组成。缓存数据存储在 table 中,其类型为AtomicReferenceArray。
上图为 Guava cache 底层的数据结构图,下表对其结构做了说明
| 序号 | 数据结构 | 特点 | 详解 |
| — | — | — | — |
| 1 | Segment<K, V>[] segments | Segment 继承于ReetrantLock,减小锁粒度,提高并发效率 | |
| 2 | AtomicReferenceArray<ReferenceEntry<K, V>> table | 类似于HasmMap 中的table 一样,相当于 entry 的容器 | |
| 3 | ReferenceEntry<K, V> referenceEntry | 基于引用的Entry,其实现类有弱引用 Entry,强引用 Entry 等 | Cache 由多个 Segment 组成,而每个 Segment 包含一个ReferenceEntry 数组,每个 ReferenceEntry 数组项都是一条 ReferenceEntry 链,且一个 ReferenceEntry 包含key、hash、valueReference、next 字段。除了在ReferenceEntry 数组项中组成的链,在一个 Segment中,所有 ReferenceEntry 还组成 access 链(accessQueue)和 write 链(writeQueue)。ReferenceEntry 可以是强引用类型的 key,也可以WeakReference 类型的 key,为了减少内存使用量,还可以根据是否配置了 expireAfterWrite、expireAfterAccess、maximumSize 来决定是否需要 write 链和 access 链确定要创建的具体 Reference:StrongEntry、StrongWriteEntry、StrongAccessEntry、StrongWriteAccessEntry等 |
| 4 | ReferenceQueue keyReferenceQueue | 已经被 GC,需要内部清理的键引用队列 | |
| 5 | ReferenceQueue valueReferenceQueue | 已经被 GC,需要内部清理的值引用队列 | 因为 Cache 支持强引用的 Value、SoftReference Value 以及 WeakReference Value,因而它对应三个实现类:StrongValueReference、SoftValueReference、WeakValueReference。为了支持动态加载机制,它还有一个 LoadingValueReference,在需要动态加载一个 key的值时,先把该值封装在 LoadingValueReference 中,以表达该 key 对应的值已经在加载了,如果其他线程也要查询该 key 对应的值,就能得到该引用,并且等待改值加载完成,从而保证该值只被加载一次,在该值加载完成后,将 LoadingValueReference 替换成其他 ValueReference类型。ValueReference 对象中会保留对 ReferenceEntry 的引用,这是因为在 Value 因为 WeakReference、SoftReference 被回收时,需要使用其 key 将对应的项从Segment 的 table 中移除 |
| 6 | Queue<ReferenceEntry<K, V>> recencyQueue | 记录升级可访问列表清单时的entries ,当segment 上达到临界值或发生写操作时该队列会被清空 | |
| 7 | Queue<ReferenceEntry<K, V>>