高性能本地緩存-caffeine

高性能本地緩存-caffeine

簡介

1、Caffeine 是基於 JAVA 8 的高性能緩存庫。並且在 spring5 (springboot 2.x) 後,spring 官方放棄了 Guava,而使用了性能更優秀的 Caffeine 作爲默認緩存組件 2、支持異步加載,事件提交隊列 3、內部使用W-TinyLFU算法,它的命中率非常高,內存佔用更加的小 4、一般在redis之後,做二級緩存      

優勢

性能比較: 8個線程同時從緩存中讀取 image.png 8個線程同時從緩存中寫入 image.png 6個線程讀取,2個線程寫入 image.png

使用場景

1、二級緩存

原生使用方式

填充策略

1、手動加載

// 初始化
public static void main(String[] args) {
    Cache<string, object> cache = Caffeine.newBuilder()
            .expireAfterWrite(1, TimeUnit.SECONDS)
            .expireAfterAccess(1, TimeUnit.SECONDS)
            .maximumSize(10)
            .build();
    String key = "testKey";
    //如果一個key不存在,那麼會進入指定的函數生成value
    Object value = cache.get(key, t -&gt; setValue(key).apply(key));
    cache.put("hello",value);
    //判斷是否存在如果不存返回null
    Object ifPresent = cache.getIfPresent(key);
    System.out.println(ifPresent);
    //移除一個key
    cache.invalidate(key);
}

public static Function<string, object> setValue(String key){
    return t -&gt; key + "value";
}

2、同步加載

構造Cache時候,build方法傳入一個CacheLoader實現類。實現load方法,通過key加載value

public static void main(String[] args) {
    String key = "key";
    LoadingCache<string, object> cache = Caffeine.newBuilder()
            .maximumSize(100)
            .expireAfterWrite(1, TimeUnit.MINUTES)
            .build(k -&gt; setValue(key).apply(key));
    System.out.println(cache.get(key));
}

public static Function<string, object> setValue(String key){
    return t -&gt; key + "value";
}

3、異步加載

AsyncLoadingCache是繼承自LoadingCache類的,異步加載使用Executor去調用方法並返回一個CompletableFuture。異步加載緩存使用了響應式編程模型

public static void main(String[] args) {
    String key = "key";
    AsyncLoadingCache<string, object> cache = Caffeine.newBuilder()
            .maximumSize(100)
            .expireAfterWrite(1, TimeUnit.MINUTES)
            .buildAsync(k -&gt; setValue(key).get());
    // 異步獲取
    CompletableFuture<object> graph = cache.get(key);
    graph.thenAccept(s -&gt; {
        System.out.println(s);
    });
}

public static CompletableFuture<object> setValue(String key){
    return CompletableFuture.supplyAsync(() -&gt; {
        return key + "value";
    });
}

springboot集成springboot-cache使用

1、添加jar依賴

<dependency>
    <groupid>org.springframework.boot</groupid>
    <artifactid>spring-boot-starter-cache</artifactid>
</dependency>
<dependency>
    <groupid>com.github.ben-manes.caffeine</groupid>
    <artifactid>caffeine</artifactid>
</dependency>

2、配置@EnableCaching註解

@EnableCaching
@Configuration
public class LocalCacheConfig {

    @Bean("caffeineCacheManager")
    public CacheManager cacheManager(){
        	CaffeineCacheManager caffeineCacheManager = new CaffeineCacheManager();
        caffeineCacheManager.setCaffeine(Caffeine.newBuilder().expireAfterAccess(10, TimeUnit.MINUTES).initialCapacity(100).maximumSize(5000));
        return caffeineCacheManager;
    }
}

3、使用

@Slf4j
@Service
@CacheConfig(cacheNames = "caffeineCacheManager")
public class SafetyInspectionTrackServiceImpl implements SafetyInspectionTrackService {

    @Autowired
    private SafetyInspectionTrackMapper safetyInspectionTrackMapper;

    @Override
    @Cacheable(key = "#id")
    public SafetyInspectionTrackDO getById(Long id) {
        log.info("進入緩存:{}",id);
        return safetyInspectionTrackMapper.selectById(id);
    }

    @Override
    @CacheEvict
    public void clear(Long id){
        log.info("清除緩存");
    }
}

@Cacheable:

配置在&nbsp;getUsersByName方法上表示其返回值將被加入緩存。同時在查詢時,會先從緩存中
獲取,若不存在纔再發起對數據庫的訪問
@CachePut:
配置於方法上時,能夠根據參數定義條件來進行緩存,其與&nbsp;@Cacheable不同的是使用&nbsp;
@CachePut標註的方法在執行前不會去檢查緩存中是否存在之前執行過的結果,而是每次都
會執行該方法並將執行結果以鍵值對的形式存入指定的緩存中,所以主要用於數據新增和修改
操作上

@CacheEvict:

配置於方法上時,表示從緩存中移除相應數據

Caffeine常用配置說明

1、初始化參數

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

注意:
expireAfterWrite和expireAfterAccess同時存在時,以expireAfterWrite爲準。
maximumSize和maximumWeight不可以同時使用
weakValues和softValues不可以同時使用

2、自動刷新

refreshAfterWrite就是設置寫入後多就會刷新,expireAfterWrite和refreshAfterWrite
的區別是,當緩存過期後,配置了expireAfterWrite,則調用時會阻塞,等待緩存計算完成,返
回新的值並進行緩存,refreshAfterWrite則是返回一箇舊值,並異步計算新值並緩存

3、回收策略

(1)基於大小

maximumSize&nbsp;初始給定緩存大小,超過設置的值後,自動回收

(2)基於時間

expireAfterAccess:在最後一次訪問或者寫入後開始計時
expireAfterWrite:在最後一次寫入緩存後開始計時
expireAfte:自定義策略

(3)基於引用

weakKeys:使用弱引用存儲key
weakValues:使用弱引用存儲value
softValues:使用軟引用存儲value&nbsp;
軟引用: 如果一個對象只具有軟引用,則內存空間足夠,垃圾回收器就不會回收它;如果內存空間不足了,就會回收這些對象的內存。
弱引用: 弱引用的對象擁有更短暫的生命週期。在垃圾回收器線程掃描它所管轄的內存區域的過程中,一旦發現了只具有弱引用的對象,不管當前內存空間足夠與否,都會回收它的內存

淘汰策略算法

1、FIFO:先進先出

在這種淘汰算法中,先進入緩存的會先被淘汰,會導致命中率很低。

2、LRU:最近最少使用算法

每次訪問數據都會將其放在我們的隊尾,如果需要淘汰數據,就只需要淘汰隊首即可。仍然有個問題,如果有個數據在 1 分鐘訪問了 1000次,再後 1 分鐘沒有訪問這個數據,但是有其他的數據訪問,就導致了我們這個熱點數據被淘汰。

3、LFU:最近最少頻率使用

利用額外的空間記錄每個數據的使用頻率,然後選出頻率最低進行淘汰。這樣就避免了 LRU 不能處理時間段的問題。

4、W-tinyLFU算法

詳細介紹 https://www.cnblogs.com/liujinhua306/p/9808500.html

源碼簡析

1、在Caffeine中有個LocalCacheFactory類,他會根據你的配置進行具體Cache的創建。 image.png

2、在Caffeine中有個scheduleDrainBuffers方法,用來進行我們的過期任務的調度 image.png

常見問題(Faq)

1、固定數據(Pinning Entries)

固定數據是不能通過驅逐策略去將數據刪除的。當數據是一個有狀態的資源時(如鎖)

個人博客地址

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