Java本地缓存技术选型(Guava Cache、Caffeine、Encache)
前言
對(duì)一個(gè)java后臺(tái)開(kāi)發(fā)者而言,提到緩存,第一反應(yīng)就是redis和memcache。利用這類緩存足以解決大多數(shù)的性能問(wèn)題了,并且java針對(duì)這兩者也都有非常成熟的api可供使用。但是我們也要知道,這兩種都屬于remote cache(分布式緩存),應(yīng)用的進(jìn)程和緩存的進(jìn)程通常分布在不同的服務(wù)器上,不同進(jìn)程之間通過(guò)RPC或HTTP的方式通信。這種緩存的優(yōu)點(diǎn)是緩存和應(yīng)用服務(wù)解耦,支持大數(shù)據(jù)量的存儲(chǔ),缺點(diǎn)是數(shù)據(jù)要經(jīng)過(guò)網(wǎng)絡(luò)傳輸,性能上會(huì)有一定損耗。
與分布式緩存對(duì)應(yīng)的是本地緩存,緩存的進(jìn)程和應(yīng)用進(jìn)程是同一個(gè),數(shù)據(jù)的讀寫(xiě)都在一個(gè)進(jìn)程內(nèi)完成,這種方式的優(yōu)點(diǎn)是沒(méi)有網(wǎng)絡(luò)開(kāi)銷,訪問(wèn)速度很快。缺點(diǎn)是受JVM內(nèi)存的限制,不適合存放大數(shù)據(jù)。
本篇文章我們主要主要討論Java本地緩存的的一些常用方案。
本地緩存常用技術(shù)
本地緩存和應(yīng)用同屬于一個(gè)進(jìn)程,使用不當(dāng)會(huì)影響服務(wù)穩(wěn)定性,所以通常需要考慮更多的因素,例如容量限制、過(guò)期策略、淘汰策略、自動(dòng)刷新等。常用的本地緩存方案有:
- 根據(jù)HashMap自實(shí)現(xiàn)本地緩存
- Guava Cache
- Caffeine
- Encache
下面分別進(jìn)行介紹:
1. 根據(jù)HashMap自定義實(shí)現(xiàn)本地緩存
緩存的本質(zhì)就是存儲(chǔ)在內(nèi)存中的KV數(shù)據(jù)結(jié)構(gòu),對(duì)應(yīng)的就是jdk中的HashMap,但是要實(shí)現(xiàn)緩存,還需要考慮并發(fā)安全性、容量限制等策略,下面簡(jiǎn)單介紹一種利用LinkedHashMap實(shí)現(xiàn)緩存的方式:
public class LRUCache extends LinkedHashMap {/*** 可重入讀寫(xiě)鎖,保證并發(fā)讀寫(xiě)安全性*/private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();private Lock readLock = readWriteLock.readLock();private Lock writeLock = readWriteLock.writeLock();/*** 緩存大小限制*/private int maxSize;public LRUCache(int maxSize) {super(maxSize + 1, 1.0f, true);this.maxSize = maxSize;}@Overridepublic Object get(Object key) {readLock.lock();try {return super.get(key);} finally {readLock.unlock();}}@Overridepublic Object put(Object key, Object value) {writeLock.lock();try {return super.put(key, value);} finally {writeLock.unlock();}}@Overrideprotected boolean removeEldestEntry(Map.Entry eldest) {return this.size() > maxSize;} }復(fù)制代碼LinkedHashMap維持了一個(gè)鏈表結(jié)構(gòu),用來(lái)存儲(chǔ)節(jié)點(diǎn)的插入順序或者訪問(wèn)順序(二選一),并且內(nèi)部封裝了一些業(yè)務(wù)邏輯,只需要覆蓋removeEldestEntry方法,便可以實(shí)現(xiàn)緩存的LRU淘汰策略。此外我們利用讀寫(xiě)鎖,保障緩存的并發(fā)安全性。需要注意的是,這個(gè)示例并不支持過(guò)期時(shí)間淘汰的策略。
自實(shí)現(xiàn)緩存的方式,優(yōu)點(diǎn)是實(shí)現(xiàn)簡(jiǎn)單,不需要引入第三方包,比較適合一些簡(jiǎn)單的業(yè)務(wù)場(chǎng)景。缺點(diǎn)是如果需要更多的特性,需要定制化開(kāi)發(fā),成本會(huì)比較高,并且穩(wěn)定性和可靠性也難以保障。對(duì)于比較復(fù)雜的場(chǎng)景,建議使用比較穩(wěn)定的開(kāi)源工具。
2. 基于Guava Cache實(shí)現(xiàn)本地緩存
Guava是Google團(tuán)隊(duì)開(kāi)源的一款 Java 核心增強(qiáng)庫(kù),包含集合、并發(fā)原語(yǔ)、緩存、IO、反射等工具箱,性能和穩(wěn)定性上都有保障,應(yīng)用十分廣泛。Guava Cache支持很多特性:
- 支持最大容量限制
- 支持兩種過(guò)期刪除策略(插入時(shí)間和訪問(wèn)時(shí)間)
- 支持簡(jiǎn)單的統(tǒng)計(jì)功能
- 基于LRU算法實(shí)現(xiàn)
Guava Cache的使用非常簡(jiǎn)單,首先需要引入maven包:
<dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>18.0</version> </dependency> 復(fù)制代碼一個(gè)簡(jiǎn)單的示例代碼如下:
public class GuavaCacheTest {public static void main(String[] args) throws Exception {//創(chuàng)建guava cacheCache<String, String> loadingCache = CacheBuilder.newBuilder()//cache的初始容量.initialCapacity(5)//cache最大緩存數(shù).maximumSize(10)//設(shè)置寫(xiě)緩存后n秒鐘過(guò)期.expireAfterWrite(17, TimeUnit.SECONDS)//設(shè)置讀寫(xiě)緩存后n秒鐘過(guò)期,實(shí)際很少用到,類似于expireAfterWrite//.expireAfterAccess(17, TimeUnit.SECONDS).build();String key = "key";// 往緩存寫(xiě)數(shù)據(jù)loadingCache.put(key, "v");// 獲取value的值,如果key不存在,調(diào)用collable方法獲取value值加載到key中再返回String value = loadingCache.get(key, new Callable<String>() {@Overridepublic String call() throws Exception {return getValueFromDB(key);}});// 刪除keyloadingCache.invalidate(key);}private static String getValueFromDB(String key) {return "v";} } 復(fù)制代碼總體來(lái)說(shuō),Guava Cache是一款十分優(yōu)異的緩存工具,功能豐富,線程安全,足以滿足工程化使用,以上代碼只介紹了一般的用法,實(shí)際上springboot對(duì)guava也有支持,利用配置文件或者注解可以輕松集成到代碼中。
3. Caffeine
Caffeine是基于java8實(shí)現(xiàn)的新一代緩存工具,緩存性能接近理論最優(yōu)。可以看作是Guava Cache的增強(qiáng)版,功能上兩者類似,不同的是Caffeine采用了一種結(jié)合LRU、LFU優(yōu)點(diǎn)的算法:W-TinyLFU,在性能上有明顯的優(yōu)越性。Caffeine的使用,首先需要引入maven包:
<dependency><groupId>com.github.ben-manes.caffeine</groupId><artifactId>caffeine</artifactId><version>2.5.5</version> </dependency> 復(fù)制代碼使用上和Guava Cache基本類似:
public class CaffeineCacheTest {public static void main(String[] args) throws Exception {//創(chuàng)建guava cacheCache<String, String> loadingCache = Caffeine.newBuilder()//cache的初始容量.initialCapacity(5)//cache最大緩存數(shù).maximumSize(10)//設(shè)置寫(xiě)緩存后n秒鐘過(guò)期.expireAfterWrite(17, TimeUnit.SECONDS)//設(shè)置讀寫(xiě)緩存后n秒鐘過(guò)期,實(shí)際很少用到,類似于expireAfterWrite//.expireAfterAccess(17, TimeUnit.SECONDS).build();String key = "key";// 往緩存寫(xiě)數(shù)據(jù)loadingCache.put(key, "v");// 獲取value的值,如果key不存在,獲取value后再返回String value = loadingCache.get(key, CaffeineCacheTest::getValueFromDB);// 刪除keyloadingCache.invalidate(key);}private static String getValueFromDB(String key) {return "v";} } 復(fù)制代碼相比Guava Cache來(lái)說(shuō),Caffeine無(wú)論從功能上和性能上都有明顯優(yōu)勢(shì)。同時(shí)兩者的API類似,使用Guava Cache的代碼很容易可以切換到Caffeine,節(jié)省遷移成本。需要注意的是,SpringFramework5.0(SpringBoot2.0)同樣放棄了Guava Cache的本地緩存方案,轉(zhuǎn)而使用Caffeine。
4. Encache
Encache是一個(gè)純Java的進(jìn)程內(nèi)緩存框架,具有快速、精干等特點(diǎn),是Hibernate中默認(rèn)的CacheProvider。同Caffeine和Guava Cache相比,Encache的功能更加豐富,擴(kuò)展性更強(qiáng):
- 支持多種緩存淘汰算法,包括LRU、LFU和FIFO
- 緩存支持堆內(nèi)存儲(chǔ)、堆外存儲(chǔ)、磁盤(pán)存儲(chǔ)(支持持久化)三種
- 支持多種集群方案,解決數(shù)據(jù)共享問(wèn)題
Encache的使用,首先需要導(dǎo)入maven包:
<dependency><groupId>org.ehcache</groupId><artifactId>ehcache</artifactId><version>3.8.0</version> </dependency> 復(fù)制代碼以下是一個(gè)簡(jiǎn)單的使用案例:
public class EncacheTest {public static void main(String[] args) throws Exception {// 聲明一個(gè)cacheBuilderCacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder().withCache("encacheInstance", CacheConfigurationBuilder//聲明一個(gè)容量為20的堆內(nèi)緩存.newCacheConfigurationBuilder(String.class,String.class, ResourcePoolsBuilder.heap(20))).build(true);// 獲取Cache實(shí)例Cache<String,String> myCache = cacheManager.getCache("encacheInstance", String.class, String.class);// 寫(xiě)緩存myCache.put("key","v");// 讀緩存String value = myCache.get("key");// 移除換粗cacheManager.removeCache("myCache");cacheManager.close();} }復(fù)制代碼總結(jié)
- 從易用性角度,Guava Cache、Caffeine和Encache都有十分成熟的接入方案,使用簡(jiǎn)單。
- 從功能性角度,Guava Cache和Caffeine功能類似,都是只支持堆內(nèi)緩存,Encache相比功能更為豐富
- 從性能上進(jìn)行比較,Caffeine最優(yōu)、GuavaCache次之,Encache最差(下圖是三者的性能對(duì)比結(jié)果)
?
?
總體來(lái)說(shuō),對(duì)于本地緩存的方案中,筆者比較推薦Caffeine,性能上遙遙領(lǐng)先。雖然Encache功能更為豐富,甚至提供了持久化和集群的功能,但是這些功能完全可以依靠其他方式實(shí)現(xiàn)。真實(shí)的業(yè)務(wù)工程中,建議使用Caffeine作為本地緩存,另外使用redis或者memcache作為分布式緩存,構(gòu)造多級(jí)緩存體系,保證性能和可靠性。
作者:捉蟲(chóng)師
鏈接:https://juejin.cn/post/6844904199453409294
來(lái)源:掘金
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處。
總結(jié)
以上是生活随笔為你收集整理的Java本地缓存技术选型(Guava Cache、Caffeine、Encache)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 游戏修改器(一)瞬移
- 下一篇: 理解js中的原型链,prototype与