亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

JVM進程緩存Caffeine的使用

 更新時間:2023年01月25日 09:35:13   作者:心潮的滴滴  
本文主要介紹了JVM進程緩存Caffeine的使用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

一、前言

Caffeine是當前最優(yōu)秀的內存緩存框架,不論讀還是寫的效率都遠高于其他緩存,而且在Spring5開始的默認緩存實現(xiàn)就將Caffeine代替原來的Google Guava

二、基本使用

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

2.1 手動創(chuàng)建緩存

void test1() {
    Cache<Object, Object> cache = Caffeine.newBuilder()
            // 初始數(shù)量
            .initialCapacity(10)
            // 最大條數(shù)
            .maximumSize(10)
            // expireAfterWrite和expireAfterAccess同時存在時,以expireAfterWrite為準
            // 最后一次寫操作后經過指定時間過期
            .expireAfterWrite(1, TimeUnit.SECONDS)
            // 最后一次讀或寫操作后經過指定時間過期
            .expireAfterAccess(1, TimeUnit.SECONDS)
            // 監(jiān)聽緩存被移除
            .removalListener((key, value, cause) -> {})
            // 記錄命中
            .recordStats()
            .build();
    cache.put("1", "張三");
    System.out.println(cache.asMap());
    System.out.println(cache.getIfPresent("1"));
    System.out.println(cache.get("2", o -> "默認值"));
}

運行結果

{1=張三}
張三
默認值

2.2 異步獲取緩存

@Test
void test2() {
    AsyncLoadingCache<String, String> asyncLoadingCache = Caffeine.newBuilder()
            // 創(chuàng)建緩存或者最近一次更新緩存后經過指定時間間隔刷新緩存:僅支持LoadingCache
            .refreshAfterWrite(1, TimeUnit.SECONDS)
            .expireAfterWrite(1, TimeUnit.SECONDS)
            .expireAfterAccess(1, TimeUnit.SECONDS)
            .maximumSize(10)
            // 根據key查詢數(shù)據庫里面的值
            .buildAsync(key -> {
                Thread.sleep(1000);
                return new Date().toString();
            });
    // 異步緩存返回的是CompletableFuture
    CompletableFuture<String> future = asyncLoadingCache.get("1");
    future.thenAccept(System.out::println);
}

2.3 記錄命中數(shù)據

@Test
void test3() {
? ? LoadingCache<String, String> cache = Caffeine.newBuilder()
? ? ? ? ? ? // 創(chuàng)建緩存或者最近一次更新緩存后經過指定時間間隔,刷新緩存:refreshAfterWrite僅支持LoadingCache
? ? ? ? ? ? .refreshAfterWrite(1, TimeUnit.SECONDS)
? ? ? ? ? ? .expireAfterWrite(1, TimeUnit.SECONDS)
? ? ? ? ? ? .expireAfterAccess(1, TimeUnit.SECONDS)
? ? ? ? ? ? .maximumSize(10)
? ? ? ? ? ? // 開啟記錄緩存命中率等信息
? ? ? ? ? ? .recordStats()
? ? ? ? ? ? // 根據key查詢數(shù)據庫里面的值
? ? ? ? ? ? .build(key -> {
? ? ? ? ? ? ? ? TimeUnit.MILLISECONDS.sleep(1000);
? ? ? ? ? ? ? ? return new Date().toString();
? ? ? ? ? ? });

? ? cache.put("1", "小明");
? ? cache.get("1");

? ? /*
? ? ?* hitCount :命中的次數(shù)
? ? ?* missCount:未命中次數(shù)
? ? ?* requestCount:請求次數(shù)
? ? ?* hitRate:命中率
? ? ?* missRate:丟失率
? ? ?* loadSuccessCount:成功加載新值的次數(shù)
? ? ?* loadExceptionCount:失敗加載新值的次數(shù)
? ? ?* totalLoadCount:總條數(shù)
? ? ?* loadExceptionRate:失敗加載新值的比率
? ? ?* totalLoadTime:全部加載時間
? ? ?* evictionCount:丟失的條數(shù)
? ? ?*/
? ? System.out.println(cache.stats());
}

會影響性能,生產環(huán)境下建議不開啟

三、淘汰策略

  • LRU: 最近最少使用,淘汰最長時間沒有被使用的頁面;
  • LFU:最不經常使用,淘汰一段時間內,使用次數(shù)最少的頁面;
  • FIFO:先進先出

LRU的優(yōu)點:LRU相比于LFU而言性能更好一些,因為它算法相對比較簡單,不需要記錄訪問頻次,可以更好地應對突發(fā)流量;
LRU的缺點:雖然性能好一些,但是它通過歷史數(shù)據來預測未來是局限的,它會認為最后到來的數(shù)據是最可能被再次訪問的,從而給與它最高的優(yōu)先級。有些非熱點數(shù)據被訪問過后,占據了高優(yōu)先級,它會在緩存中占據相當長的時間,從而造成空間浪費;
LFU的優(yōu)點:LRU根據訪問頻次訪問,在大部分情況下,熱點數(shù)據的頻次肯定高于非熱點數(shù)據,所以它的命中率非常高;
LFU的缺點:LFU算法相對比較復雜,性能比LRU差。有問題的是下面這種情況,比如前一段時間微博有個熱點話題熱度非常高,就比如那種可以讓微博短時間停止服務的,于是趕緊緩存起來,LFU算法記錄了其中熱點詞的訪問頻率,可能高達十幾億,而過后很長一段時間,這個話題已經不是熱點了,新的熱點也來了,但是,新熱點話題的熱度沒辦法到達十幾億,也就是說訪問頻次沒有之前的話提高,那之前的熱點就會一直占據著緩存空間,長時間無法被剔除。

3.1 4種淘汰方式與例子

Caffeine有4種緩存淘汰設置

  • 大?。〞褂肳-TinyLFU算法進行淘汰)
  • 權重(大小與權重,只能二選一)
  • 時間
  • 引用(不常用)
// 緩存大小淘汰
@Test
public void maximumSizeTest() throws InterruptedException {
? ? Cache<Object, Object> cache = Caffeine.newBuilder()
? ? ? ? ? ? // 超過10個后會使用W-TinyLFU算法進行淘汰
? ? ? ? ? ? .maximumSize(10)
? ? ? ? ? ? .build();
? ? for (int i = 1; i <= 10; i++) {
? ? ? ? cache.put(i, i);
? ? }
? ? // 緩存淘汰是異步的
? ? TimeUnit.MILLISECONDS.sleep(500);
? ? // 打印還沒有被淘汰的緩存
? ? System.out.println(cache.asMap());
}

// 權重淘汰
@Test
public void maximumWeightTest() throws InterruptedException {
? ? Cache<Integer, Integer> cache = Caffeine.newBuilder()
? ? ? ? ? ? // 限制總權值,若所有緩存的權重加起來>總權重就會淘汰權重小的緩存
? ? ? ? ? ? .maximumWeight(100)
? ? ? ? ? ? .weigher((Weigher<Integer, Integer>) (key, value) -> key)
? ? ? ? ? ? .build();
? ? // 總權重其實是=所有緩存的權重加起來
? ? int maximumWeight = 0;
? ? for (int i = 1; i < 20; i++) {
? ? ? ? cache.put(i, i);
? ? ? ? maximumWeight += i;
? ? ? ? System.out.println("i = " + i + ", maximumWeight = " + maximumWeight);
? ? }
? ? System.out.println("總權重 = " + maximumWeight);
? ? // 緩存淘汰是異步的
? ? TimeUnit.MILLISECONDS.sleep(500);
? ? // 打印還沒有被淘汰的緩存
? ? System.out.println(cache.asMap());
}

// 訪問后到期(每次訪問都會重置時間,也就是說如果一直被訪問就不會被淘汰)
@Test
void expireAfterAccessTest() throws InterruptedException {
? ? Cache<Object, Object> cache = Caffeine.newBuilder()
? ? ? ? ? ? .expireAfterAccess(1, TimeUnit.SECONDS)
? ? ? ? ? ? // 可以指定調度程序來及時刪除過期緩存項,而不是等待Caffeine觸發(fā)定期維護
? ? ? ? ? ? // 若不設置scheduler,則緩存會在下一次調用get的時候才會被動刪除
? ? ? ? ? ? .scheduler(Scheduler.systemScheduler())
? ? ? ? ? ? .build();
? ? cache.put(1, 2);
? ? System.out.println(cache.getIfPresent(1));
? ? Thread.sleep(3000);
? ? System.out.println(cache.getIfPresent(1));
}

// 寫入后到期
@Test
void expireAfterWriteTest() throws InterruptedException {
? ? Cache<Object, Object> cache = Caffeine.newBuilder()
? ? ? ? ? ? .expireAfterWrite(1, TimeUnit.SECONDS)
? ? ? ? ? ? // 可以指定調度程序來及時刪除過期緩存項,而不是等待Caffeine觸發(fā)定期維護
? ? ? ? ? ? // 若不設置scheduler,則緩存會在下一次調用get的時候才會被動刪除
? ? ? ? ? ? .scheduler(Scheduler.systemScheduler())
? ? ? ? ? ? .build();
? ? cache.put(1, 2);
? ? TimeUnit.MILLISECONDS.sleep(3000);
? ? System.out.println(cache.getIfPresent(1));
}

另外還有一個refreshAfterWrite()表示x秒后自動刷新緩存可以配合以上的策略使用

// 另外還有一個refreshAfterWrite()表示x秒后自動刷新緩存可以配合以上的策略使用
? ? private static int num = 0;
@Test
void refreshAfterWriteTest() throws InterruptedException {
? ? LoadingCache<Object, Integer> cache = Caffeine.newBuilder()
? ? ? ? ? ? .refreshAfterWrite(1, TimeUnit.SECONDS)
? ? ? ? ? ? .build(integer -> ++num);

? ? // 獲取ID=1的值,由于緩存里還沒有,所以會自動放入緩存
? ? System.out.println(cache.get(1));

? ? // 延遲2秒后,理論上自動刷新緩存后取到的值是2
? ? // 但其實不是,值還是1,因為refreshAfterWrite并不是設置了n秒后重新獲取就會自動刷新
? ? // 而是x秒后&&第二次調用getIfPresent的時候才會被動刷新
? ? Thread.sleep(2000);
? ? System.out.println(cache.getIfPresent(1));// 1

? ? //此時才會刷新緩存,而第一次拿到的還是舊值
? ? System.out.println(cache.getIfPresent(1));// 2
}

3.2 最佳實踐

實踐1

  • 配置:設置maxSize、refreshAfterWrite,不設置expireAfterWrite/expireAfterAccess
  • 優(yōu)缺點:因為設置expireAfterWrite當緩存過期會同步加鎖獲取緩存,所以設置expireAfterWrite時性能較好,但是某些時候會取舊數(shù)據,適合允許取到舊數(shù)據的場景

實踐2

  • 配置:設置maxSize、expireAfterWrite/expireAfterAccess,不設置refreshAfterWrite
  • 優(yōu)缺點:與上面相反,數(shù)據一致性好,不會獲取到舊數(shù)據,但是性能沒那么好,適合獲取數(shù)據時不耗時的場景

四、配合Redis做二級緩存

緩存的解決方案一般有三種:

  • 本地內存緩存,如Caffeine、Ehcache;適合單機系統(tǒng),速度最快,但是容量有限,而且重啟系統(tǒng)后緩存丟失;
  • 集中式緩存,如Redis、Memcached;適合分布式系統(tǒng),解決了容量、重啟丟失緩存等問題,但是當訪問量極大時,往往性能不是首要考慮的問題,而是帶寬?,F(xiàn)象就是Redis服務負載不高,但是由于機器網卡帶寬跑滿,導致數(shù)據讀取非常慢;
  • 第三種方案就是結合以上2種方案的二級緩存應運而生,以內存緩存作為一級緩存、集中式緩存作為二級緩存

到此這篇關于JVM進程緩存Caffeine的使用的文章就介紹到這了,更多相關JVM進程緩存Caffeine內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • 冒泡排序的原理及java代碼實現(xiàn)

    冒泡排序的原理及java代碼實現(xiàn)

    冒泡排序法:關鍵字較小的記錄好比氣泡逐趟上浮,關鍵字較大的記錄好比石塊下沉,每趟有一塊最大的石塊沉底。算法本質:(最大值是關鍵點,肯定放到最后了,如此循環(huán))每次都從第一位向后滾動比較,使最大值沉底,最小值上升一次,最后一位向前推進
    2016-02-02
  • 在Spring異步調用中傳遞上下文的方法

    在Spring異步調用中傳遞上下文的方法

    這篇文章主要給大家介紹了關于如何在Spring異步調用中傳遞上下文的相關資料,文中通過示例代碼介紹的非常詳細,對大家學習或者使用Spring具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧
    2019-08-08
  • Java中Static關鍵字的五種用法詳解

    Java中Static關鍵字的五種用法詳解

    這篇文章主要介紹了Java中static的五種用法:修飾成員變量,修飾成員方法,修飾內部類,靜態(tài)代碼塊,靜態(tài)導包,想詳細了解的小伙伴可以參考閱讀本文
    2023-03-03
  • Java 編程如何使用 Class.forName() 加載類

    Java 編程如何使用 Class.forName() 加載類

    在一些應用中,無法事先知道使用者將加載什么類,而必須讓使用者指定類名稱以加載類,可以使用 Class的靜態(tài)forName()方法實現(xiàn)動態(tài)加載類,這篇文章主要介紹了Java編程如何使用Class.forName()加載類,需要的朋友可以參考下
    2022-06-06
  • Sentinel中三種流控模式的使用詳解

    Sentinel中三種流控模式的使用詳解

    這篇文章主要為大家詳細介紹了Sentinel中三種流控模式(預熱模式,排隊等待模式和熱點規(guī)則)的使用,文中的示例代碼講解詳細,感興趣的可以了解下
    2023-08-08
  • 詳解非spring框架下使用querydsl的方法

    詳解非spring框架下使用querydsl的方法

    Querydsl是一個采用API代替拼湊字符串來構造查詢語句,可跟 Hibernate 和 JPA 等框架結合使用。本文介紹的是非spring環(huán)境下querydsl JPA整合使用,感興趣的小伙伴們可以參考一下
    2019-01-01
  • SpringBoot整合Dubbo+Zookeeper實現(xiàn)RPC調用

    SpringBoot整合Dubbo+Zookeeper實現(xiàn)RPC調用

    這篇文章主要給大家介紹了Spring Boot整合Dubbo+Zookeeper實現(xiàn)RPC調用的步驟詳解,文中有詳細的代碼示例,對我們的學習或工作有一定的幫助,需要的朋友可以參考下
    2023-07-07
  • SpringBoot2.1.4中的錯誤處理機制

    SpringBoot2.1.4中的錯誤處理機制

    這篇文章主要介紹了SpringBoot2.1.4中的錯誤處理機制,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • Java垃圾回收之標記壓縮算法詳解

    Java垃圾回收之標記壓縮算法詳解

    今天小編就為大家分享一篇關于Java垃圾回收之標記壓縮算法詳解,小編覺得內容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2018-10-10
  • Spring mvc防止數(shù)據重復提交的方法

    Spring mvc防止數(shù)據重復提交的方法

    這篇文章主要為大家詳細介紹了Spring mvc防止數(shù)據重復提交的方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-11-11

最新評論