本地緩存Caffeine Cache


DB + Redis + LocalCache = 高效存儲,高效訪問

Caffeine簡介

Caffeine是基於Java 8的高性能,接近最佳的緩存工具庫。Caffeine使用Google Guava啟發的API提供內存緩存。所以它的使用成本較低,跟Guava的API基本一致。

它主要有以下幾個功能:

  • 自動將條目加載到緩存中,可以選擇同步或異步加載
  • 基於頻率和新近度超過最大值時基於大小的逐出
  • 自上次訪問或上次寫入以來測得的基於時間的條目到期
  • 發生第一個陳舊的條目請求時,異步刷新
  • 鍵自動包裝在弱引用中
  • 值自動包裝在弱引用或軟引用中
  • 逐出(或以其他方式刪除)條目的通知
  • 寫入通知
  • 緩存訪問統計信息的

快速入門

Cache分為LoadingCache(同步緩存),AsyncLoadingCache(異步緩存)。

pom 依賴

<dependency>
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
    <version>3.1.5</version>
</dependency>

創建對象

Cache<String, Object> cache = Caffeine.newBuilder()
               .initialCapacity(100)//初始大小
               .maximumSize(200)//最大數量
               .expireAfterWrite(3, TimeUnit.SECONDS)//過期時間
               .build();

參數介紹:

  • initialCapacity:初始的緩存空間大小
  • maximumSize:緩存的最大數量
  • maximumWeight:緩存的最大權重
  • expireAfterAccess:最后一次讀或寫操作后經過指定時間過期
  • expireAfterWrite:最后一次寫操作后經過指定時間過期
  • refreshAfterWrite:創建緩存或者最近一次更新緩存后經過指定時間間隔,刷新緩存
  • weakKeys:打開key的弱引用
  • weakValues:打開value的弱引用
  • softValues:打開value的軟引用
  • recordStats:開發統計功能

注意:

  • expireAfterWrite和expireAfterAccess同時存在時,以expireAfterWrite為准。
  • maximumSize和maximumWeight不可以同時使用。

添加數據

Caffeine 為我們提供了手動、同步和異步這幾種填充策略。

Cache<String, String> cache = Caffeine.newBuilder()
		.build();
cache.put("java金融", "java金融");
System.out.println(cache.getIfPresent("java金融"));

自動添加(自定義添加函數)

public static void main(String[] args) {
    Cache<String, String> cache = Caffeine.newBuilder()
        .build();
    // 1.如果緩存中能查到,則直接返回
    // 2.如果查不到,則從我們自定義的getValue方法獲取數據,並加入到緩存中
    String val = cache.get("java金融", k -> getValue(k));
    System.out.println(val);
}

/**
 * 緩存中找不到,則會進入這個方法。一般是從數據庫獲取內容
 *
 * @param k
 * @return
 */
private static String getValue(String k) {
    return k + ":value";
}

過期策略

Caffeine 為我們提供了三種過期策略,分別是基於大小(size-based)、基於時間(time-based)、基於引用(reference-based)

基於大小(size-based)

public static void main(String[] args) {
    LoadingCache<String, String> cache = Caffeine.newBuilder()
        // 最大容量為1
        .maximumSize(1)
        .build(k -> getValue(k));
    cache.put("java金融1", "java金融1");
    cache.put("java金融2", "java金融2");
    cache.put("java金融3", "java金融3");
    cache.cleanUp();
    System.out.println(cache.getIfPresent("java金融1"));
    System.out.println(cache.getIfPresent("java金融2"));
    System.out.println(cache.getIfPresent("java金融3"));
}

/**
 * * 緩存中找不到,則會進入這個方法。一般是從數據庫獲取內容
 * * @param k
 * * @return
 */
private static String getValue(String k) {
    return k + ":value";
}

運行結果如下:淘汰了兩個只剩下一個。

null
null
java金融3

基於時間(time-based)

Caffeine提供了三種定時驅逐策略:

expireAfterWrite(long, TimeUnit)

在最后一次寫入緩存后開始計時,在指定的時間后過期。

public static void main(String[] args) throws Exception{
    LoadingCache<String, String> cache =  Caffeine.newBuilder()
        // 最大容量為1
        .maximumSize(1)
        .expireAfterWrite(3, TimeUnit.SECONDS)
        .build(k->getValue(k));
    cache.put("java金融","java金融");
    Thread.sleep(1*1000);
    System.out.println(cache.getIfPresent("java金融"));
    Thread.sleep(1*1000);
    System.out.println(cache.getIfPresent("java金融"));
    Thread.sleep(1*1000);
    System.out.println(cache.getIfPresent("java金融"));
}

/**
 * * 緩存中找不到,則會進入這個方法。一般是從數據庫獲取內容
 * * @param k
 * * @return
 */
private static String getValue(String k) {
    return k + ":value";
}

運行結果第三秒的時候取值為空:

java金融
java金融
null

expireAfterAccess

在最后一次讀或者寫入后開始計時,在指定的時間后過期。假如一直有請求訪問該key,那么這個緩存將一直不會過期。

public static void main(String[] args) throws Exception {
    LoadingCache<String, String> cache = Caffeine.newBuilder()
        // 最大容量為1
        .maximumSize(1)
        .expireAfterAccess(3, TimeUnit.SECONDS)
        .build(k -> getValue(k));
    cache.put("java金融", "java金融");
    Thread.sleep(1 * 1000);
    System.out.println(cache.getIfPresent("java金融"));
    Thread.sleep(1 * 1000);
    System.out.println(cache.getIfPresent("java金融"));
    Thread.sleep(1 * 1000);
    System.out.println(cache.getIfPresent("java金融"));
    Thread.sleep(3001);
    System.out.println(cache.getIfPresent("java金融"));
}

/**
 * * 緩存中找不到,則會進入這個方法。一般是從數據庫獲取內容
 * * @param k
 * * @return
 */
private static String getValue(String k) {
    return k + ":value";
}

運行結果:讀和寫都沒有的情況下,3秒后才過期,然后就輸出了null。

java金融
java金融
java金融
null

expireAfter(Expiry)

在expireAfter中需要自己實現Expiry接口,這個接口支持expireAfterCreate,expireAfterUpdate,以及expireAfterRead了之后多久過期。注意這個是和expireAfterAccess、expireAfterAccess是互斥的。這里和expireAfterAccess、expireAfterAccess不同的是,需要你告訴緩存框架,它應該在具體的某個時間過期,獲取具體的過期時間。 

public static void main(String[] args) throws Exception {
    LoadingCache<String, String> cache = Caffeine.newBuilder()
        // 最大容量為1
        .maximumSize(1)
        .removalListener((key, value, cause) ->
                         System.out.println("key:" + key + ",value:" + value + ",刪除原因:" + cause))
        .expireAfter(new Expiry<String, String>() {
            @Override
            public long expireAfterCreate(@NonNull String key, @NonNull String value, long currentTime) {
                return currentTime;
            }

            @Override
            public long expireAfterUpdate(@NonNull String key, @NonNull String value, long currentTime, @NonNegative long currentDuration) {
                return currentTime;
            }

            @Override
            public long expireAfterRead(@NonNull String key, @NonNull String value, long currentTime, @NonNegative long currentDuration) {
                return currentTime;
            }
        })
        .build(k -> getValue(k));
}

/**
 * * 緩存中找不到,則會進入這個方法。一般是從數據庫獲取內容
 * * @param k
 * * @return
 */
private static String getValue(String k) {
    return k + ":value";
}

刪除

  • 單個刪除:Cache.invalidate(key)
  • 批量刪除:Cache.invalidateAll(keys)
  • 刪除所有緩存項:Cache.invalidateAll

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM