源:https://github.com/google/guava/wiki/CachesExplained
適用性
緩存及其有用。blala
Cache類似于一個(gè)ConcurrentMap,但并不完全一樣。最基礎(chǔ)的不同是ConcurrentMap保存所有的元素知道它們被明確刪除。而Cache是會(huì)安排配置自動(dòng)刪除元素的以便于限制內(nèi)存占用。在某些用例中,LoadingCache即便不使用它的刪除功能,任然可以利用它的自動(dòng)加載功能。
加載數(shù)據(jù)
使用CacheLoader加載
LoadingCache是一個(gè)附加著CacheLoader的Cache。
在創(chuàng)建LoadingCache的時(shí)候傳入CacheLoader的實(shí)現(xiàn),主要是實(shí)現(xiàn)V load(K key) throws Exception方法,既怎么去加載一個(gè)key的值。
LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder()
.maximumSize(1000)
.build(
new CacheLoader<Key, Graph>() {
public Graph load(Key key) throws AnyException {
return createExpensiveGraph(key);
}
});
...
try {
return graphs.get(key);
} catch (ExecutionException e) {
throw new OtherException(e.getCause());
}
當(dāng)我們通過get(K)去查詢時(shí),會(huì)返回已經(jīng)被緩存的值,或者使用CacheLoader去 “原子性” 的加載一個(gè)新的值到緩存。因?yàn)?code>load方法本身會(huì)拋異常,所以get方法也會(huì)拋對(duì)應(yīng)的ExecutionException異常表示load失敗?;蛘咧苯邮褂?code>getUncheked(k)方法,所有異常都會(huì)被包裹成UncheckedException丟出。
當(dāng)使用loadAll加載大量key時(shí),如果遇到key加載,loadAll是迭代去取值的,如果需要更有效率的取法,自己實(shí)現(xiàn)。
使用Callable加載
CacheLoader是創(chuàng)建Cache的時(shí)候的默認(rèn)加載器,get(K,Callable<V>)則是get的時(shí)候單獨(dú)傳入一個(gè)針對(duì)本次調(diào)用的加載邏輯。
Cache<Key, Value> cache = CacheBuilder.newBuilder()
.maximumSize(1000)
.build(); // look Ma, no CacheLoader
...
try {
// If the key wasn't in the "easy to compute" group, we need to
// do things the hard way.
cache.get(key, new Callable<Value>() {
@Override
public Value call() throws AnyException {
return doThingsTheHardWay(key);
}
});
} catch (ExecutionException e) {
throw new OtherException(e.getCause());
}
直接手動(dòng)插入
cache.put(key, value)
刪除數(shù)據(jù)
根據(jù)容量刪除
如果對(duì)Cache的大小在意。創(chuàng)建的時(shí)候配置CacheBuilder.maxmumSize(long)配置緩存大小。
當(dāng)"接近容量"大小的時(shí)候會(huì)自動(dòng)刪除元素。元素也可以有Weight比重,但注意是在元素創(chuàng)建的時(shí)候計(jì)算出來靜態(tài)放置的。后面不會(huì)改變。
LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder()
.maximumWeight(100000)
.weigher(new Weigher<Key, Graph>() {
public int weigh(Key k, Graph g) {
return g.vertices().size();
}
})
.build(
new CacheLoader<Key, Graph>() {
public Graph load(Key key) { // no checked exception
return createExpensiveGraph(key);
}
});
根據(jù)時(shí)間刪除
2種策略,見名知意:
expireAfterAccess(long, TimeUnit)
expireAfterWrite(long, TimeUnit)
guava提供測(cè)試工具模擬時(shí)間流逝Ticker和CacheBuilder.ticker(Ticker)
根據(jù)引用刪除
最好使用配置明顯的Cache size去解決內(nèi)存大小,用折后在哪個(gè)策略,是依賴于JVM,而且會(huì)導(dǎo)致緩存使用==去代替'compare`在內(nèi)部邏輯中的比較。因?yàn)閖vm的判斷是基于引用的。
手動(dòng)刪除
Cache.invalidate(key)
Cache.invalidateAll(keys)
Cache.invalidateAll()
刪除的時(shí)候得到通知Removal Listeners
不要進(jìn)行耗性能和超時(shí)的動(dòng)作,如果需要,考慮RemovalListeners.asynchronous(RemovalListener,Executor)包裹。
刪除數(shù)據(jù)的時(shí)候到底發(fā)生了什么?
延遲刪除,只在讀和寫的時(shí)候才真正去做刪除維護(hù),所以remove listener才不能過長,因?yàn)槭峭綀?zhí)行的,會(huì)阻塞對(duì)應(yīng)的操作。需要頻繁刪除維護(hù),需要用戶自己去調(diào)用cleanUp().
刷新數(shù)據(jù)
LoadingCahe.refresh(K) 異步調(diào)用底層的CacheLoader.reload(K,V)刷新值。如果異常,保留原來的值,只打印日志。
自動(dòng)定時(shí)刷新可以在CacheBuilder中配置CacheBuilder.refreshAfterWrite(long,TimeUnit).
值得注意的是refreshAfterWrite僅僅只在到時(shí)后,標(biāo)記某個(gè)key可以被更新了,只有在查詢來臨的時(shí)候才會(huì)真正觸發(fā)更新。在同時(shí)有定時(shí)過期和定時(shí)更新的Cache上,只有在標(biāo)記的key在更新時(shí)間到后,查詢沒有來臨,更新未觸發(fā)。才會(huì)被過期策略考慮入刪除。
注意Guava提供的demo,CacheLoader.reload的實(shí)現(xiàn)不僅可以判斷哪些可以key應(yīng)該被更新,而且是異步的。
// Some keys don't need refreshing, and we want refreshes to be done asynchronously.
LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder()
.maximumSize(1000)
.refreshAfterWrite(1, TimeUnit.MINUTES)
.build(
new CacheLoader<Key, Graph>() {
public Graph load(Key key) { // no checked exception
return getGraphFromDatabase(key);
}
public ListenableFuture<Graph> reload(final Key key, Graph prevGraph) {
if (neverNeedsRefresh(key)) {
return Futures.immediateFuture(prevGraph);
} else {
// asynchronous!
ListenableFutureTask<Graph> task = ListenableFutureTask.create(new Callable<Graph>() {
public Graph call() {
return getGraphFromDatabase(key);
}
});
executor.execute(task);
return task;
}
}
});
統(tǒng)計(jì)功能
CacheBuilder.recordStats 開啟緩存的輔助統(tǒng)計(jì)功能。
Cache.stats方法可以返回一個(gè)CacheStats對(duì)象,包含很多統(tǒng)計(jì)信息。