本篇內(nèi)容主要講解“如何理解應(yīng)用級緩存”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“如何理解應(yīng)用級緩存”吧!
創(chuàng)新互聯(lián)長期為數(shù)千家客戶提供的網(wǎng)站建設(shè)服務(wù),團(tuán)隊(duì)從業(yè)經(jīng)驗(yàn)10年,關(guān)注不同地域、不同群體,并針對不同對象提供差異化的產(chǎn)品和服務(wù);打造開放共贏平臺,與合作伙伴共同營造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為疏附企業(yè)提供專業(yè)的做網(wǎng)站、成都網(wǎng)站建設(shè),疏附網(wǎng)站改版等技術(shù)服務(wù)。擁有十多年豐富建站經(jīng)驗(yàn)和眾多成功案例,為您定制開發(fā)。
緩存的命中率是指從緩存中獲取到數(shù)據(jù)的次數(shù)和總讀取次數(shù)的比率,命中率越高證明緩存的效果越好。這是一個很重要的指標(biāo),應(yīng)該通過監(jiān)控這個指標(biāo)來判斷我們的緩存是否設(shè)置的合理。
存活期:在設(shè)置緩存的同時設(shè)置該緩存可以存活多久,不論在存活期內(nèi)被訪問了多少次,時間到了都會過期
空閑期:是指緩存的數(shù)據(jù)多久沒有被訪問就過期
設(shè)置緩存的存儲空間,比如:設(shè)置緩存的空間是 1G,當(dāng)達(dá)到了1G之后就會按照一定的策略將部分?jǐn)?shù)據(jù)移除
設(shè)置緩存的最大條目數(shù),當(dāng)達(dá)到了設(shè)置的最大條目數(shù)之后按照一定的策略將舊的數(shù)據(jù)移除
弱引用:當(dāng)垃圾回收器開始回收內(nèi)存的時候,如果發(fā)現(xiàn)了弱引用,它將立即被回收。
軟引用:當(dāng)垃圾回收器發(fā)現(xiàn)內(nèi)存已不足的情況下會回收軟引用的對象,從而騰出一下空間,防止發(fā)生內(nèi)存溢出。軟引用適合用來做堆緩存
FIFO 先進(jìn)先出算法
LRU 最近最少使用算法
LFU 最不常用算法
堆緩存是指把數(shù)據(jù)緩存在JVM的堆內(nèi)存中,使用堆緩存的好處是沒有序列化和反序列化的操作,是最快的緩存。如果緩存的數(shù)據(jù)量很大,為了避免造成OOM通常情況下使用的時軟引用來存儲緩存對象;堆緩存的缺點(diǎn)是緩存的空間有限,并且垃圾回收器暫停的時間會變長。
Cache<string, string> cache = CacheBuilder.newBuilder() .build();
通過CacheBuilder
構(gòu)建緩存對象
Gauva Cache的主要配置和方法
put
: 向緩存中設(shè)置key-value
V get(K key, Callable<!--? extends V--> loader)
: 獲取一個緩存值,如果緩存中沒有,那么就調(diào)用loader獲取一個然后放入到緩存
expireAfterWrite
: 設(shè)置緩存的存活期,寫入數(shù)據(jù)后指定時間之后失效
expireAfterAccess
: 設(shè)置緩存的空閑期,在給定的時間內(nèi)沒有被訪問就會被回收
maximumSize
: 設(shè)置緩存的最大條目數(shù)
weakKeys/weakValues
: 設(shè)置弱引用緩存
softValues
: 設(shè)置軟引用緩存
invalidate/invalidateAll
: 主動失效指定key的緩存數(shù)據(jù)
recordStats
: 啟動記錄統(tǒng)計信息,可以查看到命中率
removalListener
: 當(dāng)緩存被刪除的時候會調(diào)用此監(jiān)聽器,可以用于查看為什么緩存會被刪除
Caffeine是使用Java8對Guava緩存的重寫版本,高性能Java本地緩存組件,也是Spring推薦的堆緩存的實(shí)現(xiàn),與spring的集成可以查看文檔https://docs.spring.io/spring-framework/docs/current/reference/html/integration.html#cache-store-configuration-caffeine。
由于是對Guava緩存的重寫版本,所以很多的配置參數(shù)都是和Guava緩存一致:
initialCapacity
: 初始的緩存空間大小
maximumSize
: 緩存的最大條數(shù)
maximumWeight
: 緩存的最大權(quán)重
expireAfterAccess
: 最后一次寫入或訪問后經(jīng)過固定時間過期
expireAfterWrite
: 最后一次寫入后經(jīng)過固定時間過期
expireAfter
: 自定義過期策略
refreshAfterWrite
: 創(chuàng)建緩存或者最近一次更新緩存后經(jīng)過固定的時間間隔,刷新緩存
weakKeys
: 打開key的弱引用
weakValues
:打開value的弱引用
softValues
:打開value的軟引用
recordStats
:開啟統(tǒng)計功能
Caffeine的官方文檔:https://github.com/ben-manes/caffeine/wiki
pom.xml中添加依賴
<dependency> <groupid>com.github.ben-manes.caffeine</groupid> <artifactid>caffeine</artifactid> <version>2.8.4</version> </dependency>
Caffeine Cache提供了三種緩存填充策略:手動、同步加載和異步加載。
手動加載:在每次get key的時候指定一個同步的函數(shù),如果key不存在就調(diào)用這個函數(shù)生成一個值
public Object manual(String key) { Cache<string, object> cache = Caffeine.newBuilder() .expireAfterAccess(1, TimeUnit.SECONDS) //設(shè)置空閑期時長 .maximumSize(10) .build(); return cache.get(key, t -> setValue(key).apply(key)); } public Function<string, object> setValue(String key){ return t -> "https://silently9527.cn"; }
同步加載:構(gòu)造Cache時候,build方法傳入一個CacheLoader實(shí)現(xiàn)類。實(shí)現(xiàn)load方法,通過key加載value。
public Object sync(String key){ LoadingCache<string, object> cache = Caffeine.newBuilder() .maximumSize(100) .expireAfterWrite(1, TimeUnit.MINUTES) //設(shè)置存活期時長 .build(k -> setValue(key).apply(key)); return cache.get(key); } public Function<string, object> setValue(String key){ return t -> "https://silently9527.cn"; }
異步加載:AsyncLoadingCache是繼承自LoadingCache類的,異步加載使用Executor去調(diào)用方法并返回一個CompletableFuture
public CompletableFuture async(String key) { AsyncLoadingCache<string, object> cache = Caffeine.newBuilder() .maximumSize(100) .expireAfterWrite(1, TimeUnit.MINUTES) .buildAsync(k -> setAsyncValue().get()); return cache.get(key); } public CompletableFuture<object> setAsyncValue() { return CompletableFuture.supplyAsync(() -> "公眾號:貝塔學(xué)JAVA"); }
監(jiān)聽緩存被清理的事件
public void removeListener() { Cache<string, object> cache = Caffeine.newBuilder() .removalListener((String key, Object value, RemovalCause cause) -> { System.out.println("remove lisitener"); System.out.println("remove Key:" + key); System.out.println("remove Value:" + value); }) .build(); cache.put("name", "silently9527"); cache.invalidate("name"); }
統(tǒng)計
public void recordStats() { Cache<string, object> cache = Caffeine.newBuilder() .maximumSize(10000) .recordStats() .build(); cache.put("公眾號", "貝塔學(xué)JAVA"); cache.get("公眾號", (t) -> ""); cache.get("name", (t) -> "silently9527"); CacheStats stats = cache.stats(); System.out.println(stats); }
通過 Cache.stats()
獲取到CacheStats
。CacheStats
提供以下統(tǒng)計方法:
hitRate()
: 返回緩存命中率
evictionCount()
: 緩存回收數(shù)量
averageLoadPenalty()
: 加載新值的平均時間
EhCache 是老牌Java開源緩存框架,早在2003年就已經(jīng)出現(xiàn)了,發(fā)展到現(xiàn)在已經(jīng)非常成熟穩(wěn)定,在Java應(yīng)用領(lǐng)域應(yīng)用也非常廣泛,而且和主流的Java框架比如Srping可以很好集成。相比于 Guava Cache,EnCache 支持的功能更豐富,包括堆外緩存、磁盤緩存,當(dāng)然使用起來要更重一些。使用 Ehcache 的Maven 依賴如下:
<dependency> <groupid>org.ehcache</groupid> <artifactid>ehcache</artifactid> <version>3.6.3</version> </dependency>
CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder().build(true); ResourcePoolsBuilder resource = ResourcePoolsBuilder.heap(10); //設(shè)置最大緩存條目數(shù) CacheConfiguration<string, string> cacheConfig = CacheConfigurationBuilder .newCacheConfigurationBuilder(String.class, String.class, resource) .withExpiry(ExpiryPolicyBuilder.timeToIdleExpiration(Duration.ofMinutes(10))) .build(); Cache<string, string> cache = cacheManager.createCache("userInfo", cacheConfig);
ResourcePoolsBuilder.heap(10)
設(shè)置緩存的最大條目數(shù),這是簡寫方式,等價于ResourcePoolsBuilder.newResourcePoolsBuilder().heap(10, EntryUnit.ENTRIES);
ResourcePoolsBuilder.newResourcePoolsBuilder().heap(10, MemoryUnit.MB)
設(shè)置緩存最大的空間10MB
withExpiry(ExpiryPolicyBuilder.timeToIdleExpiration(Duration.ofMinutes(10)))
設(shè)置緩存空閑時間
withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofMinutes(10)))
設(shè)置緩存存活時間
remove/removeAll
主動失效緩存,與Guava Cache類似,調(diào)用方法后不會立即去清除回收,只有在get或者put的時候判斷緩存是否過期
withSizeOfMaxObjectSize(10,MemoryUnit.KB)
限制單個緩存對象的大小,超過這兩個限制的對象則不被緩存
堆外緩存即緩存數(shù)據(jù)在堆外內(nèi)存中,空間大小只受本機(jī)內(nèi)存大小限制,不受GC管理,使用堆外緩存可以減少GC暫停時間,但是堆外內(nèi)存中的對象都需要序列化和反序列化,KEY和VALUE必須實(shí)現(xiàn)Serializable接口,因此速度會比堆內(nèi)緩存慢。在Java中可以通過 -XX:MaxDirectMemorySize
參數(shù)設(shè)置堆外內(nèi)存的上限
CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder().build(true); // 堆外內(nèi)存不能按照存儲條目限制,只能按照內(nèi)存大小進(jìn)行限制,超過限制則回收緩存 ResourcePoolsBuilder resource = ResourcePoolsBuilder.newResourcePoolsBuilder().offheap(10, MemoryUnit.MB); CacheConfiguration<string, string> cacheConfig = CacheConfigurationBuilder .newCacheConfigurationBuilder(String.class, String.class, resource) .withDispatcherConcurrency(4) .withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofMinutes(10))) .withSizeOfMaxObjectSize(10, MemoryUnit.KB) .build(); Cache<string, string> cache = cacheManager.createCache("userInfo2", cacheConfig); cache.put("website", "https://silently9527.cn"); System.out.println(cache.get("website"));
把緩存數(shù)據(jù)存放到磁盤上,在JVM重啟時緩存的數(shù)據(jù)不會受到影響,而堆緩存和堆外緩存都會丟失;并且磁盤緩存有更大的存儲空間;但是緩存在磁盤上的數(shù)據(jù)也需要支持序列化,速度會被比內(nèi)存更慢,在使用時推薦使用更快的磁盤帶來更大的吞吐率,比如使用閃存代替機(jī)械磁盤。
CacheManagerConfiguration<persistentcachemanager> persistentManagerConfig = CacheManagerBuilder .persistence(new File("/Users/huaan9527/Desktop", "ehcache-cache")); PersistentCacheManager persistentCacheManager = CacheManagerBuilder.newCacheManagerBuilder() .with(persistentManagerConfig).build(true); //disk 第三個參數(shù)設(shè)置為 true 表示將數(shù)據(jù)持久化到磁盤上 ResourcePoolsBuilder resource = ResourcePoolsBuilder.newResourcePoolsBuilder().disk(100, MemoryUnit.MB, true); CacheConfiguration<string, string> config = CacheConfigurationBuilder .newCacheConfigurationBuilder(String.class, String.class, resource).build(); Cache<string, string> cache = persistentCacheManager.createCache("userInfo", CacheConfigurationBuilder.newCacheConfigurationBuilder(config)); cache.put("公眾號", "貝塔學(xué)JAVA"); System.out.println(cache.get("公眾號")); persistentCacheManager.close();
在JVM停止時,一定要記得調(diào)用persistentCacheManager.close()
,保證內(nèi)存中的數(shù)據(jù)能夠dump到磁盤上。
這是典型 heap + offheap + disk 組合的結(jié)構(gòu)圖,上層比下層速度快,下層比上層存儲空間大,在ehcache中,空間大小設(shè)置 heap > offheap > disk
,否則會報錯; ehcache 會將最熱的數(shù)據(jù)保存在高一級的緩存。這種結(jié)構(gòu)的代碼如下:
CacheManagerConfiguration<persistentcachemanager> persistentManagerConfig = CacheManagerBuilder .persistence(new File("/Users/huaan9527/Desktop", "ehcache-cache")); PersistentCacheManager persistentCacheManager = CacheManagerBuilder.newCacheManagerBuilder() .with(persistentManagerConfig).build(true); ResourcePoolsBuilder resource = ResourcePoolsBuilder.newResourcePoolsBuilder() .heap(10, MemoryUnit.MB) .offheap(100, MemoryUnit.MB) //第三個參數(shù)設(shè)置為true,支持持久化 .disk(500, MemoryUnit.MB, true); CacheConfiguration<string, string> config = CacheConfigurationBuilder .newCacheConfigurationBuilder(String.class, String.class, resource).build(); Cache<string, string> cache = persistentCacheManager.createCache("userInfo", CacheConfigurationBuilder.newCacheConfigurationBuilder(config)); //寫入緩存 cache.put("name", "silently9527"); // 讀取緩存 System.out.println(cache.get("name")); // 再程序關(guān)閉前,需要手動釋放資源 persistentCacheManager.close();
前面提到的堆內(nèi)緩存和堆外緩存如果在多個JVM實(shí)例的情況下會有兩個問題:1.單機(jī)容量畢竟有限;2.多臺JVM實(shí)例緩存的數(shù)據(jù)可能不一致;3.如果緩存數(shù)據(jù)同一時間都失效了,那么請求都會打到數(shù)據(jù)庫上,數(shù)據(jù)庫壓力增大。這時候我們就需要引入分布式緩存來解決,現(xiàn)在使用最多的分布式緩存是redis
當(dāng)引入分布式緩存之后就可以把應(yīng)用緩存的架構(gòu)調(diào)整成上面的結(jié)構(gòu)。
緩存使用的模式大概分為兩類:Cache-Aside、Cache-As-SoR(SoR表示實(shí)際存儲數(shù)據(jù)的系統(tǒng),也就是數(shù)據(jù)源)
業(yè)務(wù)代碼圍繞著緩存來寫,通常都是從緩存中來獲取數(shù)據(jù),如果緩存沒有命中,則從數(shù)據(jù)庫中查找,查詢到之后就把數(shù)據(jù)放入到緩存;當(dāng)數(shù)據(jù)被更新之后,也需要對應(yīng)的去更新緩存中的數(shù)據(jù)。這種模式也是我們通常使用最多的。
讀場景
value = cache.get(key); //從緩存中讀取數(shù)據(jù) if(value == null) { value = loadFromDatabase(key); //從數(shù)據(jù)庫中查詢 cache.put(key, value); //放入到緩存中 }
寫場景
wirteToDatabase(key, value); //寫入到數(shù)據(jù)庫 cache.put(key, value); //放入到緩存中 或者 可以刪除掉緩存 cache.remove(key) ,再讀取的時候再查一次
Spring的Cache擴(kuò)展就是使用的Cache-Aside模式,Spring為了把業(yè)務(wù)代碼和緩存的讀取更新分離,對Cache-Aside模式使用AOP進(jìn)行了封裝,提供了多個注解來實(shí)現(xiàn)讀寫場景。官方參考文檔:
@Cacheable
: 通常是放在查詢方法上,實(shí)現(xiàn)的就是Cache-Aside
讀的場景,先查緩存,如果不存在在查詢數(shù)據(jù)庫,最后把查詢出來的結(jié)果放入到緩存。
@CachePut
: 通常用在保存更新方法上面,實(shí)現(xiàn)的就是Cache-Aside
寫的場景,更新完成數(shù)據(jù)庫后把數(shù)據(jù)放入到緩存中。
@CacheEvict
: 從緩存中刪除指定key的緩存
> 對于一些允許有一點(diǎn)點(diǎn)更新延遲基礎(chǔ)數(shù)據(jù)可以考慮使用canal訂閱binlog日志來完成緩存的增量更新。 > > Cache-Aside還有個問題,如果某個時刻熱點(diǎn)數(shù)據(jù)緩存失效,那么會有很多請求同時打到后端數(shù)據(jù)庫上,數(shù)據(jù)庫的壓力會瞬間增大
Cache-As-SoR模式也就會把Cache看做是數(shù)據(jù)源,所有的操作都是針對緩存,Cache在委托給真正的SoR去實(shí)現(xiàn)讀或者寫。業(yè)務(wù)代碼中只會看到Cache的操作,這種模式又分為了三種
應(yīng)用程序始終從緩存中請求數(shù)據(jù),如果緩存中沒有數(shù)據(jù),則它負(fù)責(zé)使用提供的數(shù)據(jù)加載程序從數(shù)據(jù)庫中檢索數(shù)據(jù),檢索數(shù)據(jù)后,緩存會自行更新并將數(shù)據(jù)返回給調(diào)用的應(yīng)用程序。Gauva Cache、Caffeine、EhCache都支持這種模式;
Caffeine實(shí)現(xiàn)Read Through 由于Gauva Cache和Caffeine實(shí)現(xiàn)類似,所以這里只展示Caffeine的實(shí)現(xiàn),以下代碼來自Caffeine官方文檔
LoadingCache<key, graph> cache = Caffeine.newBuilder() .maximumSize(10_000) .expireAfterWrite(10, TimeUnit.MINUTES) .build(key -> createExpensiveGraph(key)); // Lookup and compute an entry if absent, or null if not computable Graph graph = cache.get(key); // Lookup and compute entries that are absent Map<key, graph> graphs = cache.getAll(keys);
在build Cache的時候指定一個CacheLoader
[1] 在應(yīng)用程序中直接調(diào)用cache.get(key)
[2] 首先查詢緩存,如果緩存存在就直接返回數(shù)據(jù)
[3] 如果不存在,就會委托給CacheLoader
去數(shù)據(jù)源中查詢數(shù)據(jù),之后在放入到緩存,返回給應(yīng)用程序
> CacheLoader
不要直接返回null,建議封裝成自己定義的Null對像,在放入到緩存中,可以防止緩存擊穿
為了防止因?yàn)槟硞€熱點(diǎn)數(shù)據(jù)失效導(dǎo)致后端數(shù)據(jù)庫壓力增大的情況,我可以在CacheLoader
中使用鎖限制只允許一個請求去查詢數(shù)據(jù)庫,其他的請求都等待第一個請求查詢完成后從緩存中獲取,在上一篇 《萬字長文聊緩存(上)》中我們聊到了Nginx也有類似的配置參數(shù)
value = loadFromCache(key); if(value != null) { return value; } synchronized (lock) { value = loadFromCache(key); if(value != null) { return value; } return loadFromDatabase(key); }
EhCache實(shí)現(xiàn)Read Through
CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder().build(true); ResourcePoolsBuilder resource = ResourcePoolsBuilder.newResourcePoolsBuilder().heap(10, MemoryUnit.MB); //設(shè)置最大緩存條目數(shù) CacheConfiguration<string, string> cacheConfig = CacheConfigurationBuilder .newCacheConfigurationBuilder(String.class, String.class, resource) .withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofMinutes(10))) .withLoaderWriter(new CacheLoaderWriter<string, string>(){ @Override public String load(String key) throws Exception { //load from database return "silently9527"; } @Override public void write(String key, String value) throws Exception { } @Override public void delete(String key) throws Exception { } }) .build(); Cache<string, string> cache = cacheManager.createCache("userInfo", cacheConfig); System.out.println(cache.get("name"));
在EhCache中使用的是CacheLoaderWriter
來從數(shù)據(jù)庫中加載數(shù)據(jù);解決因?yàn)槟硞€熱點(diǎn)數(shù)據(jù)失效導(dǎo)致后端數(shù)據(jù)庫壓力增大的問題和上面的方式一樣,也可以在load
中實(shí)現(xiàn)。
和Read Through模式類似,當(dāng)數(shù)據(jù)進(jìn)行更新時,先去更新SoR,成功之后在更新緩存。
Caffeine實(shí)現(xiàn)Write Through
Cache<string, string> cache = Caffeine.newBuilder() .maximumSize(100) .writer(new CacheWriter<string, string>() { @Override public void write(@NonNull String key, @NonNull String value) { //write data to database System.out.println(key); System.out.println(value); } @Override public void delete(@NonNull String key, @Nullable String value, @NonNull RemovalCause removalCause) { //delete from database } }) .build(); cache.put("name", "silently9527");
Caffeine通過使用CacheWriter
來實(shí)現(xiàn)Write Through,CacheWriter
可以同步的監(jiān)聽到緩存的創(chuàng)建、變更和刪除操作,只有寫成功了才會去更新緩存
EhCache實(shí)現(xiàn)Write Through
CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder().build(true); ResourcePoolsBuilder resource = ResourcePoolsBuilder.newResourcePoolsBuilder().heap(10, MemoryUnit.MB); //設(shè)置最大緩存條目數(shù) CacheConfiguration<string, string> cacheConfig = CacheConfigurationBuilder .newCacheConfigurationBuilder(String.class, String.class, resource) .withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofMinutes(10))) .withLoaderWriter(new CacheLoaderWriter<string, string>(){ @Override public String load(String key) throws Exception { return "silently9527"; } @Override public void write(String key, String value) throws Exception { //write data to database System.out.println(key); System.out.println(value); } @Override public void delete(String key) throws Exception { //delete from database } }) .build(); Cache<string, string> cache = cacheManager.createCache("userInfo", cacheConfig); System.out.println(cache.get("name")); cache.put("website","https://silently9527.cn");
EhCache還是通過CacheLoaderWriter
來實(shí)現(xiàn)的,當(dāng)我們調(diào)用cache.put("xxx","xxx")
進(jìn)行寫緩存的時候,EhCache首先會將寫的操作委托給CacheLoaderWriter
,有CacheLoaderWriter.write
去負(fù)責(zé)寫數(shù)據(jù)源
這種模式通常先將數(shù)據(jù)寫入緩存,再異步地寫入數(shù)據(jù)庫進(jìn)行數(shù)據(jù)同步。這樣的設(shè)計既可以減少對數(shù)據(jù)庫的直接訪問,降低壓力,同時對數(shù)據(jù)庫的多次修改可以合并操作,極大地提升了系統(tǒng)的承載能力。但是這種模式也存在風(fēng)險,如當(dāng)緩存機(jī)器出現(xiàn)宕機(jī)時,數(shù)據(jù)有丟失的可能。
Caffeine要想實(shí)現(xiàn)Write Behind可以在CacheLoaderWriter.write
方法中把數(shù)據(jù)發(fā)送到MQ中,實(shí)現(xiàn)異步的消費(fèi),這樣可以保證數(shù)據(jù)的安全,但是要想實(shí)現(xiàn)合并操作就需要擴(kuò)展功能更強(qiáng)大的CacheLoaderWriter
。
EhCache實(shí)現(xiàn)Write Behind
//1 定義線程池 PooledExecutionServiceConfiguration testWriteBehind = PooledExecutionServiceConfigurationBuilder .newPooledExecutionServiceConfigurationBuilder() .pool("testWriteBehind", 5, 10) .build(); CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder() .using(testWriteBehind) .build(true); ResourcePoolsBuilder resource = ResourcePoolsBuilder.newResourcePoolsBuilder().heap(10, MemoryUnit.MB); //設(shè)置最大緩存條目數(shù) //2 設(shè)置回寫模式配置 WriteBehindConfiguration testWriteBehindConfig = WriteBehindConfigurationBuilder .newUnBatchedWriteBehindConfiguration() .queueSize(10) .concurrencyLevel(2) .useThreadPool("testWriteBehind") .build(); CacheConfiguration<string, string> cacheConfig = CacheConfigurationBuilder .newCacheConfigurationBuilder(String.class, String.class, resource) .withLoaderWriter(new CacheLoaderWriter<string, string>() { @Override public String load(String key) throws Exception { return "silently9527"; } @Override public void write(String key, String value) throws Exception { //write data to database } @Override public void delete(String key) throws Exception { } }) .add(testWriteBehindConfig) .build(); Cache<string, string> cache = cacheManager.createCache("userInfo", cacheConfig);
首先使用PooledExecutionServiceConfigurationBuilder
定義了線程池配置;然后使用WriteBehindConfigurationBuilder
設(shè)置會寫模式配置,其中newUnBatchedWriteBehindConfiguration
表示不進(jìn)行批量寫操作,因?yàn)槭钱惒綄懀孕枰褜懖僮飨确湃氲疥?duì)列中,通過queueSize
設(shè)置隊(duì)列大小,useThreadPool
指定使用哪個線程池; concurrencyLevel
設(shè)置使用多少個并發(fā)線程和隊(duì)列進(jìn)行Write Behind
EhCache實(shí)現(xiàn)批量寫的操作也很容易
首先把newUnBatchedWriteBehindConfiguration()
替換成newBatchedWriteBehindConfiguration(10, TimeUnit.SECONDS, 20)
,這里設(shè)置的是數(shù)量達(dá)到20就進(jìn)行批處理,如果10秒內(nèi)沒有達(dá)到20個也會進(jìn)行處理
其次在CacheLoaderWriter
中實(shí)現(xiàn)wirteAll 和 deleteAll進(jìn)行批處理
> 如果需要把對相同的key的操作合并起來只記錄最后一次數(shù)據(jù),可以通過enableCoalescing()
來啟用合并
到此,相信大家對“如何理解應(yīng)用級緩存”有了更深的了解,不妨來實(shí)際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!
分享文章:如何理解應(yīng)用級緩存
文章起源:http://m.rwnh.cn/article48/jipshp.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供用戶體驗(yàn)、網(wǎng)頁設(shè)計公司、商城網(wǎng)站、品牌網(wǎng)站建設(shè)、移動網(wǎng)站建設(shè)、標(biāo)簽優(yōu)化
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)