Caffeine使用指南

Caffeine使用指南

Caffeine基於java8的高性能,接近最優的緩存庫。Caffeine提供的內存緩存使用參考Google guava的API。Caffeine是基於Google guava和 ConcurrentLinkedHashMap的設計經驗上改進的成果。

Caffeine可以通過建造者模式靈活的組合以下特性:

  1. 通過異步自動加載實體到緩存中
  2. 基於大小的回收策略
  3. 基於時間的回收策略
  4. 自動刷新
  5. key自動封裝虛引用
  6. value自動封裝弱引用或軟引用
  7. 實體過期或被刪除的通知
  8. 寫入外部資源
  9. 統計累計訪問緩存

加載策略

Caffeine提供了3種加載策略:手動加載,同步加載,異步加載

手動加載

Cache<Key, Graph> cache = Caffeine.newBuilder()
    .expireAfterWrite(10, TimeUnit.MINUTES)
    .maximumSize(10_000)
    .build();
// 檢索一個entry,如果沒有則爲null
Graph graph = cache.getIfPresent(key);
// 檢索一個entry,如果entry爲null,則通過key創建一個entry並加入緩存
graph = cache.get(key, k -> createExpensiveGraph(key));
// 插入或更新一個實體
cache.put(key, graph);
// 移除一個實體
cache.invalidate(key);

同步加載

構造Cache時候,build方法傳入一個CacheLoader實現類。實現load方法,通過key加載value。

LoadingCache<Key, Graph> cache = Caffeine.newBuilder()
    .maximumSize(10_000)
    .expireAfterWrite(10, TimeUnit.MINUTES)
    .build(key -> createExpensiveGraph(key));
//如果緩存種沒有對應的value,通過createExpensiveGraph方法加載
Graph graph = cache.get(key);

Map<Key, Graph> graphs = cache.getAll(keys);

異步加載

AsyncLoadingCache<Key, Graph> cache = Caffeine.newBuilder()
    .maximumSize(10_000)
    .expireAfterWrite(10, TimeUnit.MINUTES)
    .buildAsync((key, executor) -> createExpensiveGraphAsync(key, executor));
CompletableFuture<Graph> graph = cache.get(key);
CompletableFuture<Map<Key, Graph>> graphs = cache.getAll(keys);

AsyncLoadingCache 是 LoadingCache 的變體, 可以異步計算實體在一個線程池(Executor)上並且返回 CompletableFuture.

回收策略

Caffeine提供了3種回收策略:基於大小回收,基於時間回收,基於引用回收

基於大小回收

// 基於實體數量淘汰實體
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
    .maximumSize(10_000)
    .build(key -> createExpensiveGraph(key));

// 通過權重來計算,每個實體都有不同的權重,總權重到達最高時淘汰實體。
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
    .maximumWeight(10_000)
    .weigher((Key key, Graph graph) -> graph.vertices().size())
    .build(key -> createExpensiveGraph(key));

到達最大大小時淘汰最近最少使用的實體

基於時間回收

  1. 實體被訪問之後,在實體被讀或被寫後的一段時間後過期
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
    .expireAfterAccess(5, TimeUnit.MINUTES)
    .build(key -> createExpensiveGraph(key));
  1. 基於寫之後,在實體被寫入後的一段時間後過期
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
    .expireAfterWrite(10, TimeUnit.MINUTES)
    .build(key -> createExpensiveGraph(key));
  1. 自定義策略Expiry,可以自定義在實體被讀,被更新,被創建後的時間過期。
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
    .expireAfter(new Expiry<Key, Graph>() {
      public long expireAfterCreate(Key key, Graph graph, long currentTime) {
        // Use wall clock time, rather than nanotime, if from an external resource
        long seconds = graph.creationDate().plusHours(5)
            .minus(System.currentTimeMillis(), MILLIS)
            .toEpochSecond();
        return TimeUnit.SECONDS.toNanos(seconds);
      }
      public long expireAfterUpdate(Key key, Graph graph, 
          long currentTime, long currentDuration) {
        return currentDuration;
      }
      public long expireAfterRead(Key key, Graph graph,
          long currentTime, long currentDuration) {
        return currentDuration;
      }
    })
    .build(key -> createExpensiveGraph(key));

基於引用回收

java種有四種引用:強引用,軟引用,弱引用和虛引用,caffeine可以將值封裝成弱引用或軟引用。
軟引用:如果一個對象只具有軟引用,則內存空間足夠,垃圾回收器就不會回收它;如果內存空間不足了,就會回收這些對象的內存。
弱引用:弱引用的對象擁有更短暫的生命週期。在垃圾回收器線程掃描它所管轄的內存區域的過程中,一旦發現了只具有弱引用的對象,不管當前內存空間足夠與否,都會回收它的內存

LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
    .weakKeys()
    .weakValues()
    .build(key -> createExpensiveGraph(key));


LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
    .softValues()
    .build(key -> createExpensiveGraph(key));

自動刷新

LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
    .maximumSize(10_000)
    .refreshAfterWrite(1, TimeUnit.MINUTES)
    .build(key -> createExpensiveGraph(key));

在寫後的持續時間過後,調用createExpensiveGraph刷新

移除通知

Cache<Key, Graph> graphs = Caffeine.newBuilder()
    .removalListener((Key key, Graph graph, RemovalCause cause) ->
        System.out.printf("Key %s was removed (%s)%n", key, cause))
    .build();

通過removalListener添加實體移除監聽器

寫到外部存儲

通過CacheWriter 可以將緩存回寫的外部存儲中。

LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
  .writer(new CacheWriter<Key, Graph>() {
    @Override public void write(Key key, Graph graph) {
      // 寫入到外部存儲或二級緩存
    }
    @Override public void delete(Key key, Graph graph, RemovalCause cause) {
      // 刪除外部存儲或者二級緩存
    }
  })
  .build(key -> createExpensiveGraph(key));

使用場景

  1. 緩存同步數據庫
  2. 多級緩存同步

注意,CacheWriter不能與弱鍵或AsyncLoadingCache一起使用

統計緩存使用情況

Cache<Key, Graph> graphs = Caffeine.newBuilder()
    .maximumSize(10_000)
    .recordStats()
    .build();

通過使用Caffeine.recordStats(), 可以轉化成一個統計的集合. 通過 Cache.stats() 返回一個CacheStats。CacheStats提供以下統計方法

hitRate(): 返回緩存命中率
evictionCount(): 緩存回收數量
averageLoadPenalty(): 加載新值的平均時間

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章