深入分布式缓存-从原理到实践(二)
规划定义
JSR
JSR是java Specification Requests的缩写,是java定义的一种对java对象临时在内存中进行缓存的方法,包括对象的创建\访问\失效\一致性等
缓存常用方法
| 接口 | 说明 | Ehcache | Guava |
|---|---|---|---|
| put() | 添加缓存 | ✔ | ✔ |
| putIfAbsent() | 如果key不存在则添加缓存 | ✔ | ✔ |
| replace() | 如果key存在则替换缓存 | ✔ | ✔ |
| get() | 获取缓存 | ✔ | ✔ |
| getAll() | 获取所有缓存 | ✔ | ✔ |
| getAllPresent(key) | 存在key在进行加载 | ❌ | ✔ |
| putAll(entries) | 添加所有缓存 | ✔ | ✔ |
| keySet() | 获取所有key | ❌ | ❌ |
| remove() | 移除缓存 | ✔ | ✔ |
| clear() | 清空缓存 | ✔ | ✔ |
可以看到常见的缓存操作就三类:添加<B>获取<B>移除
下面介绍两类缓存的实现方式:本地缓存和分布式缓存
本地缓存
Ehcache
暂且将Ehcache划分到本地缓存中,因为在使用中大多数场景下还是将Ehcache作为本地缓存来进行使用
Ehcache2的核心淘汰策略逻辑如下:

AbstractPolicy.selectedBasedOnPolicy
- compare
/**
* Compares the desirableness for eviction of two elements
*
* @param element1 the element to compare against
* @param element2 the element to compare
* @return true if the second element is preferable for eviction to the first element
* under ths policy
*/
boolean compare(Element element1, Element element2);
可以看到compare()方法是核心的比较方法,下层有LRUPolicy<B>LFUPolicy<B>FIFOPolicy底层实现
- LruPolicy
// 最近最少使用算法
public boolean compare(Element element1, Element element2) {
// 比较最后访问时间
return element2.getLastAccessTime() < element1.getLastAccessTime();
}
- LFU
// 最近最少使用算法
public boolean compare(Element element1, Element element2) {
// 比较访问次数
return element2.getHitCount() < element1.getHitCount();
}
- FIFO
// 最近最少使用算法
public boolean compare(Element element1, Element element2) {
// 比较创建和更新时间
return element2.getLatestOfCreationAndUpdateTime() < element1.getLatestOfCreationAndUpdateTime();
}
Guava Cache
Guava Cache是Google对java集合的一种封装来实现缓存功能;
Guava Cache 提供缓存的失效时间和定时更新功能,下面介绍Guava Cache定时更新方法
- scheduleRefresh()
V scheduleRefresh(
ReferenceEntry<K, V> entry,
K key,
int hash,
V oldValue,
long now,
CacheLoader<? super K, V> loader) {
if (map.refreshes()//map.refreshes()判断是否存在过期时间
&& (now - entry.getWriteTime() > map.refreshNanos)//判断是否已经过期
&& !entry.getValueReference().isLoading()) {//判断是否当前正在加载新值
V newValue = refresh(key, hash, loader, true);//重新加载数据
if (newValue != null) {
return newValue;
}
}
return oldValue;
}
scheduleRefresh()方法是在get()方法中调用的
LocalCache.scheduleRefresh
Caffine
二级缓存
在使用集中式或者数据库热点配置数据时,我们往往将这些数据放到应用进程空间中中,这样可以提高缓存的命中率;可以用Echache/Guava作为二级缓存来进行使用
下面介绍两种常用的设计方案:
- 定时轮询

- 消息通知

消息通知的方案可以保证准实时下的推送,但是会带来一定的开销,比如消息通知的推送频率是每秒一次,那么每秒钟的消息会被推送到缓存中,这样会带来一定的开销,通过这样的消耗来保证实时性;还需要注意一点的是应用的重启后一定要消费最新的消息或从redis中获取最新的数据