以前我也没想过使用堆缓存或者使用堆外缓存,如果这对你是个知识盲区?
Guava Cache网上一搜有很多的教程,阐述了它的各种用法,我只是想将这些Guava Cache的知识压缩一下,连成一条线,轻松的记住。
场景
由于它是JVM内存级别的缓存,特点就是快,缺点嘛也多,内存有限,垃圾回收问题,分布式问题等等。
适合存放少量,高频访问的数据。
使用
存与读
创建一个Cache容器
1 2 3 4 5 6 7
   | private Cache<String, User> cache = CacheBuilder.newBuilder()         .build(new CacheLoader<String, User>() {             @Override             public User load(String key) throws Exception {                 return null;             }         });
   | 
 
想容器中存储数据
1
   | cache.put("apple", new User("apple", 1));
  | 
 
从Cache中读取数据
1 2 3 4
   | public void test1() {     User u = cache.getIfPresent("apple");     assertEquals(1, u.getAge()); }
  | 
 
刷新
之前在看亿级流量那本书里面提到了缓存用法中,有一种用法是Read-Through,意思是获取数据时,直接请求缓存,如果命中缓存则从缓存中返回,否则回源的数据源地址查询。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
   | LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder()        .maximumSize(1000)        .refreshAfterWrite(1, TimeUnit.MINUTES)        .build(             @Override            new CacheLoader<Key, Graph>() {              public Graph load(Key key) {                 return getGraphFromDatabase(key);              }             @Override              public ListenableFuture<Graph> reload(final Key key, Graph prevGraph) {                if (neverNeedsRefresh(key)) {                  return Futures.immediateFuture(prevGraph);                } else {                                    ListenableFutureTask<Graph> task = ListenableFutureTask.create(new Callable<Graph>() {                    public Graph call() {                      return getGraphFromDatabase(key);                    }                  });                  executor.execute(task);                  return task;                }              }            });
   | 
 
这个是文档中给出的示例,就是采用这种Read-Through的方法使用缓存
上述的代码指定了刷新的时间每分钟,刷新没成功前,会返回旧值。
清理
Guava提供了多种清理策略
大小
这个大小还需要自己算–
1 2 3 4 5 6 7 8 9 10 11 12 13
   | 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) {                 return createExpensiveGraph(key);              }            });
   | 
 
数量
按照缓存key数量 .maximumSize(3)
最近使用
expireAfterAccess(long, TimeUnit)
最近写时间
expireAfterWrite(long, TimeUnit)
垃圾回收器清理
通过设置K和V为弱引用的方法,让垃圾回收器进行回收
- CacheBuilder.weakKeys()
 
- CacheBuilder.weakValues()
 
- CacheBuilder.softValues() 软引用: 我也没懂
 
手动清理
- Cache.invalidate(key)
 
- Cache.invalidateAll(keys)
 
- Cache.invalidateAll()
 
最后,可以添加Remove监听来收到数据清理时的通知
1 2 3 4
   | CacheBuilder.newBuilder()   .expireAfterWrite(2, TimeUnit.MINUTES)   .removalListener(removalListener)   .build(loader);
   | 
 
统计
打开统计
1
   | CacheBuilder.recordStats()
   | 
 
获得统计数据
1
   | CacheStats cacheStats = cache.stats();
   | 
 
参考
1. 官方文档
2. 中文翻译
3. Guava Cache用法介绍