应用场景
- 你愿意花些内存来提高应用的速度
- 你所要查询的数据需要花较多时间查询且需要经常查
- 缓存数据所需要的内存不大于应用的内存
数据加载
From a CacheLoader
当缓存不存在时,通过调用loader的load方法去加载缓存
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());
}
LoadingCache的get(k)方法会抛个检查性异常,若不需要检查性异常可使用getUnchecked(k)方法,其如果有异常,会抛出个运行时异常。
From a Callable
没有提供load方法,其是通过get(K, Callable<V>)方式处理,当K缓存不存在时,调用Callable方法加载,若缓存存在,则不需要调用Callable。
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());
}
Inserted Directly
直接调用cache.put(key, value)
缓存过期
Size-based Eviction
CacheBuilder.maximumSize(long) 限制缓存的个数,当超过这个大小的时候,The cache will try to evict entries that haven't been used recently or very often
Timed Eviction
- expireAfterAccess(long, TimeUnit) 最近有多久没被访问后, 缓存将被驱逐
- expireAfterWrite(long, TimeUnit) 从创建或覆盖缓存起,超过多久后缓存将被驱逐
Explicit Removals 删除缓存
- individually, using Cache.invalidate(key)
- in bulk, using Cache.invalidateAll(keys)
- to all entries, using Cache.invalidateAll()
Removal Listeners
当缓存被删除时,将会触发监听
CacheLoader<Key, DatabaseConnection> loader = new CacheLoader<Key, DatabaseConnection> () {
public DatabaseConnection load(Key key) throws Exception {
return openConnection(key);
}
};
RemovalListener<Key, DatabaseConnection> removalListener = new RemovalListener<Key, DatabaseConnection>() {
public void onRemoval(RemovalNotification<Key, DatabaseConnection> removal) {
DatabaseConnection conn = removal.getValue();
conn.close(); // tear down properly
}
};
return CacheBuilder.newBuilder()
.expireAfterWrite(2, TimeUnit.MINUTES)
.removalListener(removalListener)
.build(loader);
默认的,触发监听是同步执行的,若监听执行的时间不可忍受,可使用异步的方式RemovalListeners.asynchronous(RemovalListener, Executor)
Refresh
使用LoadingCache.refresh(K)来刷新缓存, 刷新运作可能会是异步的, 所以在刷新没完全前,你取到的数据还是有可能是旧的.若在刷新时发生异常,将还是使用旧的缓存. 刷新运作也可在配置缓存的时候使用自动刷新操作CacheBuilder.refreshAfterWrite(long, TimeUnit)
// 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;
}
}
});
其他
分析
可通过CacheBuilder.recordStats()来打开分析的功能. Cache.stats()返回一个CacheStats对象,该对象提供了一些有用的分析方法:
- hitRate(), which returns the ratio of hits to requests
- averageLoadPenalty(), the average time spent loading new values, in nanoseconds
- evictionCount(), the number of cache evictions