<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          緩存之王Caffeine Cache,性能比Guava更強,命中率更高!

          共 4204字,需瀏覽 9分鐘

           ·

          2021-03-19 16:42

          在項目開發(fā)中,為提升系統(tǒng)性能,減少 IO 開銷,本地緩存是必不可少的。最常見的本地緩存是 Guava 和 Caffeine,本篇文章將為大家介紹 Caffeine。

          Caffeine 是基于 Google Guava Cache 設(shè)計經(jīng)驗改進(jìn)的結(jié)果,相較于 Guava 在性能和命中率上更具有效率,你可以認(rèn)為其是 Guava Plus。

          毋庸置疑的,你應(yīng)該盡快將你的本地緩存從 Guava 遷移至 Caffeine,本文將重點和 Guava 對比二者性能占據(jù),給出本地緩存的最佳實踐,以及遷移策略。

          二、PK Guava

          2.1 功能

          從功能上看,Guava 已經(jīng)比較完善了,滿足了絕大部分本地緩存的需求。Caffine 除了提供 Guava 已有的功能外,同時還加入了一些擴展功能。

          2.2 性能

          Guava 中其讀寫操作夾雜著過期時間的處理,也就是你在一次 put 操作中有可能會做淘汰操作,所以其讀寫性能會受到一定影響。

          Caffeine 在讀寫操作方面完爆 Guava,主要是因為 Caffeine 對這些事件的操作是異步的,將事件提交至隊列(使用 Disruptor RingBuffer),然后會通過默認(rèn)的 ForkJoinPool.commonPool(),或自己配置的線程池,進(jìn)行取隊列操作,然后再進(jìn)行后續(xù)的淘汰、過期操作。

          以下性能對比來自 Caffeine 官方提供數(shù)據(jù):

          (1)在此基準(zhǔn)測試中,從配置了最大大小的緩存中,8 個線程并發(fā)讀:

          (2)在此基準(zhǔn)測試中,從配置了最大大小的緩存中,6個線程并發(fā)讀、2個線程并發(fā)寫:

          image.png

          (3)在此基準(zhǔn)測試中,從配置了最大大小的緩存中,8 個線程并發(fā)寫:

          image.png

          2.3 命中率

          緩存的淘汰策略是為了預(yù)測哪些數(shù)據(jù)在短期內(nèi)最可能被再次用到,從而提升緩存的命中率。Guava 使用 S-LRU 分段的最近最少未使用算法,Caffeine 采用了一種結(jié)合 LRU、LFU 優(yōu)點的算法:W-TinyLFU,其特點是:高命中率、低內(nèi)存占用。

          2.3.1 LRU

          Least Recently Used:如果數(shù)據(jù)最近被訪問過,將來被訪問的概率也更高。每次訪問就把這個元素放到隊列的頭部,隊列滿了就淘汰隊列尾部的數(shù)據(jù),即淘汰最長時間沒有被訪問的。

          需要維護(hù)每個數(shù)據(jù)項的訪問頻率信息,每次訪問都需要更新,這個開銷是非常大的。

          其缺點是,如果某一時刻大量數(shù)據(jù)到來,很容易將熱點數(shù)據(jù)擠出緩存,留下來的很可能是只訪問一次,今后不會再訪問的或頻率極低的數(shù)據(jù)。比如外賣中午時候訪問量突增、微博爆出某明星糗事就是一個突發(fā)性熱點事件。當(dāng)事件結(jié)束后,可能沒有啥訪問量了,但是由于其極高的訪問頻率,導(dǎo)致其在未來很長一段時間內(nèi)都不會被淘汰掉。

          2.3.2 LFU

          Least Frequently Used:如果數(shù)據(jù)最近被訪問過,那么將來被訪問的概率也更高。也就是淘汰一定時間內(nèi)被訪問次數(shù)最少的數(shù)據(jù)(時間局部性原理)。

          需要用 Queue 來保存訪問記錄,可以用 LinkedHashMap 來簡單實現(xiàn)一個基于 LRU 算法的緩存。

          其優(yōu)點是,避免了 LRU 的缺點,因為根據(jù)頻率淘汰,不會出現(xiàn)大量進(jìn)來的擠壓掉老的,如果在數(shù)據(jù)的訪問的模式不隨時間變化時候,LFU 能夠提供絕佳的命中率。

          其缺點是,偶發(fā)性的、周期性的批量操作會導(dǎo)致LRU命中率急劇下降,緩存污染情況比較嚴(yán)重。

          2.3.3 TinyLFU

          TinyLFU 顧名思義,輕量級LFU,相比于 LFU 算法用更小的內(nèi)存空間來記錄訪問頻率。

          TinyLFU 維護(hù)了近期訪問記錄的頻率信息,不同于傳統(tǒng)的 LFU 維護(hù)整個生命周期的訪問記錄,所以他可以很好地應(yīng)對突發(fā)性的熱點事件(超過一定時間,這些記錄不再被維護(hù))。這些訪問記錄會作為一個過濾器,當(dāng)新加入的記錄(New Item)訪問頻率高于將被淘汰的緩存記錄(Cache Victim)時才會被替換。流程如下:

          tiny-lfu-arch

          盡管維護(hù)的是近期的訪問記錄,但仍然是非常昂貴的,TinyLFU 通過 Count-Min Sketch 算法來記錄頻率信息,它占用空間小且誤報率低,關(guān)于 Count-Min Sketch 算法可以參考論文:pproximating Data with the Count-Min Data Structure

          2.3.4 W-TinyLFU

          W-TinyLFU 是 Caffeine 提出的一種全新算法,它可以解決頻率統(tǒng)計不準(zhǔn)確以及訪問頻率衰減的問題。這個方法讓我們從空間、效率、以及適配舉證的長寬引起的哈希碰撞的錯誤率上做均衡。

          下圖是一個運行了 ERP 應(yīng)用的數(shù)據(jù)庫服務(wù)中各種算法的命中率,實驗數(shù)據(jù)來源于 ARC 算法作者,更多場景的性能測試參見官網(wǎng):

          database

          W-TinyLFU 算法是對 TinyLFU算法的優(yōu)化,能夠很好地解決一些稀疏的突發(fā)訪問元素。在一些數(shù)目很少但突發(fā)訪問量很大的場景下,TinyLFU將無法保存這類元素,因為它們無法在短時間內(nèi)積累到足夠高的頻率,從而被過濾器過濾掉。W-TinyLFU 將新記錄暫時放入 Window Cache 里面,只有通過 TinLFU 考察才能進(jìn)入 Main Cache。大致流程如下圖:

          W-TinyLFU

          三、最佳實踐

          3.1 實踐1

          配置方式:設(shè)置 maxSize、refreshAfterWrite,不設(shè)置 expireAfterWrite

          存在問題:get 緩存間隔超過 refreshAfterWrite 后,觸發(fā)緩存異步刷新,此時會獲取緩存中的舊值

          適用場景

          • 緩存數(shù)據(jù)量大,限制緩存占用的內(nèi)存容量
          • 緩存值會變,需要刷新緩存
          • 可以接受任何時間緩存中存在舊數(shù)據(jù)

          設(shè)置 maxSize、refreshAfterWrite,不設(shè)置 expireAfterWrite

          3.2 實踐2

          配置方式:設(shè)置 maxSize、expireAfterWrite,不設(shè)置 refreshAfterWrite

          存在問題:get 緩存間隔超過 expireAfterWrite 后,針對該 key,獲取到鎖的線程會同步執(zhí)行 load,其他未獲得鎖的線程會阻塞等待,獲取鎖線程執(zhí)行延時過長會導(dǎo)致其他線程阻塞時間過長

          適用場景

          • 緩存數(shù)據(jù)量大,限制緩存占用的內(nèi)存容量
          • 緩存值會變,需要刷新緩存
          • 不可以接受緩存中存在舊數(shù)據(jù)
          • 同步加載數(shù)據(jù)延遲?。ㄊ褂?redis 等)

          設(shè)置 maxSize、expireAfterWrite,不設(shè)置refreshAfterWrite

          3.3 實踐3

          配置方式:設(shè)置 maxSize,不設(shè)置 refreshAfterWriteexpireAfterWrite,定時任務(wù)異步刷新數(shù)據(jù)

          存在問題:需要手動定時任務(wù)異步刷新緩存

          適用場景

          • 緩存數(shù)據(jù)量大,限制緩存占用的內(nèi)存容量
          • 緩存值會變,需要刷新緩存
          • 不可以接受緩存中存在舊數(shù)據(jù)
          • 同步加載數(shù)據(jù)延遲可能會很大
          g

          設(shè)置 maxSize,不設(shè)置 refreshAfterWriteexpireAfterWrite,定時任務(wù)異步刷新數(shù)據(jù)

          3.4 實踐4

          配置方式:設(shè)置 maxSizerefreshAfterWrite、expireAfterWrite,refreshAfterWrite < expireAfterWrite

          存在問題

          • get 緩存間隔在 refreshAfterWrite 和 expireAfterWrite 之間,觸發(fā)緩存異步刷新,此時會獲取緩存中的舊值
          • get 緩存間隔大于 expireAfterWrite,針對該 key,獲取到鎖的線程會同步執(zhí)行 load,其他未獲得鎖的線程會阻塞等待,獲取鎖線程執(zhí)行延時過長會導(dǎo)致其他線程阻塞時間過長

          適用場景

          • 緩存數(shù)據(jù)量大,限制緩存占用的內(nèi)存容量
          • 緩存值會變,需要刷新緩存
          • 可以接受有限時間緩存中存在舊數(shù)據(jù)
          • 同步加載數(shù)據(jù)延遲?。ㄊ褂?redis 等)

          設(shè)置 maxSizerefreshAfterWrite、expireAfterWrite

          四、遷移指南

          4.1 切換至 Caffeine

          在 pom 文件中引入 Caffeine 依賴:

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

          Caffeine 兼容 Guava API,從 Guava 切換到 Caffeine,僅需要把 CacheBuilder.newBuilder()改成 Caffeine.newBuilder() 即可。

          4.2 Get Exception

          需要注意的是,在使用 Guava 的 get()方法時,當(dāng)緩存的 load()方法返回 null 時,會拋出 ExecutionException。切換到 Caffeine 后,get()方法不會拋出異常,但允許返回為 null。

          Guava 還提供了一個getUnchecked()方法,它不需要我們顯示的去捕捉異常,但是一旦 load()方法返回 null時,就會拋出 UncheckedExecutionException。切換到 Caffeine 后,不再提供 getUnchecked()方法,因此需要做好判空處理。

          來源 | https://jitwxs.cn/126e3eed.html

          瀏覽 92
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产精品激情综合网 | 成人91AV网 | 操逼逼网 | 成人综合中文字幕 | 一级片免费不卡 |