背景
guava cache是google工具包中提供的關(guān)于本地緩存解決方案,提供了基于容量骨杂,時(shí)間和引用的緩存回收方式。
容量:利用LRU算法雄卷,
回收:Java虛擬機(jī)的垃圾回收機(jī)制(堆緩存)搓蚪。
使用場景
愿意花費(fèi)一部分內(nèi)存來提高速度 -- 以空間換時(shí)間
期待有些關(guān)鍵字會(huì)被多次查詢 -- 熱點(diǎn)數(shù)據(jù)
緩存并不需要存儲(chǔ)比RAM中更多的數(shù)據(jù)。Guava caches是一次性運(yùn)行的本地緩存丁鹉,并不會(huì)把數(shù)據(jù)存儲(chǔ)到文件中或者外部服務(wù)器上 -- 不能持久化
功能
創(chuàng)建本地緩存
a.CacheLoader
/**
* CacheLoader 當(dāng)檢索不存在的時(shí)候妒潭,會(huì)自動(dòng)的加載信息的悴能!
*/
private static LoadingCache<String, String> loadingCache = CacheBuilder
.newBuilder()
.maximumSize(2)
.expireAfterWrite(10, TimeUnit.SECONDS)
.concurrencyLevel(2)
.recordStats()
.build(new CacheLoader<String, String>() {
@Override
public String load(String key) throws Exception {
String value = map.get(key);
log.info(" load value by key; key:{},value:{}", key, value);
return value;
}
});
public static String getValue(String key) {
try {
return loadingCache.get(key);
} catch (Exception e) {
log.warn(" get key error ", e);
return null;
}
}
b.Callable
private static Cache<String, String> cacheCallable = CacheBuilder
.newBuilder()
.maximumSize(2)
.expireAfterWrite(10, TimeUnit.SECONDS)
.concurrencyLevel(2)
.recordStats()
.build();
/**
* Callable 如果有緩存則返回;否則運(yùn)算雳灾、緩存漠酿、然后返回
*/
public static String getValue1(String key) {
try {
return cacheCallable.get(key, new Callable<String>() {
@Override
public String call() throws Exception {
String value = map.get(key);
log.info(" load value by key; key:{},value:{}", key, value);
return value;
}
});
} catch (Exception e) {
log.warn(" get key error ", e);
return null;
}
}
顯示插入-顯示清除
public static void put(String key, String value){
loadingCache.put(key,value); //手動(dòng)添加值
}
public static void remove(String key){
loadingCache.invalidate(key); //移除一個(gè)
loadingCache.invalidateAll(Lists.newArrayList(key)); // 批量移除
loadingCache.invalidateAll(); // 移除全部-清空
}
移除監(jiān)聽器
a.同步
private static LoadingCache<String, String> loadingCache = CacheBuilder
.newBuilder()
.maximumSize(2)
.expireAfterWrite(10, TimeUnit.SECONDS)
.concurrencyLevel(2)
.recordStats()
.removalListener(new RemovalListener<String, String>() { // sync
@Override
public void onRemoval(RemovalNotification<String, String> removal) {
// TODO remove notification
log.info("loadingCache is removed. key:{},value:{}",removal.getKey(),removal.getValue());
}
})
.build(new CacheLoader<String, String>() {
@Override
public String load(String key) throws Exception {
String value = map.get(key);
log.info(" load value by key; key:{},value:{}", key, value);
return value;
}
});
b.異步
// 創(chuàng)建一個(gè)監(jiān)聽器
private static class MyRemovalListener implements RemovalListener<String,String>{
@Override
public void onRemoval(RemovalNotification<String, String> removal) {
// TODO remove notification
log.info("loadingCache is removed. key:{},value:{}",removal.getKey(),removal.getValue());
}
}
private static RemovalListener<String, String> async = RemovalListeners.asynchronous(new MyRemovalListener(), Executors.newSingleThreadExecutor());
private static Cache<String, String> cacheCallable = CacheBuilder
.newBuilder()
.maximumSize(2)
.expireAfterWrite(10, TimeUnit.SECONDS)
.concurrencyLevel(2)
.recordStats()
.removalListener(async)
.build();
為什么使用異步,不需要解釋了吧谎亩!
統(tǒng)計(jì)
//先開啟統(tǒng)計(jì)
private static Cache<String, String> cacheCallable = CacheBuilder
.newBuilder()
.maximumSize(2)
.expireAfterWrite(10, TimeUnit.SECONDS)
.concurrencyLevel(2)
.recordStats()
.removalListener(async)
.recordStats()//開啟統(tǒng)計(jì)
.build();
//獲取統(tǒng)計(jì)信息
CacheStats stats = cacheCallable.stats();
>>
public final class CacheStats {
private final long hitCount;
private final long missCount;
private final long loadSuccessCount;
private final long loadExceptionCount;
private final long totalLoadTime;
private final long evictionCount;
...
緩存回收方式
a. 基于容量回收
maximumSize(long):當(dāng)緩存中的元素?cái)?shù)量超過指定值時(shí)炒嘲。
b. 定時(shí)回收
expireAfterAccess(long, TimeUnit):緩存項(xiàng)在給定時(shí)間內(nèi)沒有被讀/寫訪問,則回收团驱。請(qǐng)注意這種緩存的回收順序和基于大小回收一樣摸吠。
expireAfterWrite(long, TimeUnit):緩存項(xiàng)在給定時(shí)間內(nèi)沒有被寫訪問(創(chuàng)建或覆蓋),則回收嚎花。如果認(rèn)為緩存數(shù)據(jù)總是在固定時(shí)候后變得陳舊不可用寸痢,這種回收方式是可取的。
如下文所討論紊选,定時(shí)回收周期性地在寫操作中執(zhí)行啼止,偶爾在讀操作中執(zhí)行。
c. 基于引用回收(Reference-based Eviction)
CacheBuilder.weakKeys():使用弱引用存儲(chǔ)鍵兵罢。當(dāng)鍵沒有其它(強(qiáng)或軟)引用時(shí)献烦,緩存項(xiàng)可以被垃圾回收。
CacheBuilder.weakValues():使用弱引用存儲(chǔ)值卖词。當(dāng)值沒有其它(強(qiáng)或軟)引用時(shí)巩那,緩存項(xiàng)可以被垃圾回收。
CacheBuilder.softValues():使用軟引用存儲(chǔ)值此蜈。軟引用只有在響應(yīng)內(nèi)存需要時(shí)即横,才按照全局最近最少使用的順序回收。
優(yōu)點(diǎn)
線程安全的緩存裆赵,與ConcurrentMap相似东囚,但前者增加了更多的元素失效策略,后者只能顯示的移除元素战授。
提供了三種基本的緩存回收方式:基于容量回收页藻、定時(shí)回收和基于引用回收。定時(shí)回收有兩種:按照寫入時(shí)間植兰,最早寫入的最先回收份帐;按照訪問時(shí)間,最早訪問的最早回收钉跷。
監(jiān)控緩存加載/命中情況弥鹦。
集成了多部操作,調(diào)用get方式,可以在未命中緩存的時(shí)候彬坏,從其他地方獲取數(shù)據(jù)源(DB朦促,redis),并加載到緩存中栓始。
缺點(diǎn)
Guava Cache的超時(shí)機(jī)制不是精確的务冕;
不能持久化本地緩存;
受限于服務(wù)器的內(nèi)存幻赚。
總結(jié)
那話說回來了禀忆,為什么要使用本地緩存呢?比IO更高效落恼,比分布式緩存更穩(wěn)定箩退。