關(guān)于Guava緩存詳解及使用說明
緩存
緩存分為本地緩存與分布式緩存。
本地緩存為了保證線程安全問題,一般使用ConcurrentMap的方式保存在內(nèi)存之中,而常見的分布式緩存則有Redis,MongoDB等。
- 一致性:本地緩存由于數(shù)據(jù)存儲于內(nèi)存之中,每個實例都有自己的副本,可能會存在不一致的情況;分布式緩存則可有效避免這種情況
- 開銷:本地緩存會占用JVM內(nèi)存,會影響GC及系統(tǒng)性能;分布式緩存的開銷則在于網(wǎng)絡(luò)時延和對象序列化,故主要影響調(diào)用時延
- 適用場景:本地緩存適用于數(shù)據(jù)量較小或變動較少的數(shù)據(jù);分布式緩存則適用于一致性要求較高及數(shù)量量大的場景(可彈性擴容)
本地緩存適用于數(shù)據(jù)量較小或變動較少的數(shù)據(jù),因為變動多需要考慮到不同實例的緩存一致性問題,而數(shù)據(jù)量大則需要考慮緩存回收策略及GC相關(guān)的問題
Guava cache
Guava Cache 是Google Fuava中的一個內(nèi)存緩存模塊,用于將數(shù)據(jù)緩存到JVM內(nèi)存中。
- 提供了get、put封裝操作,能夠集成數(shù)據(jù)源 ;
- 線程安全的緩存,與ConcurrentMap相似,但前者增加了更多的元素失效策略,后者只能顯示的移除元素;
- Guava Cache提供了多種基本的緩存回收方式
- 監(jiān)控緩存加載/命中情況
通常,Guava緩存適用于以下情況:
- 愿意花費一些內(nèi)存來提高速度。
- 使用場景有時會多次查詢key。
- 緩存將不需要存儲超出RAM容量的數(shù)據(jù)
詳細(xì)配置
緩存的并發(fā)級別
Guava提供了設(shè)置并發(fā)級別的API,使得緩存支持并發(fā)的寫入和讀取。
與ConcurrentHashMap類似,Guava cache的并發(fā)也是通過分離鎖實現(xiàn)。
在通常情況下,推薦將并發(fā)級別設(shè)置為服務(wù)器cpu核心數(shù)。
CacheBuilder.newBuilder() // 設(shè)置并發(fā)級別為cpu核心數(shù),默認(rèn)為4 .concurrencyLevel(Runtime.getRuntime().availableProcessors()) .build();
緩存的初始容量設(shè)置
我們在構(gòu)建緩存時可以為緩存設(shè)置一個合理大小初始容量,由于Guava的緩存使用了分離鎖的機制,擴容的代價非常昂貴。
所以合理的初始容量能夠減少緩存容器的擴容次數(shù)。
CacheBuilder.newBuilder() // 設(shè)置初始容量為100 .initialCapacity(100) .build();
設(shè)置最大存儲
Guava Cache可以在構(gòu)建緩存對象時指定緩存所能夠存儲的最大記錄數(shù)量。
當(dāng)Cache中的記錄數(shù)量達(dá)到最大值后再調(diào)用put方法向其中添加對象,Guava會先從當(dāng)前緩存的對象記錄中選擇一條刪除掉,騰出空間后再將新的對象存儲到Cache中。
CacheBuilder.newBuilder() // 設(shè)置最大容量為1000 .maximumSize(1000) .build();
緩存清除策略
基于存活時間的清除策略
expireAfterWrite
寫緩存后多久過期expireAfterAccess
讀寫緩存后多久過期
存活時間策略可以單獨設(shè)置或組合配置
基于容量的清除策略
通過CacheBuilder.maximumSize(long)方法可以設(shè)置Cache的最大容量數(shù),當(dāng)緩存數(shù)量達(dá)到或接近該最大值時,Cache將清除掉那些最近最少使用的緩存
基于權(quán)重的清除策略
使用CacheBuilder.weigher(Weigher)指定一個權(quán)重函數(shù),并且用CacheBuilder.maximumWeight(long)指定最大總重。
如每一項緩存所占據(jù)的內(nèi)存空間大小都不一樣,可以看作它們有不同的“權(quán)重”(weights),作為執(zhí)行清除策略時優(yōu)化回收的對象
LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder() .maximumWeight(100000) .weigher(new Weigher<Key, Graph>() { public int weigh(Key k, Graph g) { return g.vertices().size(); } }) .build( new CacheLoader<Key, Graph>() { public Graph load(Key key) { // no checked exception return createExpensiveGraph(key); } });
顯式清除
- 清除單個key:Cache.invalidate(key)
- 批量清除key:Cache.invalidateAll(keys)
- 清除所有緩存項:Cache.invalidateAll()
基于引用的清除策略
在構(gòu)建Cache實例過程中,通過設(shè)置使用弱引用的鍵、或弱引用的值、或軟引用的值,從而使JVM在GC時順帶實現(xiàn)緩存的清除
CacheBuilder.weakKeys()
:使用弱引用存儲鍵。當(dāng)鍵沒有其它(強或軟)引用時,緩存項可以被垃圾回收CacheBuilder.weakValues()
:使用弱引用存儲值。當(dāng)值沒有其它(強或軟)引用時,緩存項可以被垃圾回收CacheBuilder.softValues()
:使用軟引用存儲值。軟引用只有在響應(yīng)內(nèi)存需要時,才按照全局最近最少使用的順序回收。考慮到使用軟引用的性能影響,我們通常建議使用更有性能預(yù)測性的緩存大小限定
垃圾回收僅依賴==恒等式,使用弱引用鍵的緩存用而不是equals(),即同一對象引用。
Cache
顯式put操作置入內(nèi)存
private static Cache<Integer, Integer> numCache = CacheBuilder.newBuilder() .expireAfterWrite(5, TimeUnit.MINUTES) .build(); public static void main(String[] args) throws Exception { System.out.println(numCache.getIfPresent(1)); Thread.sleep(1000); System.out.println(numCache.getIfPresent(1)); Thread.sleep(1000); numCache.put(1, 5); System.out.println(numCache.getIfPresent(1)); // console: null null 5 }
LoadingCache
使用自定義ClassLoader加載數(shù)據(jù),置入內(nèi)存中。從LoadingCache中獲取數(shù)據(jù)時,若數(shù)據(jù)存在則直接返回;若數(shù)據(jù)不存在,則根據(jù)ClassLoader的load方法加載數(shù)據(jù)至內(nèi)存,然后返回該數(shù)據(jù)
private static LoadingCache<Integer,Integer> numCache = CacheBuilder.newBuilder(). expireAfterWrite(5L, TimeUnit.MINUTES). maximumSize(5000L). build(new CacheLoader<Integer, Integer>() { @Override public Integer load(Integer key) throws Exception { System.out.println("no cache"); return key * 5; } }); public static void main(String[] args) throws Exception { System.out.println(numCache.get(1)); Thread.sleep(1000); System.out.println(numCache.get(1)); Thread.sleep(1000); numCache.put(1, 6); System.out.println(numCache.get(1)); // console: 5 5 6 }
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
使用Spring Data Jpa的CriteriaQuery一個陷阱
使用Spring Data Jpa的CriteriaQuery進(jìn)行動態(tài)條件查詢時,可能會遇到一個陷阱,當(dāng)條件為空時,查詢不到任何結(jié)果,并不是期望的返回所有結(jié)果。這是為什么呢?2020-11-11這一次搞懂Spring自定義標(biāo)簽以及注解解析原理說明
這篇文章主要介紹了這一次搞懂Spring自定義標(biāo)簽以及注解解析原理說明,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-08-08Java實現(xiàn)中文算數(shù)驗證碼的實現(xiàn)示例(算數(shù)運算+-*/)
這篇文章主要介紹了Java實現(xiàn)中文算數(shù)驗證碼的實現(xiàn)示例(算數(shù)運算+-*/),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07springboot+gradle 構(gòu)建多模塊項目的步驟
這篇文章主要介紹了springboot+gradle 構(gòu)建多模塊項目的步驟,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-05-05java定時任務(wù)Timer和TimerTask使用詳解
這篇文章主要為大家詳細(xì)介紹了java定時任務(wù)Timer和TimerTask使用方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-02-02Hibernate的Annotation版Hello world實例
這篇文章主要介紹了Hibernate的Annotation版Hello world實現(xiàn)方法,詳細(xì)分析了Annotation的具體使用步驟與Hello world實現(xiàn)方法,需要的朋友可以參考下2016-03-03完美解決因數(shù)據(jù)庫一次查詢數(shù)據(jù)量過大導(dǎo)致的內(nèi)存溢出問題
今天小編就為大家分享一篇完美解決因數(shù)據(jù)庫一次查詢數(shù)據(jù)量過大導(dǎo)致的內(nèi)存溢出問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-06-06