最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JVM進(jìn)程緩存Caffeine的使用

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

一、前言

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

二、基本使用

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

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

void test1() {
    Cache<Object, Object> cache = Caffeine.newBuilder()
            // 初始數(shù)量
            .initialCapacity(10)
            // 最大條數(shù)
            .maximumSize(10)
            // expireAfterWrite和expireAfterAccess同時(shí)存在時(shí),以expireAfterWrite為準(zhǔn)
            // 最后一次寫操作后經(jīng)過(guò)指定時(shí)間過(guò)期
            .expireAfterWrite(1, TimeUnit.SECONDS)
            // 最后一次讀或?qū)懖僮骱蠼?jīng)過(guò)指定時(shí)間過(guò)期
            .expireAfterAccess(1, TimeUnit.SECONDS)
            // 監(jiān)聽(tīng)緩存被移除
            .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 -> "默認(rèn)值"));
}

運(yùn)行結(jié)果

{1=張三}
張三
默認(rèn)值

2.2 異步獲取緩存

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

2.3 記錄命中數(shù)據(jù)

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

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

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

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

三、淘汰策略

  • LRU: 最近最少使用,淘汰最長(zhǎng)時(shí)間沒(méi)有被使用的頁(yè)面;
  • LFU:最不經(jīng)常使用,淘汰一段時(shí)間內(nèi),使用次數(shù)最少的頁(yè)面;
  • FIFO:先進(jìn)先出

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

3.1 4種淘汰方式與例子

Caffeine有4種緩存淘汰設(shè)置

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

// 權(quán)重淘汰
@Test
public void maximumWeightTest() throws InterruptedException {
? ? Cache<Integer, Integer> cache = Caffeine.newBuilder()
? ? ? ? ? ? // 限制總權(quán)值,若所有緩存的權(quán)重加起來(lái)>總權(quán)重就會(huì)淘汰權(quán)重小的緩存
? ? ? ? ? ? .maximumWeight(100)
? ? ? ? ? ? .weigher((Weigher<Integer, Integer>) (key, value) -> key)
? ? ? ? ? ? .build();
? ? // 總權(quán)重其實(shí)是=所有緩存的權(quán)重加起來(lái)
? ? 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("總權(quán)重 = " + maximumWeight);
? ? // 緩存淘汰是異步的
? ? TimeUnit.MILLISECONDS.sleep(500);
? ? // 打印還沒(méi)有被淘汰的緩存
? ? System.out.println(cache.asMap());
}

// 訪問(wèn)后到期(每次訪問(wèn)都會(huì)重置時(shí)間,也就是說(shuō)如果一直被訪問(wèn)就不會(huì)被淘汰)
@Test
void expireAfterAccessTest() throws InterruptedException {
? ? Cache<Object, Object> cache = Caffeine.newBuilder()
? ? ? ? ? ? .expireAfterAccess(1, TimeUnit.SECONDS)
? ? ? ? ? ? // 可以指定調(diào)度程序來(lái)及時(shí)刪除過(guò)期緩存項(xiàng),而不是等待Caffeine觸發(fā)定期維護(hù)
? ? ? ? ? ? // 若不設(shè)置scheduler,則緩存會(huì)在下一次調(diào)用get的時(shí)候才會(huì)被動(dòng)刪除
? ? ? ? ? ? .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)
? ? ? ? ? ? // 可以指定調(diào)度程序來(lái)及時(shí)刪除過(guò)期緩存項(xiàng),而不是等待Caffeine觸發(fā)定期維護(hù)
? ? ? ? ? ? // 若不設(shè)置scheduler,則緩存會(huì)在下一次調(diào)用get的時(shí)候才會(huì)被動(dòng)刪除
? ? ? ? ? ? .scheduler(Scheduler.systemScheduler())
? ? ? ? ? ? .build();
? ? cache.put(1, 2);
? ? TimeUnit.MILLISECONDS.sleep(3000);
? ? System.out.println(cache.getIfPresent(1));
}

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

// 另外還有一個(gè)refreshAfterWrite()表示x秒后自動(dòng)刷新緩存可以配合以上的策略使用
? ? 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的值,由于緩存里還沒(méi)有,所以會(huì)自動(dòng)放入緩存
? ? System.out.println(cache.get(1));

? ? // 延遲2秒后,理論上自動(dòng)刷新緩存后取到的值是2
? ? // 但其實(shí)不是,值還是1,因?yàn)閞efreshAfterWrite并不是設(shè)置了n秒后重新獲取就會(huì)自動(dòng)刷新
? ? // 而是x秒后&&第二次調(diào)用getIfPresent的時(shí)候才會(huì)被動(dòng)刷新
? ? Thread.sleep(2000);
? ? System.out.println(cache.getIfPresent(1));// 1

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

3.2 最佳實(shí)踐

實(shí)踐1

  • 配置:設(shè)置maxSize、refreshAfterWrite,不設(shè)置expireAfterWrite/expireAfterAccess
  • 優(yōu)缺點(diǎn):因?yàn)樵O(shè)置expireAfterWrite當(dāng)緩存過(guò)期會(huì)同步加鎖獲取緩存,所以設(shè)置expireAfterWrite時(shí)性能較好,但是某些時(shí)候會(huì)取舊數(shù)據(jù),適合允許取到舊數(shù)據(jù)的場(chǎng)景

實(shí)踐2

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

四、配合Redis做二級(jí)緩存

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

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

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

相關(guān)文章

最新評(píng)論

汝阳县| 大宁县| 梁山县| 沙田区| 明星| 昌吉市| 藁城市| 饶阳县| 满洲里市| 巴东县| 呼伦贝尔市| 微山县| 信宜市| 宁乡县| 普格县| 麦盖提县| 东丰县| 阿拉善左旗| 宝丰县| 信丰县| 沐川县| 建水县| 大姚县| 公安县| 新河县| 丘北县| 武宁县| 高青县| 女性| 仪陇县| 丰镇市| 天全县| 沈丘县| 崇信县| 长垣县| 沁源县| 苍山县| 云和县| 黎平县| 光泽县| 花莲市|